Запрос Rails 3 ActiveRecord с использованием операторов SQL IN и SQL OR
Я пишу запрос Rails 3 ActiveRecord с использованием синтаксиса "where", который использует как SQL IN, так и оператор SQL OR и не может понять, как использовать оба из них вместе.
Этот код работает (в моей модели пользователя):
Question.where(:user_id => self.friends.ids)
#note: self.friends.ids returns an array of integers
но этот код
Question.where(:user_id => self.friends.ids OR :target => self.friends.usernames)
возвращает эту ошибку
syntax error, unexpected tCONSTANT, expecting ')'
...user_id => self.friends.ids OR :target => self.friends.usern...
Любая идея, как писать это в Rails, или просто какой должен быть исходный SQL-запрос?
Ответы
Ответ 1
raw SQL
SELECT *
FROM table
WHERE user_id in (LIST OF friend.ids) OR target in (LIST OF friends.usernames)
с каждой отдельной запятой. Я не очень хорошо знаю Rails ActiveRecord. Для И вы просто поместите запятую между этими двумя условиями, но idk об OR
Ответ 2
Вам не нужно использовать необработанный SQL, просто укажите шаблон как строку и добавьте именованные параметры:
Question.where('user_id in (:ids) or target in (:usernames)',
:ids => self.friends.ids, :usernames => self.friends.usernames)
Или позиционные параметры:
Question.where('user_id in (?) or target in (?)',
self.friends.ids, self.friends.usernames)
Вы также можете использовать отличный Squeel драгоценный камень, поскольку @erroric указал на его ответ (блок my { }
нужен, только если вам нужен доступ к self
или переменные экземпляра):
Question.where { user_id.in(my { self.friends.ids }) |
target.in(my { self.friends.usernames }) }
Ответ 3
Хотя Rails 3 AR не дает вам оператора или оператора, вы все равно можете добиться того же результата, не дойдя до SQL и напрямую использовать Arel. Под этим я подразумеваю, что вы можете сделать это следующим образом:
t = Question.arel_table
Question.where(t[:user_id].in(self.friends.ids).or(t[:username].in(self.friends.usernames)))
Некоторые могут сказать, что это не так красиво, некоторые могут сказать это довольно просто, потому что он не содержит SQL. Во всяком случае, это, безусловно, может быть красивее, и там тоже есть камень: MetaWhere
Для получения дополнительной информации см. этот railscast: http://railscasts.com/episodes/215-advanced-queries-in-rails-3
и MetaWhere: http://metautonomo.us/projects/metawhere/
ОБНОВЛЕНИЕ: Позже Райан Бэйтс сделал еще один railscast о метаобласти и метапоисках: http://railscasts.com/episodes/251-metawhere-metasearch
Позже, хотя Metawhere (и поиск) стали более или менее наследственными драгоценными камнями. То есть они даже не работают с Rails 3.1. Автор считал, что они (Метавей и поиск) нуждаются в радикальном переписывании. Настолько, что он действительно отправился на новый камень вместе. Преемником Metawhere является Squeel. Подробнее об анонсах авторов читайте здесь:
http://erniemiller.org/2011/08/31/rails-3-1-and-the-future-of-metawhere-and-metasearch/
и проверьте домашнюю страницу проекта:
http://erniemiller.org/projects/squeel/
"Metasearch 2.0" называется Ransack, и вы можете прочитать что-то об этом отсюда:
http://erniemiller.org/2011/04/01/ransack-the-library-formerly-known-as-metasearch-2-0/
Ответ 4
В качестве альтернативы вы можете использовать Squeel. На мой взгляд, это проще. Вы можете выполнить операции IN (>>
) и OR (|
), используя следующий синтаксис:
Question.where{(:user_id >> my{friends.id}) | (:target >> my{friends.usernames})}
Обычно я обертываю свои условия в (...)
, чтобы обеспечить соответствующий порядок работы - как IN, так и до OR.
Блок my{...}
выполняет методы из контекста self
, как определено перед вызовом Squeel - в этом случае Question
. Внутри блока Squeel self
относится к объекту Squeel, а не к объекту Question
(см. Squeel Readme для более). Вы обходите это, используя обертку my{...}
, чтобы восстановить исходный контекст.