Rails 4 LIKE-Abfrage - ActiveRecord fügt Anführungszeichen hinzu

127

Ich versuche eine ähnliche Abfrage zu machen

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

Aber wenn es ausgeführt wird, fügt etwas Anführungszeichen hinzu, was dazu führt, dass die SQL-Anweisung so herauskommt

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

Sie können also mein Problem sehen. Ich benutze Rails 4 und Postgres 9, die ich beide noch nie benutzt habe. Ich bin mir also nicht sicher, ob es sich um ein Activerecord-Ding oder möglicherweise ein Postgres-Ding handelt.

Wie kann ich das so einrichten, dass ich es '%my_search%'in der Endabfrage mag ?

Harry Forbess
quelle

Antworten:

228

Ihr Platzhalter wird durch eine Zeichenfolge ersetzt und Sie behandeln ihn nicht richtig.

Ersetzen

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

mit

"name LIKE ? OR postal_code LIKE ?", "%#{search}%", "%#{search}%"
rb512
quelle
6
Ist dies nicht anfällig für SQL Injection? Ich meine, sind diese searchSaiten bereinigt?
jdscosta91
6
@ jdscosta91 die ?in der wo wird für die Desinfektion
sorgen
7
@ house9-Instanzen von %und _innerhalb searchwerden bei diesem Ansatz nicht bereinigt .
Barry Kelly
8
Beim Benutzen '?' Auf diese Weise wird es in Schienen in eine parametrisierte Abfrage konvertiert. Die Daten innerhalb des Parameters werden nicht bereinigt (%), es ist jedoch unmöglich, den Kontext aus der Abfrage heraus zu ändern und in verarbeitete SQL-Anweisungen umzuwandeln.
David Hoelzer
50

Verwenden Sie stattdessen conditionsdie whereMethode von Rails 4, anstatt die Syntax von Rails 2 zu verwenden :

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

HINWEIS: In der obigen Beschreibung wird anstelle von? Die Parametersyntax verwendet. Platzhalter: Beide sollten dieselbe SQL generieren.

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

HINWEIS: Verwenden Sie ILIKEfür den Namen - postgres Groß- und Kleinschreibung von LIKE

house9
quelle
Gilt das noch für Rails 5? Denn wenn ich es habe, wird Movie.where("title ILIKE :s", s: search_string)es SELECT 1 AS one FROM "movies" WHERE (title ILIKE 'test') LIMIT $1von ActiveRecord (Rails 5.1.6) übersetzt - bitte beachten Sie, dass es nach dem ILIKE kein Prozentzeichen gibt)
sekmo
@sekmo - es sollte funktionieren, Prozentsatz würde in die search_stringVariable gehen. Ich denke, die SELECT 1 AS oneAusgabe ist nur in der Rails-Konsole oder Sie verwenden limit(1)? Zu Ihrer Information: Rails 5.1.6 hat Sicherheitsprobleme. Verwenden Sie stattdessen 5.1.6.2 oder 5.1.7
house9
22

Während die String-Interpolation funktioniert, da Ihre Frage Rails 4 angibt, könnten Sie Arel dafür verwenden und Ihre App-Datenbank agnostisch halten.

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
Nummern 1311407
quelle
Sollte wirklich einfach sein, eine Funktion zu erstellen, um dies dynamisch mit einer Liste von Attributen zu tun
Cevaris
Ungetestet, obwohl es ungefähr so ​​aussehen könnte: scope search_attributes, -> (Abfrage, Attribute) {arel_attributes = attribute.map {| a | arel_table [a]} arel_queries = arel_attributes.map {| a | a.matches (Abfrage)} return where (arel_queries.reduce {| res, q | res.or q})}
Pedro Rolo
8

ActiveRecord ist klug genug, um zu wissen, dass der Parameter, auf den ?sich das bezieht, eine Zeichenfolge ist, und schließt ihn daher in einfache Anführungszeichen ein. Sie können, wie in einem Beitrag vorgeschlagen, die Ruby-String-Interpolation verwenden, um den String mit den erforderlichen %Symbolen aufzufüllen. Dies kann Sie jedoch einer SQL-Injection aussetzen (was schlecht ist). Ich würde vorschlagen, dass Sie die SQL- CONCAT()Funktion verwenden, um den String wie folgt vorzubereiten:

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

John Cleary
quelle
Ich habe dies gerade in einer Anwendung implementiert und es hat großartig funktioniert. Danke John.
Fuzzygroup
Nein, bei Injektionen ist dies nicht der Fall, und dies macht auf keinen Fall einen Unterschied. Die Zeichenfolge wird in die SQL eingefügt, und Rails sollte sie zuvor validiert haben (unabhängig davon, ob a %angehängt / vorangestellt ist oder nicht). Entweder funktioniert es wie erwartet oder Rails hat einen großen Fehler, der beide Fälle betrifft.
Estani
5

Versuchen

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

Weitere Informationen finden Sie in den Dokumenten zu den AREL-Bedingungen.

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