ActiveRecord ИЛИ запрос

181

Как вы делаете запрос OR в Rails 3 ActiveRecord. Все примеры, которые я нахожу, имеют только запросы AND.

Редактировать: метод OR доступен начиная с Rails 5. См. ActiveRecord :: QueryMethods

pho3nixf1re
источник
7
Изучай SQL. ActiveRecord облегчает работу, но вам все равно нужно знать, что делают ваши запросы, иначе ваш проект может не масштабироваться. Я также думал, что смогу обойтись без необходимости иметь дело с SQL, и я был очень неправ в этом.
ryan0
1
@ ryan0, ты так прав. Мы можем использовать причудливые драгоценные камни и другие вещи, но мы должны знать, что делают эти драгоценные камни внутри и какова основная технология, и, возможно, использовать основные технологии без помощи драгоценного камня, если возникнет необходимость, ради производительность. Драгоценные камни создаются с учетом определенного количества вариантов использования, но могут быть ситуации, когда один из вариантов использования в нашем проекте может отличаться.
rubyprince
2
Может быть
полезным
1
Начиная с Rails 5, ИМХО, принятым ответом должна быть версия Грега Олсена:Post.where(column: 'something').or(Post.where(other: 'else'))
Филипп Классен
6
Этот вопрос имеет тег ruby-on-rails-3. Почему принятый ответ относится только к Rails 5?
iNulty

Ответы:

109

Используйте ARel

t = Post.arel_table

results = Post.where(
  t[:author].eq("Someone").
  or(t[:title].matches("%something%"))
)

Результирующий SQL:

ree-1.8.7-2010.02 > puts Post.where(t[:author].eq("Someone").or(t[:title].matches("%something%"))).to_sql
SELECT     "posts".* FROM       "posts"  WHERE     (("posts"."author" = 'Someone' OR "posts"."title" LIKE '%something%'))
Дэн МакНевин
источник
Чувствую себя немного грязно, но, по крайней мере, я не пишу SQL, который просто чувствует себя неправильно! Я собираюсь больше изучить использование Arel.
pho3nixf1re
55
Я не могу поверить, насколько более элегантный сиквел
Макарио
3
В SQL нет ничего плохого. То, что может пойти не так, это то, как мы строим строку SQL, а именно SQL-инъекцию . Таким образом, мы должны очистить вводимые пользователем данные, прежде чем предоставлять их SQL-запросу, который должен выполняться к базе данных. Это будет обрабатываться ORM , и они обрабатывают крайние случаи, которые мы, как правило, упускаем. Поэтому всегда рекомендуется использовать ORM для создания SQL-запросов.
rubyprince
5
Другая проблема с SQL заключается в том, что он не зависит от базы данных. Например, matchesбудет производить LIKEдля sqlite (без учета регистра по умолчанию) и ILIKEдля postgres (требуется явный без учета регистра оператор типа like).
amoebe
1
@ToniLeigh ActiveRecord использует SQL под капотом. Это просто синтаксис для написания SQL-запросов, который обрабатывает санацию SQL и делает ваши запросы независимыми от БД. Единственные издержки - это когда Rails преобразует синтаксис в SQL-запрос. И это всего лишь миллисекунды. Конечно, вы должны написать лучший SQL-запрос и попытаться преобразовать его в синтаксис ActiveRecord. Если SQL не может быть представлен в синтаксисе ActiveRecord, вам следует перейти на простой SQL.
rubyprince
208

Если вы хотите использовать оператор OR для значения одного столбца, вы можете передать массив, .whereи ActiveRecord будет использовать IN(value,other_value):

Model.where(:column => ["value", "other_value"]

выходы:

SELECT `table_name`.* FROM `table_name` WHERE `table_name`.`column` IN ('value', 'other_value')

Это должно достигнуть эквивалента ORна одном столбце

deadkarma
источник
13
это самый чистый и самый «рельсовый» ответ, его следовало принять
koonse
10
Спасибо @koonse, это не совсем запрос «ИЛИ», но он дает тот же результат для этой ситуации.
Deadkarma
Помогите, если сможете - stackoverflow.com/questions/15517286/…
Пратик Ботра
80
Это решение не является заменой ИЛИ, так как вы не можете использовать два разных столбца таким образом.
Фотанус
152

в Rails 3 это должно быть

Model.where("column = ? or other_column = ?", value, other_value)

Это также включает в себя raw sql, но я не думаю, что в ActiveRecord есть способ выполнить операцию ИЛИ. Ваш вопрос не вопрос нуба.

rubyprince
источник
41
Даже если есть сырой SQL - это утверждение выглядит для меня гораздо яснее, чем Арель. Может быть, даже сушилка.
Джибиэль
6
если вам нужно Page.where("pages.column = ? or pages.other_column = ?", value, other_value)
объединить
1
И это не удастся, если запрос псевдоним таблиц. Вот когда сияет арель.
DGM
58

Обновленная версия Rails / ActiveRecord может изначально поддерживать этот синтаксис. Это будет выглядеть примерно так:

Foo.where(foo: 'bar').or.where(bar: 'bar')

Как отмечено в этом запросе на получение доступа, https://github.com/rails/rails/pull/9052.

На данный момент просто придерживаться следующих работ прекрасно работает:

Foo.where('foo= ? OR bar= ?', 'bar', 'bar')

Обновление: По https://github.com/rails/rails/pull/16052or функция будет доступна в Rails 5

Обновление: функция была объединена с веткой Rails 5

Кристиан Фаззини
источник
2
orсейчас доступен Rails 5, но не реализован таким образом, потому что ожидает 1 аргумент. Ожидается объект Арель. См принятый ответ
El'Magnifico
2
orМетод ActiveRecord работает , если вы используете его напрямую: но может нарушить ваши ожидания , если он используется в объеме , который затем прикован.
Джереми Лист
То, что сказал @JeremyList, точно. Это сильно меня укусило.
courtimas
23

Rails 5 поставляется с orметодом. ( чернила к документации )

Этот метод принимает ActiveRecord::Relationобъект. например:

User.where(first_name: 'James').or(User.where(last_name: 'Scott'))
Santhosh
источник
14

Если вы хотите использовать массивы в качестве аргументов, следующий код работает в Rails 4:

query = Order.where(uuid: uuids, id: ids)
Order.where(query.where_values.map(&:to_sql).join(" OR "))
#=> Order Load (0.7ms)  SELECT "orders".* FROM "orders" WHERE ("orders"."uuid" IN ('5459eed8350e1b472bfee48375034103', '21313213jkads', '43ujrefdk2384us') OR "orders"."id" IN (2, 3, 4))

Дополнительная информация: OR запрашивает массивы в качестве аргументов в Rails 4 .

Рафал Цеслак
источник
9
Или вот так: Order.where(query.where_values.inject(:or))использовать арел всю дорогу.
Кэмерон Мартин
> ИЛИ запросы с массивами в качестве аргументов в Rails 4. Полезная ссылка! Спасибо!
Зик Фаст
7

MetaWhere плагин совершенно удивительно.

Легко смешивайте ИЛИ и И, присоединяйтесь к условиям любой ассоциации и даже указывайте ВНЕШНИЕ СОЕДИНЕНИЯ!

Post.where({sharing_level: Post::Sharing[:everyone]} | ({sharing_level: Post::Sharing[:friends]} & {user: {followers: current_user} }).joins(:user.outer => :followers.outer}
герцог
источник
6

Просто добавьте ИЛИ в условия

Model.find(:all, :conditions => ["column = ? OR other_column = ?",value, other_value])
Тоби хеде
источник
4
Это больше синтаксис для Rails 2, и он требует от меня написать хотя бы часть строки sql.
pho3nixf1re
3

Я только что извлек этот плагин из клиентской работы, который позволяет вам комбинировать области действия .or., например, Post.published.or.authored_by(current_user), Squeel (более новая реализация MetaSearch) также хорош, но не позволяет вам выполнять операции ИЛИ, поэтому логика запросов может стать немного избыточной.

Woahdae
источник
3

С рельсами + арелом, более понятный способ:

# Table name: messages
#
# sender_id:    integer
# recipient_id: integer
# content:      text

class Message < ActiveRecord::Base
  scope :by_participant, ->(user_id) do
    left  = arel_table[:sender_id].eq(user_id)
    right = arel_table[:recipient_id].eq(user_id)

    where(Arel::Nodes::Or.new(left, right))
  end
end

Производит:

$ Message.by_participant(User.first.id).to_sql 
=> SELECT `messages`.* 
     FROM `messages` 
    WHERE `messages`.`sender_id` = 1 
       OR `messages`.`recipient_id` = 1
itsnikolay
источник
3

Вы можете сделать это так:

Person.where("name = ? OR age = ?", 'Pearl', 24)

или более элегантно, установите rails_or gem и сделайте так:

Person.where(:name => 'Pearl').or(:age => 24)
Хиав Реой
источник
-2

Я хотел бы добавить, что это решение для поиска нескольких атрибутов ActiveRecord. поскольку

.where(A: param[:A], B: param[:B])

будет искать А и Б.

Томас Гаэте
источник
-4
Book.where.any_of(Book.where(:author => 'Poe'), Book.where(:author => 'Hemingway')
Мэтью Ригдон
источник
2
Добавьте пояснения к своему ответу
kvorobiev
1
Я полагаю, что Мэтью имел в виду камень activerecord_any_of, который добавляет поддержку этого синтаксиса.
DannyB
2
Если это правда, спасибо @DannyB. Ответ должен объяснить его контекст.
Себастьянсо