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}%"
rb512
источник
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

house9
источник
Это все еще актуально для 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
numbers1311407
источник
Должно быть действительно легко создать функцию для динамического выполнения этого со списком атрибутов
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.

tihom
источник
-1
.find(:all, where: "value LIKE product_%", params: { limit: 20, page: 1 })
Джефф
источник