Consulta Rails 4 LIKE: ActiveRecord agrega comillas

127

Estoy tratando de hacer una consulta similar

def self.search(search, page = 1 )
  paginate :per_page => 5, :page => page,
    :conditions => ["name LIKE '%?%' OR postal_code like '%?%'", search, search],   order => 'name'
end

Pero cuando se ejecuta, algo agrega comillas, lo que hace que la instrucción sql salga así

SELECT COUNT(*)
FROM "schools" 
WHERE (name LIKE '%'havard'%' OR postal_code like '%'havard'%')):

Entonces puedes ver mi problema. Estoy usando Rails 4 y Postgres 9, los cuales nunca he usado, así que no estoy seguro de si es una cosa de grabación activa o posiblemente una cosa de postgres.

¿Cómo puedo configurar esto para que me guste '%my_search%'en la consulta final?

Harry Forbess
fuente

Respuestas:

228

Su marcador de posición se reemplaza por una cadena y no lo está manejando correctamente.

Reemplazar

"name LIKE '%?%' OR postal_code LIKE '%?%'", search, search

con

"name LIKE ? OR postal_code LIKE ?", "%#{search}%", "%#{search}%"
rb512
fuente
66
¿No es esto vulnerable a la inyección de SQL? Quiero decir, ¿se searchsanean esas cuerdas?
jdscosta91
66
@ jdscosta91 the ?in the where se encargará de la desinfección
house9
77
Las instancias @ house9 de %y _dentro searchno serán desinfectadas, bajo este enfoque.
Barry Kelly
8
Cuando usas '?' de esta manera en rails se convierte en una consulta parametrizada. Los datos dentro del parámetro no están desinfectados (el%) pero es imposible cambiar el contexto fuera de la consulta y convertirlo en declaraciones SQL procesadas.
David Hoelzer
50

En lugar de usar la conditionssintaxis de Rails 2, use el wheremétodo de Rails 4 :

def self.search(search, page = 1 )
  wildcard_search = "%#{search}%"

  where("name ILIKE :search OR postal_code LIKE :search", search: wildcard_search)
    .page(page)
    .per_page(5)
end

NOTA: lo anterior usa la sintaxis de parámetros en lugar de? marcador de posición: ambos deberían generar el mismo sql.

def self.search(search, page = 1 )
  wildcard_search = "%#{search}%"

  where("name ILIKE ? OR postal_code LIKE ?", wildcard_search, wildcard_search)
    .page(page)
    .per_page(5)
end

NOTA: usar ILIKEpara el nombre - versión postgres insensible a mayúsculas y minúsculas de LIKE

casa9
fuente
¿Sigue siendo válido para Rails 5? Porque si lo tengo ActiveRecord lo Movie.where("title ILIKE :s", s: search_string)traduce SELECT 1 AS one FROM "movies" WHERE (title ILIKE 'test') LIMIT $1(Rails 5.1.6), tenga en cuenta que no hay un símbolo de porcentaje después del ILIKE)
sekmo
@sekmo: debería funcionar, el porcentaje iría en la search_stringvariable. ¿Creo que la SELECT 1 AS onesalida es solo en la consola de rails o está usando limit(1)? FYI: Rails 5.1.6 tiene problemas de seguridad, use 5.1.6.2 o 5.1.7 en su lugar
house9
22

Si bien la interpolación de cadenas funcionará, ya que su pregunta especifica los rieles 4, podría usar Arel para esto y mantener la base de datos de su aplicación independiente.

def self.search(query, page=1)
  query = "%#{query}%"
  name_match = arel_table[:name].matches(query)
  postal_match = arel_table[:postal_code].matches(query)
  where(name_match.or(postal_match)).page(page).per_page(5)
end
números1311407
fuente
Realmente debería ser fácil crear una función para hacer esto dinámicamente con una lista de atributos
cevaris
No probado, aunque podría ser algo como esto: alcance search_attributes, -> (consulta, atributos) {arel_attributes = atributos.map {| a | arel_table [a]} arel_queries = arel_attributes.map {| a | a.matches (query)} return where (arel_queries.reduce {| res, q | res.or q})}
Pedro Rolo
8

ActiveRecord es lo suficientemente inteligente como para saber que el parámetro al que hace referencia ?es una cadena, por lo que lo encierra entre comillas simples. Usted podría como un puesto sugiere el uso de Ruby interpolación de cadenas para rellenar la cadena con los requeridos %símbolos. Sin embargo, esto podría exponerlo a la inyección de SQL (que es malo). Te sugiero que uses la CONCAT()función SQL para preparar la cadena de esta manera:

"name LIKE CONCAT('%',?,'%') OR postal_code LIKE CONCAT('%',?,'%')", search, search)

John Cleary
fuente
Acabo de implementar esto en una aplicación y funcionó muy bien. Gracias John.
fuzzygroup
con respecto a las inyecciones, no, no lo hace y, en cualquier caso, esto no hace ninguna diferencia. La cadena se inserta en el sql y los rieles deberían haberla validado antes (no importa si %se agrega / anexó o no). O funciona como se esperaba o los rieles tienen un error importante que afecta a ambos casos.
estani
5

Tratar

 def self.search(search, page = 1 )
    paginate :per_page => 5, :page => page,
      :conditions => ["name LIKE  ? OR postal_code like ?", "%#{search}%","%#{search}%"],   order => 'name'
  end

Consulte los documentos sobre las condiciones de AREL para obtener más información.

tihom
fuente
-1
.find(:all, where: "value LIKE product_%", params: { limit: 20, page: 1 })
Jeff
fuente