Comment faire une requête LIKE dans Arel et Rails?


115

Je veux faire quelque chose comme:

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

Ma tentative à Arel:

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

Cependant, cela devient:

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

Arel encapsule correctement la chaîne de requête «Smith», mais comme il s'agit d'une instruction LIKE, cela ne fonctionne pas.

Comment faire une requête LIKE dans Arel?

Bonus PS - J'essaie en fait de scanner deux champs de la table, le nom et la description, pour voir s'il y a des correspondances avec la requête. Comment cela fonctionnerait-il?


1
J'ai mis à jour la réponse arel pour le bonus.
— Pedro Rolo

Réponses:


275

Voici comment vous effectuez une requête similaire dans 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
Contrairement à l'utilisation where("name like ?", ...), cette approche est plus portable dans différentes bases de données. Par exemple, cela entraînerait ILIKEune utilisation dans une requête sur une base de données Postgres.
— dkobozev

20
est-ce protégé contre les injections SQL?
— sren

7
Cela ne protège PAS complètement contre l'injection SQL. Essayez de définir user_name sur "%". La requête renverra des correspondances
— travis-146

5
J'ai essayé d'injecter SQL en utilisant directement les paramètres User.where(users[:name].matches("%#{params[:user_name]}%")), j'ai essayé TRUNCATE users;et d'autres requêtes similaires et rien ne s'est passé du côté SQL. Cela me semble sûr.
— earlonrails

5
À utiliser .gsub(/[%_]/, '\\\\\0')pour échapper les caractères génériques MySql.
— aercolino

116

Essayer

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

PS.

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

Et ça fait longtemps mais @ cgg5207 a ajouté une modification (surtout utile si vous allez rechercher des paramètres à nom long ou plusieurs paramètres à nom long ou si vous êtes trop paresseux pour taper)

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

ou

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

9
Comment Rails sait-il qu'il ne doit pas s'échapper %dans la chaîne substituée? Il semble que si vous ne vouliez qu'un caractère générique unilatéral, rien n'empêche l'utilisateur de soumettre une valeur de requête qui inclut %aux deux extrémités (je sais qu'en pratique, Rails empêche %d'apparaître dans une chaîne de requête, mais il semble y avoir devrait être une protection contre cela au niveau ActiveRecord).
— Steven

8
N'est-ce pas vulnérable aux attaques par injection SQL?
— Behrang Saeedzadeh

7
@Behrang no 8) User.where ("nom comme% # {params [: query]}% ou description comme% # {params [: query]}%"). To_sql serait vulnérable, mais, dans le format que je montre , Rails échappe les paramètres [: query]
— Reuben Mallaby

Désolé pour offtopic. J'ai le sql git de method to_sqlou arel manager, comment exécuter le sql sur db?
— Малъ Скрылевъ

Model.where (to_sql_result)
— Pedro Rolo

4

La réponse de Reuben Mallaby peut être raccourcie pour utiliser les liaisons de paramètres:

User.where("name like :kw or description like :kw", :kw=>"%#{params[:query]}%").to_sql
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.