Rails 4 LIKE query - 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, оба из которых я никогда не использовал, поэтому не уверен, что это и функция activerecord, или, возможно, вещь postgres.

Как я могу настроить это так, чтобы '%my_search%'в конечном запросе было лайк ?

Ответы:


229

Ваш заполнитель заменяется строкой, и вы неправильно с ней обращаетесь.

замещать

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

с участием

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

6
Разве это не уязвимо для SQL-инъекции? Я имею в виду, searchпродезинфицированы ли эти строки?
jdscosta91

6
@ jdscosta91 ?в где позаботятся о дезинфекции
house9

7
Экземпляры @ house9 внутри %и _внутри searchне будут обрабатываться при таком подходе.
Барри Келли

8
Когда используешь '?' таким образом в рельсах он преобразуется в параметризованный запрос. Данные в параметре не очищаются (%), но невозможно изменить контекст вне запроса и превратить его в обработанные операторы SQL.
Дэвид Хельцер 06

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для имени - postgres нечувствительная к регистру версия 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 нет символа процента)
sekmo

@sekmo - должно работать, процент будет в search_stringпеременной. Я думаю, что SELECT 1 AS oneвывод находится только в консоли rails или вы используете limit(1)? К вашему сведению: 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

Непроверено, хотя это может быть что-то вроде этого: scope search_attributes, -> (query, 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-инъекции (что плохо). Я бы посоветовал вам использовать CONCAT()функцию SQL для подготовки такой строки:

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


Я просто реализовал это в приложении, и он отлично работал. Спасибо, Джон.
fuzzygroup

Что касается инъекций, нет, и в любом случае это не имеет значения. Строка вставляется в sql, и рельсы должны были проверить ее раньше (не имеет значения %, добавлен / добавлен или нет). Либо он работает, как ожидалось, либо у rails есть серьезная ошибка, затрагивающая оба случая.
Estani

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.