Как указать несколько значений в где с интерфейсом запроса AR в rails3 - PullRequest
25 голосов
/ 18 февраля 2011

Согласно разделу 2.2 руководства по рельсам в интерфейсе запросов Active Record здесь :

, который, кажется, указывает на то, что я могу передать строку, определяющую условие (я), а затем массив значений, которые должны быть заменены в какой-то момент, пока строится арель. Итак, у меня есть оператор, который генерирует мою строку условий, которая может представлять собой различное количество атрибутов, связанных между собой либо И, либо ИЛИ между ними, и я передаю массив как второй аргумент метода where, и я получаю:

ActiveRecord :: PreparedStatementInvalid: неправильное количество переменных связывания (1 на 5)

, что наводит меня на мысль, что я делаю это неправильно. Однако я не нахожу ничего о том, как сделать это правильно. Чтобы переформулировать проблему другим способом, мне нужно передать строку в метод where, такой как "table.attribute =? AND table.attribute1 =? OR table.attribute1 =?" с неизвестным числом этих условий, соединенных или объединенных вместе, и затем передают что-то, что я думал, будет массивом в качестве второго аргумента, который будет использоваться для замены значений в строке условий первого аргумента. Это правильный подход, или я просто пропускаю какую-то другую огромную концепцию и ошибаюсь? Я думаю, что каким-то образом это должно быть возможно, если не считать простой строки SQL.

Ответы [ 7 ]

36 голосов
/ 03 октября 2014

Это на самом деле довольно просто:

Model.where(attribute: [value1,value2])
23 голосов
/ 18 февраля 2011

Похоже, вы делаете что-то вроде этого:

Model.where("attribute = ? OR attribute2 = ?", [value, value])

В то время как вам нужно сделать это:

# notice the lack of an array as the last argument
Model.where("attribute = ? OR attribute2 = ?", value, value)

Посмотрите на http://guides.rubyonrails.org/active_record_querying.html#array-conditions для более подробной информации о том, как это работает.

21 голосов
/ 10 сентября 2014

Вместо того, чтобы передавать один и тот же параметр несколько раз в where(), как это

User.where(
  "first_name like ? or last_name like ? or city like ?", 
  "%#{search}%", "%#{search}%", "%#{search}%"
)

, вы можете легко предоставить хеш

User.where(
  "first_name like :search or last_name like :search or city like :search",
  {search: "%#{search}%"}
)

, который делает ваш запрос намного более читабельным в течение длительного времени.списки аргументов.

12 голосов
/ 01 мая 2015

Похоже, вы делаете что-то вроде этого:

Model.where("attribute = ? OR attribute2 = ?", [value, value])

В то время как вам нужно сделать это:

#notice the lack of an array as the last argument
Model.where("attribute = ? OR attribute2 = ?", value, value) Have a

посмотрите на http://guides.rubyonrails.org/active_record_querying.html#array-conditions для получения более подробной информации о том, как это работает.

Был действительно близко. Вы можете превратить массив в список аргументов с помощью * my_list.

Model.where("id = ? OR id = ?", *["1", "2"])

OR

params = ["1", "2"]
Model.where("id = ? OR id = ?", *params)

Должно работать

4 голосов
/ 27 января 2013

Если вы хотите связать воедино открытый список условий (имен и значений атрибутов), я бы предложил использовать таблицу arel.

Немного сложно дать конкретику, поскольку ваш вопрос настолько расплывчатый, поэтому я просто объясню, как это сделать, для простого случая модели Post и нескольких атрибутов, скажем title, summary и user_id (то есть пользователь has_many сообщений).

Во-первых, возьмите таблицу арелей для модели:

table = Post.arel_table

Затем начните строить свой предикат (который вы в конечном итоге будете использовать для создания SQL-запроса):

relation = table[:title].eq("Foo")
relation = relation.or(table[:summary].eq("A post about foo"))
relation = relation.and(table[:user_id].eq(5))

Здесь table[:title], table[:summary] и table[:user_id] являются представлениями столбцов в таблице posts. Когда вы вызываете table[:title].eq("Foo"), вы создаете предикат, примерно эквивалентный условию поиска (получите все строки, столбец заголовка которых равен «Foo»). Эти предикаты могут быть соединены вместе с and и or.

Когда ваш агрегированный предикат готов, вы можете получить результат с помощью:

Post.where(relation)

, который сгенерирует SQL:

SELECT "posts".* FROM "posts"
WHERE (("posts"."title" = "Foo" OR "posts"."summary" = "A post about foo")
AND "posts"."user_id" = 5)

Вы получите все сообщения с заголовком «Foo» или резюме «Сообщение о Foo», и , принадлежащие пользователю с идентификатором 5.

Обратите внимание на то, как предикаты верблюдов могут бесконечно объединяться в цепочку для создания все более сложных запросов. Это означает, что если у вас есть (скажем) хэш пар атрибут / значение и есть какой-то способ узнать, использовать ли AND или OR для каждой из них, вы можете циклически просматривать их один за другим и создавать свое условие :

relation = table[:title].eq("Foo")
hash.each do |attr, value|
  relation = relation.and(table[attr].eq(value))
  # or relation = relation.or(table[attr].eq(value)) for an OR predicate
end
Post.where(relation)

Помимо простоты условий объединения, еще одним преимуществом таблиц arel является то, что они независимы от базы данных, поэтому вам не нужно беспокоиться о том, будет ли ваш запрос MySQL работать в PostgreSQL и т. Д.

Вот Railscast с подробностями об арене: http://railscasts.com/episodes/215-advanced-queries-in-rails-3?view=asciicast

Надеюсь, это поможет.

2 голосов
/ 27 марта 2014

Вы можете использовать хеш вместо строки.Создайте хеш с любым количеством условий и соответствующих значений, которые вы собираетесь иметь, и поместите его в первый аргумент метода where.

1 голос
/ 26 сентября 2015

НЕВЕРНО Это то, что я делал по какой-то причине.

keys = params[:search].split(',').map!(&:downcase)
# keys are now ['brooklyn', 'queens']

query = 'lower(city) LIKE ?'

if keys.size > 1
  # I need something like this depending on number of keys 
  # 'lower(city) LIKE ? OR lower(city) LIKE ? OR lower(city) LIKE ?'
 query_array = []
 keys.size.times { query_array << query }
 #['lower(city) LIKE ?','lower(city) LIKE ?']
 query = query_array.join(' OR ')
 # which gives me 'lower(city) LIKE ? OR lower(city) LIKE ?'
end
# now I can query my model
# if keys size is one then keys are just 'brooklyn', 
# in this case it is  'brooklyn', 'queens'
# @posts = Post.where('lower(city) LIKE ? OR lower(city) LIKE ?','brooklyn', 'queens' )
@posts = Post.where(query, *keys )

сейчас, однако - да, это очень просто. как упомянуто nfriend21

Model.where(attribute: [value1,value2])

делает то же самое

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...