Rails 5: ActiveRecord OR запрос

Ответы:

228

Возможность связывать orпредложение вместе с whereпредложением в ActiveRecordзапросе будет доступна в Rails 5 . См. Соответствующее обсуждение и запрос на перенос .

Итак, в Rails 5 вы сможете делать следующее :

Чтобы получить postс id1 или 2:

Post.where('id = 1').or(Post.where('id = 2'))

Еще несколько примеров:

(A && B) || C:

    Post.where(a).where(b).or(Post.where(c))

(A || B) && C:

    Post.where(a).or(Post.where(b)).where(c)
К.М. Ракибул Ислам
источник
3
Как я могу получить (A || B) && (C || D). Я пробовал Post.where (a) .or (Post.where (b)). Where (c) .or (Post.where (d)), но он производит как: (A || B) && C || D
Имран Ахмад
3
@Imran, я думаю, это должно было Post.where(a).or(Post.where(b)).where(Post.where(c).or(Post.where(d)))создать (a || b) && (c || d)
engineeringmnky
1
@Imran Это, похоже, не работает для меня: я понялArgumentError: Unsupported argument type: #<MyModel::ActiveRecord_Relation:0x00007f8edbc075a8> (MyModel::ActiveRecord_Relation)
Suan
5
Эквивалент .or, который принимает отношение и создает и - это .merge. (A || B) && (C || D) могут быть созданы Post.where (a) .or (Post.where (b)). Merge (Post.where (c) .or (Post.where (d )))
Siim Liiser
2
@MathieuJ. Это ActiveRecord :: Relation # merge. api.rubyonrails.org/classes/ActiveRecord/…
Siim Liiser
13

Нам не нужно ждать, пока rails 5 будет использовать этот ORзапрос. Мы также можем использовать его с rails 4.2.3. Существует портировать здесь .

Спасибо Эрику-Гуо за драгоценный камень where-or. Теперь мы можем добавить эту ORфункциональность и в >= rails 4.2.3этот драгоценный камень.

Дипак Гупта
источник
6

(Просто дополнение к ответу К.М. Ракибула Ислама.)

Используя области видимости, код может стать красивее (в зависимости от того, какие глаза смотрят):

scope a,      -> { where(a) }
scope b,      -> { where(b) }

scope a_or_b, -> { a.or(b) }
Николай
источник
5

Мне нужно было сделать (A && B) || (C && D) || (E && F)

Но в 5.1.4текущем состоянии Rails это слишком сложно сделать с помощью Arel or-chain. Но я по-прежнему хотел использовать Rails для генерации как можно большего количества запросов.

Итак, я сделал небольшой взлом:

В моей модели я создал частный метод под названием sql_where:

private
  def self.sql_where(*args)
    sql = self.unscoped.where(*args).to_sql
    match = sql.match(/WHERE\s(.*)$/)
    "(#{match[1]})"
  end

Затем в своей области я создал массив для хранения OR

scope :whatever, -> {
  ors = []

  ors << sql_where(A, B)
  ors << sql_where(C, D)
  ors << sql_where(E, F)

  # Now just combine the stumps:
  where(ors.join(' OR '))
}

Который даст ожидаемый результат запроса: SELECT * FROM `models` WHERE ((A AND B) OR (C AND D) OR (E AND F)).

И теперь я могу легко комбинировать это с другими областями и т. Д. Без каких-либо неправомерных операций.

Прелесть в том, что мой sql_where принимает обычные аргументы where-clause: sql_where(name: 'John', role: 'admin')будет сгенерирован (name = 'John' AND role = 'admin').

mtrolle
источник
Я думаю, вы можете использовать .mergeкак эквивалент && и построить правильное дерево для захвата ваших родителей. Что-то вроде ... (scopeA.merge(scopeB)).or(scopeC.merge(scopeD)).or(scopeE.merge(scopeF)), если предположить, что каждый из прицелов выглядит примерно такModel.where(...)
nar8789
Проверьте это перед использованием слияния - github.com/rails/rails/issues/33501
Aarthi
1

В Rails 5 есть возможность использовать orпредложения where. Например.

User.where(name: "abc").or(User.where(name: "abcd"))
Форам Такрал
источник