Zapytanie Rails 4 LIKE - ActiveRecord dodaje cudzysłowy

127

Próbuję wykonać podobne zapytanie

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

Ale kiedy jest uruchamiany, coś dodaje cudzysłowy, co powoduje, że instrukcja sql wygląda tak

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

Więc możesz zobaczyć mój problem. Używam Rails 4 i Postgres 9, z których nigdy nie korzystałem, więc nie jestem pewien, czy jest to rzecz z aktywnym nagraniem, czy może postgres.

Jak mogę to ustawić tak, żeby '%my_search%'w końcu było zapytanie?

Harry Forbess
źródło

Odpowiedzi:

228

Twój symbol zastępczy jest zastępowany ciągiem i nie radzisz sobie z nim prawidłowo.

Zastąpić

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

z

"name LIKE ? OR postal_code LIKE ?", "%#{search}%", "%#{search}%"
rb512
źródło
6
Czy to nie jest podatne na iniekcję SQL? To znaczy, czy te searchstruny są odkażone?
jdscosta91
6
@ jdscosta91, ?gdzie zajmie się odkażaniem
house9
7
Przy takim podejściu wystąpienia @ house9 %i _wewnątrz searchnie zostaną odkażone.
Barry Kelly
8
Podczas używania '?' w ten sposób w railsach jest konwertowany na sparametryzowane zapytanie. Dane w parametrze nie są oczyszczone (%), ale nie można zmienić kontekstu zapytania i przekształcić go w przetworzone instrukcje SQL.
David Hoelzer,
50

Zamiast używać conditionsskładni z Rails 2, użyj wheremetody 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

UWAGA: powyższe używa składni parametru zamiast? symbol zastępczy: oba powinny generować ten sam 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

UWAGA: używając ILIKEnazwy - postgres wersja LIKE bez rozróżniania wielkości liter

domek 9
źródło
Czy to nadal działa na Rails 5? Bo jeśli mam Movie.where("title ILIKE :s", s: search_string)to zostanie przetłumaczone na SELECT 1 AS one FROM "movies" WHERE (title ILIKE 'test') LIMIT $1przez ActiveRecord (Rails 5.1.6) - proszę zauważyć, że po
ILIKE
@sekmo - powinno działać, w search_stringzmiennej zostanie umieszczony procent . Myślę, że SELECT 1 AS onewyjście jest tylko w konsoli railsowej lub używasz limit(1)? FYI: Rails 5.1.6 ma problemy z bezpieczeństwem, użyj zamiast tego 5.1.6.2 lub 5.1.7
house9
22

Chociaż interpolacja ciągów zadziała, ponieważ twoje pytanie określa rails 4, możesz użyć do tego Arel i zachować agnostyk bazy danych aplikacji.

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
numery1311407
źródło
Naprawdę powinno być łatwo stworzyć funkcję, która zrobiłaby to dynamicznie, z listą atrybutów
cevaris
Nie przetestowano, choć może to wyglądać mniej więcej tak: zasięg search_attributes, -> (zapytanie, atrybuty) {arel_attributes = attributes.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 jest na tyle sprytny, że wie, że parametr, do którego odwołuje ?się ciąg, jest ciągiem znaków, więc zawiera go w pojedynczych cudzysłowach. Ty może jako jedno stanowisko sugeruje używanie Ruby interpolacji ciąg pad ciąg z wymaganymi %symbolami. Może to jednak narazić Cię na wstrzyknięcie SQL (co jest złe). Sugerowałbym użycie CONCAT()funkcji SQL do przygotowania ciągu w następujący sposób:

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

John Cleary
źródło
Właśnie zaimplementowałem to w aplikacji i działało świetnie. Dziękuję John.
fuzzygroup
jeśli chodzi o zastrzyki, nie, nie ma i w każdym razie nie ma to znaczenia. Łańcuch jest wstawiany do sql, a railsy powinny go wcześniej zweryfikować (nie ma znaczenia, czy a %jest dołączane / poprzedzane, czy nie). Albo działa zgodnie z oczekiwaniami, albo railsy mają poważny błąd wpływający na oba przypadki.
estani
5

Próbować

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

Więcej informacji można znaleźć w dokumentach dotyczących warunków AREL.

tihom
źródło
-1
.find(:all, where: "value LIKE product_%", params: { limit: 20, page: 1 })
Jeff
źródło