Posted about 6 years ago. Visible to the public.
Support OR in Active Record
Rails 5 has added OR method to Active Relation for generating queries with OR clause.
Copy>> 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.
Some Examples of OR usage
With group and having
Copy>> 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>]>
With scope
Copyclass 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>]>
With combination of scopes
Copyclass 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>]>
Constraints for using OR method
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
.
Copy>> 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.
Copy>> 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>]>