Rails 5 adds OR support in Active Record

Abhishek Jain

By Abhishek Jain

on 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.

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.

Stay up to date with our blogs. Sign up for our newsletter.

We write about Ruby on Rails, ReactJS, React Native, remote work,open source, engineering & design.