Як зробити запит 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'%';

Arel обертає рядок запиту 'Smith' правильно, але оскільки це твердження LIKE, воно не працює.

Як можна зробити запит LIKE в Арелі?

PS Bonus - я насправді намагаюся сканувати два поля на столі, ім’я та опис, щоб побачити, чи є відповідність запиту. Як би це працювало?


1
Я оновив відповідь arel для бонусу.
Педро Роло

Відповіді:


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використання в запиті до db Postgres.
dkobozev

20
чи захищено це від ін'єкцій SQL?
sren

7
Це НЕ захищає повністю від ін'єкції SQL. Спробуйте встановити ім'я користувача "%". Запит поверне збіги
travis-146

5
Я намагався безпосередньо вводити sql, використовуючи параметри User.where(users[:name].matches("%#{params[:user_name]}%")), я намагався TRUNCATE users;й інші подібні запити, і нічого не сталося на стороні sql. Мені виглядає безпечно.
вушні перила

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 № 8) User.where ("ім'я, як% # {params [: запит]}% або опис, як% # {params [: запит]}%"). To_sql буде вразливим, але у форматі, який я показую , Rails уникає парами [: запит]
Reuben Mallaby

Вибачте за офтопік. У мене є 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.