Skip to content

MongoId's any_of like implementation for activerecord

License

Notifications You must be signed in to change notification settings

oelmekki/activerecord_any_of

Repository files navigation

ActiverecordAnyOf

Warning! Development of activerecord_any_of is now happening on Gitlab.

Please go there for issues and merge requests.

Introduction

This gem provides #any_of and #none_of on ActiveRecord.

#any_of is inspired by any_of from mongoid.

It was released before #or was implemented in ActiveRecord. Its main purpose was to both :

  • remove the need to write a sql string when we want an OR
  • allows to write dynamic OR queries, which would be a pain with a string

It can still be useful today given the various ways you can call it. While ActiveRecord's #or only accepts relations, you can pass to #any_of and #none_of the same kind of conditions you would pass to #where:

User.where.any_of({ active: true }, ['offline = ?', required_status], 'posts_count > 0')

And you can still use relations, like AR's #or:

inactive_users = User.not_activated
offline_users = User.offline

User.where.any_of(inactive_users, offline)

Installation

In your Gemfile :

gem 'activerecord_any_of'

Usage

#any_of

It allows to compute an OR like query that leverages AR's #where syntax:

basics

User.where.any_of(first_name: 'Joe', last_name: 'Joe')
# => SELECT * FROM users WHERE first_name = 'Joe' OR last_name = 'Joe'

grouped conditions

You can separate sets of hash condition by explicitly group them as hashes :

User.where.any_of({ first_name: 'John', last_name: 'Joe' }, { first_name: 'Simon', last_name: 'Joe' })
# => SELECT * FROM users WHERE ( first_name = 'John' AND last_name = 'Joe' ) OR ( first_name = 'Simon' AND last_name = 'Joe' )

it's plain #where syntax

Each #any_of set is the same kind you would have passed to #where :

Client.where.any_of("orders_count = '2'", ["name = ?", 'Joe'], { email: 'joe@example.com' })

with relations

You can as well pass #any_of to other relations :

Client.where("orders_count = '2'").where.any_of({ email: 'joe@example.com' }, { email: 'john@example.com' })

with associations

And with associations :

User.find(1).posts.where.any_of({ published: false }, 'user_id IS NULL')

dynamic OR queries

The best part is that #any_of accepts other relations as parameter, to help compute dynamic OR queries :

banned_users = User.where(banned: true)
unconfirmed_users = User.where("confirmed_at IS NULL")
inactive_users = User.where.any_of(banned_users, unconfirmed_users)

#none_of

#none_of is the negative version of #any_of. This will return all active users :

banned_users = User.where(banned: true)
unconfirmed_users = User.where('confirmed_at IS NULL')
active_users = User.where.none_of(banned_users, unconfirmed_users)