Как сделать LIKE-запрос в Arel и Rails?

115

Я хочу сделать что-то вроде:

SELECT * FROM USER WHERE NAME LIKE '%Smith%';

Моя попытка в Ареле:

# params[:query] = 'Smith'
User.where("name like '%?%'", params[:query]).to_sql

Однако это становится:

SELECT * FROM USER WHERE NAME LIKE '%'Smith'%';

Арел правильно оборачивает строку запроса «Смит», но поскольку это оператор LIKE, он не работает.

Как выполнить запрос LIKE в Arel?

PS Бонус - я на самом деле пытаюсь просканировать два поля в таблице, имя и описание, чтобы увидеть, есть ли какие-либо совпадения с запросом. Как это будет работать?

Filsa
источник
1
Я обновил арел ответ на бонус.
Педро Роло

Ответы:

275

Вот как вы выполняете подобный запрос в arel:

users = User.arel_table
User.where(users[:name].matches("%#{user_name}%"))

PS:

users = User.arel_table
query_string = "%#{params[query]}%"
param_matches_string =  ->(param){ 
  users[param].matches(query_string) 
} 
User.where(param_matches_string.(:name)\
                       .or(param_matches_string.(:description)))
Педро Роло
источник
10
В отличие от использования where("name like ?", ...), этот подход более переносим для разных баз данных. Например, это может привести ILIKEк использованию в запросе к базе данных Postgres.
dkobozev 02
20
это защищено от SQL-инъекций?
sren
7
Это НЕ полностью защищает от SQL-инъекции. Попробуйте установить для user_name значение «%». Запрос вернет совпадения
travis-146
5
Я попытался ввести sql напрямую, используя параметры User.where(users[:name].matches("%#{params[:user_name]}%")), я пробовал TRUNCATE users;и другие подобные запросы, и на стороне sql ничего не произошло. Мне кажется, это безопасно.
Earlonrails
5
Используйте .gsub(/[%_]/, '\\\\\0')для экранирования подстановочных знаков MySql.
aercolino
116

Пытаться

User.where("name like ?", "%#{params[:query]}%").to_sql

PS.

q = "%#{params[:query]}%"
User.where("name like ? or description like ?", q, q).to_sql

Да, прошло много времени, но @ cgg5207 добавил модификацию (в основном полезно, если вы собираетесь искать параметры с длинными именами или несколько параметров с длинными именами или вам лень вводить текст)

q = "%#{params[:query]}%"
User.where("name like :q or description like :q", :q => q).to_sql

или

User.where("name like :q or description like :q", :q => "%#{params[:query]}%").to_sql
Рубен Маллаби
источник
9
Как Rails знает, что %в подставленной строке не нужно экранировать ? Похоже, что если вам нужен только односторонний подстановочный знак, ничто не %мешает пользователю отправить значение запроса, которое включает в себя с обоих концов (я знаю, что на практике Rails предотвращает %отображение в строке запроса, но похоже, что там должна быть защита от этого на уровне ActiveRecord).
Стивен
8
Разве это не уязвимо для атак с использованием SQL-инъекций?
Behrang Saeedzadeh
7
@Behrang no 8) User.where ("имя как% # {params [: query]}% или описание как% # {params [: query]}%"). To_sql будет уязвимым, но в формате, который я показываю , Rails экранирует params [: query]
Рувим Маллаби
Сорри за оффтоп. У меня есть метод sql git to_sqlили менеджер arel, как выполнить sql на db?
Малъ Скрылевъ
Model.where (to_sql_result)
Педро Роло
4

Ответ Рубена Маллаби можно сократить, чтобы использовать привязки параметров:

User.where("name like :kw or description like :kw", :kw=>"%#{params[:query]}%").to_sql
cgg5207
источник