Jak wykonać zapytanie LIKE w Arel and Rails?

115

Chcę zrobić coś takiego:

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

Moja próba w Arel:

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

Jednak staje się to:

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

Arel poprawnie opakowuje ciąg zapytania „Smith”, ale ponieważ jest to instrukcja LIKE, nie działa.

Jak zrobić zapytanie LIKE w Arel?

Bonus PS - tak naprawdę próbuję przeskanować dwa pola w tabeli, zarówno nazwę, jak i opis, aby sprawdzić, czy są jakieś dopasowania do zapytania. Jak by to działało?

filsa
źródło
1
Zaktualizowałem odpowiedź arel dotyczącą bonusu.
Pedro Rolo

Odpowiedzi:

275

Oto jak wykonujesz podobne zapytanie w 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)))
Pedro Rolo
źródło
10
W przeciwieństwie do używania where("name like ?", ...), to podejście jest bardziej przenośne w różnych bazach danych. Na przykład spowodowałoby ILIKEto użycie w zapytaniu do bazy danych Postgres.
dkobozev
20
czy to jest chronione przed wstrzyknięciami SQL?
sren
7
NIE chroni to w pełni przed wstrzyknięciem SQL. Spróbuj ustawić user_name na „%”. Zapytanie zwróci dopasowania
travis-146
5
Próbowałem wstrzyknąć sql bezpośrednio przy użyciu parametrów User.where(users[:name].matches("%#{params[:user_name]}%")), próbowałem TRUNCATE users;i innych podobnych zapytań i nic się nie stało po stronie sql. Wydaje mi się bezpieczne.
Earlonrails
5
Służy .gsub(/[%_]/, '\\\\\0')do zmiany znaczenia znaków wieloznacznych MySql.
aercolino
116

Próbować

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

PS.

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

Aa i minęło dużo czasu, ale @ cgg5207 dodał modyfikację (głównie przydatne, jeśli zamierzasz wyszukiwać parametry o długich lub wielu nazwach lub jesteś zbyt leniwy, aby pisać)

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

lub

User.where("name like :q or description like :q", :q => "%#{params[:query]}%").to_sql
Reuben Mallaby
źródło
9
Skąd Railsy wiedzą, że nie należy zmieniać znaczenia %w podstawionym ciągu? Wygląda na to, że gdybyś chciał tylko jednostronnego symbolu wieloznacznego, nic nie powstrzymuje użytkownika przed przesłaniem wartości zapytania zawierającej %na obu końcach (wiem, że w praktyce Railsy zapobiegają %wyświetlaniu się w ciągu zapytania, ale wygląda na to, że powinny być przed tym chronione na poziomie ActiveRecord).
Steven
8
Czy to nie jest podatne na ataki typu SQL injection?
Behrang Saeedzadeh
7
@Behrang no 8) User.where ("nazwa taka jak% # {params [: query]}% lub opis jak% # {params [: query]}%"). To_sql byłby podatny, ale w formacie, który pokazuję , Railsy uciekają z params [: query]
Reuben Mallaby
Przepraszamy za offtopic. Mam sql git metody to_sqllub menedżera arel, jak wykonać sql na db?
Малъ Скрылевъ
Model.where (to_sql_result)
Pedro Rolo
4

Odpowiedź Reubena Mallaby'ego można dalej skrócić, aby użyć powiązań parametrów:

User.where("name like :kw or description like :kw", :kw=>"%#{params[:query]}%").to_sql
cgg5207
źródło