Query Rails 4 LIKE - ActiveRecord aggiunge le virgolette


127

Sto cercando di fare una query simile in questo modo

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

Ma quando viene eseguito qualcosa, si aggiungono virgolette che fanno uscire l'istruzione sql in questo modo

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

Quindi puoi vedere il mio problema. Sto usando Rails 4 e Postgres 9 che non ho mai usato, quindi non sono sicuro se sia una cosa di activerecord o possibilmente una cosa di postgres.

Come posso impostare questo in modo che mi piaccia '%my_search%'nella query finale?

Risposte:


228

Il segnaposto viene sostituito da una stringa e non lo stai gestendo correttamente.

Sostituire

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

con

"name LIKE ? OR postal_code LIKE ?", "%#{search}%", "%#{search}%"

6
Questo non è vulnerabile a SQL Injection? Voglio dire, quelle searchcorde sono disinfettate?
jdscosta91,

6
@ jdscosta91 the ?in dove si occuperà della sanificazione
house9

7
Le istanze di @ house9 all'interno %e _all'interno searchnon verranno disinfettate, con questo approccio.
Barry Kelly,

8
Quando si usa '?' in questo modo in rotaie viene convertito in una query con parametri. I dati all'interno del parametro non sono disinfettati (%) ma è impossibile modificare il contesto dalla query e trasformarlo in istruzioni SQL elaborate.
David Hoelzer,

50

Invece di usare la conditionssintassi di Rails 2, usa whereinvece il metodo di 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: quanto sopra utilizza la sintassi dei parametri anziché? segnaposto: entrambi dovrebbero generare lo stesso 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: usando ILIKEper il nome - versione non sensibile al maiuscolo / minuscolo di LIKE


È ancora valido per Rails 5? Perché se ce l'ho Movie.where("title ILIKE :s", s: search_string)viene tradotto SELECT 1 AS one FROM "movies" WHERE (title ILIKE 'test') LIMIT $1da ActiveRecord (Rails 5.1.6) - si prega di notare che non esiste un simbolo percentuale dopo ILIKE)
sekmo

@sekmo - dovrebbe funzionare, la percentuale andrebbe nella search_stringvariabile. Penso che l' SELECT 1 AS oneoutput sia solo nella console delle rotaie o che stai usando limit(1)? FYI: Rails 5.1.6 ha problemi di sicurezza, usa invece 5.1.6.2 o 5.1.7
house9

22

Mentre l'interpolazione delle stringhe funzionerà, poiché la tua domanda specifica le rotaie 4, potresti utilizzare Arel per questo e mantenere il database delle app in agnostico.

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

Dovrebbe essere davvero facile creare una funzione per farlo in modo dinamico con un elenco di attributi
cevaris,

Non testato, anche se potrebbe essere qualcosa del genere: scope search_attributes, -> (query, attributi) {arel_attributes = attributi.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 è abbastanza intelligente da sapere che il parametro a cui fa riferimento ?è una stringa e quindi lo racchiude tra virgolette singole. Si potrebbe in un unico post suggerisce l'uso di Ruby stringa di interpolazione per riempire la stringa con i necessari %simboli. Tuttavia, questo potrebbe esporti a SQL-injection (che è male). Suggerirei di utilizzare la CONCAT()funzione SQL per preparare la stringa in questo modo:

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


L'ho appena implementato in un'applicazione e ha funzionato alla grande. Grazie Giovanni
fuzzygroup,

per quanto riguarda le iniezioni, no non lo fa e in ogni caso questo non fa differenza. La stringa viene inserita in sql e le rotaie dovrebbero averla convalidata in precedenza (non importa se %viene aggiunta / aggiunta o meno). Funziona come previsto o le rotaie hanno un bug importante che interessa entrambi i casi.
estani,

5

Provare

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

Consulta i documenti sulle condizioni di AREL per ulteriori informazioni.


-1
.find(:all, where: "value LIKE product_%", params: { limit: 20, page: 1 })
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.