Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to invert an ActiveRecord::Relation query?

Let's say we have the following:

irb> Post.where(:hidden => true).to_sql
=> "SELECT `posts`.* FROM `posts` WHERE posts.hidden = 1"

Could we somehow get an inverted SQL query out of it?

What I am looking for, should probably look like this:

irb> Post.where(:hidden => true).invert.to_sql
=> "SELECT `posts`.* FROM `posts` WHERE NOT (posts.hidden = 1)"
like image 736
Kostas Avatar asked Mar 14 '11 18:03

Kostas


People also ask

What is ActiveRecord relation?

Whereas an instance of ActiveRecord::Relation is a representation of a query that can be run against your database (but wasn't run yet). Once you run that query by calling to_a , each , first etc. on that Relation a single instance or an array of ActiveRecord::Base instances will be returned.

What is Inverse_of?

From the documentation, it seems like the :inverse_of option is a method for avoiding SQL queries, not generating them. It's a hint to ActiveRecord to use already loaded data instead of fetching it again through a relationship.

What is ActiveRecord :: Base in Rails?

ActiveRecord::Base indicates that the ActiveRecord class or module has a static inner class called Base that you're extending. Edit: as Mike points out, in this case ActiveRecord is a module... ActiveRecord is defined as a module in Rails, github.com/rails/rails/tree/master/activerecord/lib/…

What is use of inverse_ of in Rails?

Rails 7 now allows automatic inverse_of detection for associations with scopes. Feb 1, 2022 , by Swaathi Kakarla. 2 minute read. ActiveRecord has many tricks up its sleeve. One of the many is this seemingly simple association option called inverse_of , which helps us to explicitly declare bi-directional associations.


1 Answers

invert_where (Rails 7+)

Starting from Rails 7, there is a new invert_where method.

According to the docs, it:

Allows you to invert an entire where clause instead of manually applying conditions.

class User
  scope :active, -> { where(accepted: true, locked: false) }
end

User.where(accepted: true)
# WHERE `accepted` = 1

User.where(accepted: true).invert_where
# WHERE `accepted` != 1

User.active
# WHERE `accepted` = 1 AND `locked` = 0

User.active.invert_where
# WHERE NOT (`accepted` = 1 AND `locked` = 0)

Be careful because this inverts all conditions before invert_where call.

class User
  scope :active, -> { where(accepted: true, locked: false) }
  scope :inactive, -> { active.invert_where } # Do not attempt it
end

# It also inverts `where(role: 'admin')` unexpectedly.
User.where(role: 'admin').inactive
# WHERE NOT (`role` = 'admin' AND `accepted` = 1 AND `locked` = 0)

Sources:

  • invert_where from official Rails API docs.
  • Link to the PR.
  • BigBinary blog article about invert_where method.
like image 90
Marian13 Avatar answered Oct 23 '22 04:10

Marian13