This blog is part of our Rails 5 series.
Rails 5 has added OR method to Active Relation for generating queries with OR clause.
1 2>> Post.where(id: 1).or(Post.where(title: 'Learn Rails')) 3 SELECT "posts".* FROM "posts" WHERE ("posts"."id" = ? OR "posts"."title" = ?) [["id", 1], ["title", "Learn Rails"]] 4 5=> <ActiveRecord::Relation [#<Post id: 1, title: 'Rails'>]> 6
This returns ActiveRecord::Relation object, which is logical union of two relations.
Some Examples of OR usage
With group and having
1 2>> posts = Post.group(:user_id) 3>> posts.having('id > 3').or(posts.having('title like "Hi%"')) 4SELECT "posts".* FROM "posts" GROUP BY "posts"."user_id" HAVING ((id > 2) OR (title like "Rails%")) 5 6=> <ActiveRecord::Relation [#<Post id: 3, title: "Hi", user_id: 4>, 7#<Post id: 6, title: "Another new blog", user_id: 6>]> 8
With scope
1 2class Post < ApplicationRecord 3 scope :contains_blog_keyword, -> { where("title LIKE '%blog%'") } 4end 5 6>> Post.contains_blog_keyword.or(Post.where('id > 3')) 7SELECT "posts".* FROM "posts" WHERE ((title LIKE '%blog%') OR (id > 3)) 8 9=> <ActiveRecord::Relation [#<Post id: 4, title: "A new blog", user_id: 6>, 10#<Post id: 5, title: "Rails blog", user_id: 4>, 11#<Post id: 6, title: "Another new blog", user_id: 6>]> 12
With combination of scopes
1 2class Post < ApplicationRecord 3 4 scope :contains_blog_keyword, -> { where("title LIKE '%blog%'") } 5 scope :id_greater_than, -> (id) {where("id > ?", id)} 6 7 scope :containing_blog_keyword_with_id_greater_than, ->(id) { contains_blog_keyword.or(id_greater_than(id)) } 8end 9 10>> Post.containing_blog_keyword_with_id_greater_than(2) 11SELECT "posts".* FROM "posts" WHERE ((title LIKE '%blog%') OR (id > 2)) ORDER BY "posts"."id" DESC 12 13=> <ActiveRecord::Relation [#<Post id: 3, title: "Hi", user_id: 6>, 14#<Post id: 4, title: "A new blog", user_id: 6>, 15#<Post id: 5, title: "Another new blog", user_id: 6>, 16<#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.
1 2>> Post.where(id: 1).limit(1).or(Post.where(:id => [2, 3])) 3 4ArgumentError: Relation passed to #or must be structurally compatible. Incompatible values: [:limit] 5
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.
1 2>> Post.where(id: 1).limit(2).or(Post.where(:id => [2, 3]).limit(2)) 3 4SELECT "posts".* FROM "posts" WHERE ("posts"."id" = ? OR "posts"."id" IN (2, 3)) LIMIT ? [["id", 1], ["LIMIT", 2]] 5 6=> <ActiveRecord::Relation [#<Post id: 1, title: 'Blog', user_id: 3, published: true>, 7#<Post id: 2, title: 'Rails 5 post', user_id: 4, published: true>]> 8
There is an issue open in which discussions are ongoing regarding completely stopping usage of limit, offset or distinct when using with or.