Rails 5: ActiveRecord OR query
Asked Answered
M

5

113

How do you do an or query in Rails 5 ActiveRecord? Also, is it possible to chain or with where in ActiveRecord queries?

Mediant answered 24/9, 2015 at 4:6 Comment(1)
May be of helpPestalozzi
M
243

The ability to chain or clause along with where clause in ActiveRecord query will be available in Rails 5. See the related discussion and the pull request.

So, you will be able to do the following things in Rails 5:

To get a post with id 1 or 2:

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

Some other examples:

(A && B) || C:

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

(A || B) && C:

    Post.where(a).or(Post.where(b)).where(c)
Mediant answered 24/9, 2015 at 4:7 Comment(6)
How can I get (A || B) && ( C || D). I tried Post.where(a).or(Post.where(b)).where(c).or(Post.where(d)) but It produces as: (A || B) && C || DCasas
@Imran i believe it would be Post.where(a).or(Post.where(b)).where(Post.where(c).or(Post.where(d))) this should create (a || b) && (c || d)Kelton
@Imran This doesn't seem to work for me: I get ArgumentError: Unsupported argument type: #<MyModel::ActiveRecord_Relation:0x00007f8edbc075a8> (MyModel::ActiveRecord_Relation)Windjammer
The equivalent to .or that takes a relation and produces an and is .merge. (A || B) && ( C || D) can be produced by Post.where(a).or(Post.where(b)).merge(Post.where(c).or(Post.where(d)))Humanity
@SiimLiiser where is merge coming from ? if its Array#merge that does not work for me, as I want a single query to be executed. and also being able to pass the whole thing as a relationStepchild
@MathieuJ. It's ActiveRecord::Relation#merge. api.rubyonrails.org/classes/ActiveRecord/…Humanity
E
14

We don't need to wait for rails 5 to use this OR query. We can also used it with rails 4.2.3. There is a backport here.

Thank to Eric-Guo for gem where-or, Now we can add this OR functionality in >= rails 4.2.3 also using this gem.

Earplug answered 24/9, 2015 at 6:11 Comment(0)
P
8

(Just an addition to the answer by K M Rakibul Islam.)

Using scopes, the code can become prettier (depending on the eyes looking):

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

scope a_or_b, -> { a.or(b) }
Porism answered 20/6, 2018 at 19:51 Comment(0)
T
5

I needed to do a (A && B) || (C && D) || (E && F)

But in Rails 5.1.4's current state this get's too complicated to accomplish with the Arel or-chain. But I still wanted to use Rails to generate as much of the query as possible.

So I made a small hack:

In my model I created a private method called sql_where:

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

Next in my scope I created an array to hold the OR's

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 '))
}

Which will produce the expected query result: SELECT * FROM `models` WHERE ((A AND B) OR (C AND D) OR (E AND F)).

And now I can easily combine this with other scopes etc. without any wrongful OR's.

The beauty being that my sql_where takes normal where-clause arguments: sql_where(name: 'John', role: 'admin') will generate (name = 'John' AND role = 'admin').

Thumping answered 5/10, 2017 at 13:21 Comment(2)
I think you can use .merge as the equivalent of &&, and build a proper tree to capture your parens. Something like... (scopeA.merge(scopeB)).or(scopeC.merge(scopeD)).or(scopeE.merge(scopeF)), assuming each of the scopes looks something like Model.where(...)Pernickety
Check out this before using merge - github.com/rails/rails/issues/33501Burn
B
1

Rails 5 has ability for or clause with where. For Example.

User.where(name: "abc").or(User.where(name: "abcd"))
Beulabeulah answered 20/12, 2018 at 11:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.