¿Cómo hacer una consulta LIKE en Arel y Rails?

115

Quiero hacer algo como:

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

Mi intento en Arel:

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

Sin embargo, esto se convierte en:

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

Arel envuelve correctamente la cadena de consulta 'Smith', pero debido a que es una declaración LIKE, no funciona.

¿Cómo se hace una consulta LIKE en Arel?

PS Bonus: en realidad estoy tratando de escanear dos campos en la tabla, tanto el nombre como la descripción, para ver si hay coincidencias con la consulta. ¿Cómo funcionaría eso?

filsa
fuente
1
Actualicé la respuesta arel para el bono.
Pedro Rolo

Respuestas:

275

Así es como se realiza una consulta similar en arel:

users = User.arel_table
User.where(users[:name].matches("%#{user_name}%"))

PD:

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
fuente
10
A diferencia del uso where("name like ?", ...), este enfoque es más portátil en diferentes bases de datos. Por ejemplo, resultaría en ILIKEser utilizado en una consulta contra una base de datos de Postgres.
dkobozev
20
¿Está esto protegido contra inyecciones de SQL?
sren
7
Esto NO protege completamente contra la inyección de SQL. Intente establecer user_name en "%". La consulta devolverá coincidencias
travis-146
5
Intenté inyectar sql usando params directamente, User.where(users[:name].matches("%#{params[:user_name]}%"))intenté TRUNCATE users;y otras consultas similares y no sucedió nada en el lado de sql. Me parece seguro.
Earnrails
5
Úselo .gsub(/[%_]/, '\\\\\0')para escapar de los caracteres comodín de MySql.
aercolino
116

Tratar

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

PD.

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

Y ha pasado mucho tiempo, pero @ cgg5207 agregó una modificación (principalmente útil si va a buscar parámetros de nombre largo o múltiples con nombre largo o si es demasiado vago para escribir)

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
Reuben Mallaby
fuente
9
¿Cómo sabe Rails que no debe escapar %en la cadena sustituida? Parece que si solo quisiera un comodín de un solo lado, no hay nada que impida que el usuario envíe un valor de consulta que incluya %en ambos extremos (sé que en la práctica, Rails evita %que aparezca en una cadena de consulta, pero parece que hay debe ser protección contra esto en el nivel ActiveRecord).
Steven
8
¿No es esto vulnerable a los ataques de inyección SQL?
Behrang Saeedzadeh
7
@Behrang no 8) User.where ("nombre como% # {params [: consulta]}% o descripción como% # {params [: consulta]}%"). To_sql sería vulnerable, pero, en el formato que muestro , Rails escapa params [: consulta]
Reuben Mallaby
Perdón por fuera de tema. Tengo el sql git of method to_sqlo arel manager, ¿cómo ejecutar el sql en db?
Малъ Скрылевъ
Model.where (to_sql_result)
Pedro Rolo
4

La respuesta de Reuben Mallaby se puede acortar aún más para usar enlaces de parámetros:

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