Rails 4 LIKE-запит - ActiveRecord додає лапки


127

Я намагаюся зробити подібний запит, як це

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

Але коли він запускається щось додає лапки, що призводить до того, що оператор sql виходить так

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

Тож ви можете побачити мою проблему. Я використовую Rails 4 та Postgres 9, які я ніколи не використовував, так що не впевнений, чи є це речі та активними записами або, можливо, що є postgres.

Як я можу це налаштувати так, щоб у мене був результат '%my_search%'у кінцевому запиті?

Відповіді:


228

Ваш заповнювач місця замінено рядком, і ви не обробляєте його правильно.

Замініть

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

з

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

6
Чи не вразлива ця ін'єкція SQL? Я маю на увазі, чи searchсаніфіковані ці рядки?
jdscosta91

6
@ jdscosta91 the ?in the де подбає про санітарну
обробку

7
При такому підході випадки, що знаходяться в будинку %або _всередині нього search, не підлягатимуть санітарній обробці.
Баррі Келлі

8
Під час використання "?" таким чином у рейках перетворюється на параметризований запит. Дані в параметрі не санітизуються (%), але неможливо змінити контекст із запиту та перетворити його в оброблені оператори SQL.
Девід Хольцер

50

Замість використання conditionsсинтаксису з Rails 2 використовуйте whereметод 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

ПРИМІТКА: вищевказаний використовує синтаксис параметрів замість? заповнювач: вони обидва повинні генерувати однакові 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

ПРИМІТКА: використання ILIKEдля назви - нечутливої ​​до регістру версії LIKE


Чи все-таки це справедливо для Rails 5? Тому що, якщо у мене Movie.where("title ILIKE :s", s: search_string)це буде переведено на SELECT 1 AS one FROM "movies" WHERE (title ILIKE 'test') LIMIT $1ActiveRecord (Rails 5.1.6) - будь ласка, зауважте, що після ILIKE немає символу відсотків)
semo

@sekmo - це повинно працювати, відсоток пішов би в search_stringзмінну. Я думаю, що SELECT 1 AS oneвихід знаходиться тільки в консолі рейки або ви використовуєте limit(1)? FYI: Rails 5.1.6 має проблеми із безпекою, скористайтеся 5.1.6.2 або 5.1.7
house9

22

Хоча стропова інтерполяція буде працювати, оскільки у вашому запитанні вказані рейки 4, ви можете використовувати для цього Arel і зберігати агностику своєї бази даних.

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

Справді легко створити функцію, щоб це зробити динамічно зі списком атрибутів
cevaris

Неперевірений, хоча це може бути щось на кшталт цього: застосування search_attributes, -> (запит, атрибути) {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})}
Педро Роло

8

ActiveRecord досить розумний, щоб знати, що параметр, на який посилається, ?- це рядок, і тому він вкладає його в одиничні лапки. Ви можете, як пропонує одна публікація, використовувати інтерполяцію рядка Ruby, щоб набивати рядок потрібними %символами. Однак це може піддати вас ін'єкції SQL (що погано). Я б запропонував використовувати функцію SQL CONCAT()для підготовки рядка так:

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


Я щойно реалізував це в додатку, і він чудово працював. Дякую, Джон.
fuzzygroup

що стосується ін'єкцій, ні це не робить, і в будь-якому випадку це не має ніякої різниці. Рядок вставляється в sql і рейли повинні попередньо перевірити його (не має значення %, додається / додається чи ні). Або він працює як очікувалося, або рейки мають великий помилку, що впливає на обидва випадки.
Естані

5

Спробуйте

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

Детальнішу інформацію див. У документах щодо умов AREL.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.