Wie mache ich eine LIKE-Abfrage in Arel and Rails?

115

Ich möchte so etwas tun wie:

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

Mein Versuch in Arel:

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

Dies wird jedoch:

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

Arel umschließt die Abfragezeichenfolge 'Smith' korrekt, aber da dies eine LIKE-Anweisung ist, funktioniert sie nicht.

Wie macht man eine LIKE-Abfrage in Arel?

PS-Bonus - Ich versuche tatsächlich, zwei Felder in der Tabelle zu scannen, sowohl den Namen als auch die Beschreibung, um festzustellen, ob die Abfrage übereinstimmt. Wie würde das funktionieren?

filsa
quelle
1
Ich habe die Antwort für den Bonus aktualisiert.
Pedro Rolo

Antworten:

275

So führen Sie eine ähnliche Abfrage in arel durch:

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
quelle
10
Im Gegensatz zur Verwendung where("name like ?", ...)ist dieser Ansatz für verschiedene Datenbanken portabler. Dies würde beispielsweise dazu führen, ILIKEdass eine Abfrage für eine Postgres-Datenbank verwendet wird.
dkobozev
20
Ist dies gegen SQL-Injektionen geschützt?
Sren
7
Dies schützt NICHT vollständig vor SQL-Injection. Versuchen Sie, Benutzername auf "%" zu setzen. Die Abfrage wird Übereinstimmungen zurückgeben
Travis-146
5
Ich habe versucht, SQL direkt mit Params zu injizieren User.where(users[:name].matches("%#{params[:user_name]}%")), ich habe es versucht TRUNCATE users;und andere solche Abfragen und nichts ist auf der SQL-Seite passiert. Sieht für mich sicher aus.
Earlonrails
5
Verwenden Sie diese .gsub(/[%_]/, '\\\\\0')Option, um MySQL-Platzhalterzeichen zu maskieren.
Aercolino
116

Versuchen

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

PS.

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

Aaund es ist lange her, aber @ cgg5207 hat eine Änderung hinzugefügt (meistens nützlich, wenn Sie nach lang- oder mehreren langnamigen Parametern suchen oder zu faul sind, um sie einzugeben).

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

oder

User.where("name like :q or description like :q", :q => "%#{params[:query]}%").to_sql
Reuben Mallaby
quelle
9
Woher weiß Rails, dass er nicht %in der ersetzten Zeichenfolge entkommen soll ? Es scheint, als ob Sie nur einen einseitigen Platzhalter wollten, aber nichts hindert den Benutzer daran, einen Abfragewert zu senden, der %an beiden Enden enthalten ist (ich weiß, dass Rails in der Praxis verhindert, %dass er in einer Abfragezeichenfolge angezeigt wird, aber es scheint so sollte auf ActiveRecord-Ebene dagegen geschützt sein).
Steven
8
Ist dies nicht anfällig für SQL-Injection-Angriffe?
Behrang Saeedzadeh
7
@Behrang Nr. 8) User.where ("Name wie% # {params [: query]}% oder Beschreibung wie% # {params [: query]}%"). To_sql wäre anfällig, aber in dem von mir gezeigten Format , Rails entkommt Params [: Abfrage]
Reuben Mallaby
Entschuldigung für offtopic. Ich habe den SQL-Git der Methode to_sqloder des Arel-Managers. Wie führe ich den SQL- Code auf der Datenbank aus?
Малъ Скрылевъ
Model.where (to_sql_result)
Pedro Rolo
4

Die Antwort von Reuben Mallaby kann weiter verkürzt werden, um Parameterbindungen zu verwenden:

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