Posted over 5 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

Copy
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>]>

With combination of scopes

Copy
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>]>

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>]>

Owner of this card:

Avatar
Alexander M
Last edit:
over 5 years ago
by Alexander M
Tags:
Rails-5-0
Posted by Alexander M to Ruby and RoR knowledge base
This website uses short-lived cookies to improve usability.
Accept or learn more