May 30, 2016
This blog is part of our Rails 5 series.
Rails 5 has added OR method to Active Relation for generating queries with OR clause.
>> Post.where(id: 1).or(Post.where(title: 'Learn Rails'))
SELECT "posts".* FROM "posts" WHERE ("posts"."id" = ? OR "posts"."title" = ?) [["id", 1], ["title", "Learn Rails"]]
=> <ActiveRecord::Relation [#<Post id: 1, title: 'Rails'>]>
This returns ActiveRecord::Relation
object, which is logical union of two
relations.
>> posts = Post.group(:user_id)
>> posts.having('id > 3').or(posts.having('title like "Hi%"'))
SELECT "posts".* FROM "posts" GROUP BY "posts"."user_id" HAVING ((id > 2) OR (title like "Rails%"))
=> <ActiveRecord::Relation [#<Post id: 3, title: "Hi", user_id: 4>,
#<Post id: 6, title: "Another new blog", user_id: 6>]>
class Post < ApplicationRecord
scope :contains_blog_keyword, -> { where("title LIKE '%blog%'") }
end
>> Post.contains_blog_keyword.or(Post.where('id > 3'))
SELECT "posts".* FROM "posts" WHERE ((title LIKE '%blog%') OR (id > 3))
=> <ActiveRecord::Relation [#<Post id: 4, title: "A new blog", user_id: 6>,
#<Post id: 5, title: "Rails blog", user_id: 4>,
#<Post id: 6, title: "Another new blog", user_id: 6>]>
class Post < ApplicationRecord
scope :contains_blog_keyword, -> { where("title LIKE '%blog%'") }
scope :id_greater_than, -> (id) {where("id > ?", id)}
scope :containing_blog_keyword_with_id_greater_than, ->(id) { contains_blog_keyword.or(id_greater_than(id)) }
end
>> Post.containing_blog_keyword_with_id_greater_than(2)
SELECT "posts".* FROM "posts" WHERE ((title LIKE '%blog%') OR (id > 2)) ORDER BY "posts"."id" DESC
=> <ActiveRecord::Relation [#<Post id: 3, title: "Hi", user_id: 6>,
#<Post id: 4, title: "A new blog", user_id: 6>,
#<Post id: 5, title: "Another new blog", user_id: 6>,
<#Post id: 6, title: "Another new blog", user_id: 6>]>
The two relations must be structurally compatible, they must be scoping the same
model, and they must differ only by WHERE
or HAVING
.
In order to use OR operator, neither relation should have a limit
, offset
,
or distinct
.
>> Post.where(id: 1).limit(1).or(Post.where(:id => [2, 3]))
ArgumentError: Relation passed to #or must be structurally compatible. Incompatible values: [:limit]
When limit
, offset
or distinct
is passed only with one relation, then it
throws ArgumentError
as shown above.
As of now, we can use limit
, offset
or distinct
when passed with both the
relations and with same the parameters.
>> Post.where(id: 1).limit(2).or(Post.where(:id => [2, 3]).limit(2))
SELECT "posts".* FROM "posts" WHERE ("posts"."id" = ? OR "posts"."id" IN (2, 3)) LIMIT ? [["id", 1], ["LIMIT", 2]]
=> <ActiveRecord::Relation [#<Post id: 1, title: 'Blog', user_id: 3, published: true>,
#<Post id: 2, title: 'Rails 5 post', user_id: 4, published: true>]>
There is an issue open in which
discussions are ongoing regarding completely stopping usage of limit
, offset
or distinct
when using with or
.
If this blog was helpful, check out our full blog archive.