Come eseguire una query LIKE in Arel e Rails?


115

Voglio fare qualcosa come:

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

Il mio tentativo ad Arel:

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

Tuttavia, questo diventa:

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

Arel avvolge correttamente la stringa di query "Smith", ma poiché questa è un'istruzione LIKE non funziona.

Come si esegue una query LIKE in Arel?

Bonus PS: sto effettivamente cercando di scansionare due campi sulla tabella, sia il nome che la descrizione, per vedere se ci sono corrispondenze con la query. Come funzionerebbe?


1
Ho aggiornato la risposta arel per il bonus.
Pedro Rolo

Risposte:


275

Ecco come eseguire una query simile in 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
A differenza dell'utilizzo where("name like ?", ...), questo approccio è più portabile su diversi database. Ad esempio, risulterebbe ILIKEessere utilizzato in una query su un database Postgres.
dkobozev

20
è protetto contro le iniezioni SQL?
sren

7
Questo NON protegge completamente dall'iniezione SQL. Prova a impostare user_name su "%". La query restituirà corrispondenze
travis-146

5
Ho provato a iniettare sql usando direttamente params User.where(users[:name].matches("%#{params[:user_name]}%")), ho provato TRUNCATE users;e altre query simili e non è successo nulla sul lato sql. Mi sembra al sicuro.
earlonrails

5
Utilizzare .gsub(/[%_]/, '\\\\\0')per l'escape dei caratteri jolly MySql.
aercolino

116

Provare

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

PS.

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

E è passato molto tempo ma @ cgg5207 ha aggiunto una modifica (utile soprattutto se hai intenzione di cercare parametri con nome lungo o più parametri con nome lungo o sei troppo pigro per digitare)

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

o

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

9
Come fa Rails a sapere di non dover eseguire l'escape %nella stringa sostituita? Sembra che se volessi solo un carattere jolly unilaterale, non c'è nulla che impedisca all'utente di inviare un valore di query che include %entrambe le estremità (so che in pratica Rails impedisce %di essere visualizzato in una stringa di query, ma sembra che ci dovrebbe essere una protezione contro questo a livello di ActiveRecord).
Steven

8
Non è vulnerabile agli attacchi di SQL injection?
Behrang Saeedzadeh

7
@ Behrang no 8) User.where ("name like% # {params [: query]}% or description like% # {params [: query]}%"). To_sql sarebbe vulnerabile, ma, nel formato che mostro , Rails sfugge ai parametri [: query]
Reuben Mallaby

Scusa per l'offtopic. Ho il git sql di method to_sqlo arel manager, come eseguire sql su db?
Малъ Скрылевъ

Model.where (to_sql_result)
Pedro Rolo

4

La risposta di Reuben Mallaby può essere ulteriormente abbreviata per utilizzare le associazioni di parametri:

User.where("name like :kw or description like :kw", :kw=>"%#{params[:query]}%").to_sql
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.