Подтвердить что ты не робот

Запрос 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-запрос?

4b9b3361

Ответ 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{...}, чтобы восстановить исходный контекст.