How to Negate a Scope in Rails

Is it possible to negate a scope in Rails?

I wouldn't use a single scope for this, but two:

scope :with_missing_coins, joins(:coins).where("coins.is_missing = ?", true)
scope :without_missing_coins, joins(:coins).where("coins.is_missing = ?", false)

That way, when these scopes are used then it's explicit what's happening. With what numbers1311407 suggests, it is not immediately clear what the false argument to with_missing_coins is doing.

We should try to write code as clear as possible and if that means being less of a zealot about DRY once in while then so be it.

Negate ActiveRecord query scope

To negate an scope you can use:

Contact.where.not(id: Contact.group_search('query'))

This is not the same as using pluck (proposed in one of the comments):

Contact.where.not(id: Contact.group_search('query').pluck(:id)) 

Without the pluck, it produces one query (with two selects):

SELECT  `contacts`.* FROM `contacts` WHERE `contacts`.`id` NOT IN (SELECT `contacts`.`id` FROM `contacts` WHERE `contacts`.`group_search` = 'query')

With the pluck, it produces two independent queries:

SELECT `contacts`.`id` FROM `contacts` WHERE `contacts`.`group_search` = 'query'
SELECT `contacts`.* FROM `contacts` WHERE `contacts`.`id` NOT IN (1, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361)

When querying many records, the first one is way more efficient. Of course Contact.where.not(group_search: 'query') is more efficient as it produces one query with one select (but this may be not possible in some cases):

SELECT `contacts`.`id` FROM `contacts` WHERE `contacts`.`group_search` != 'query'

Rails 5, scope opposite to another scope

How about creating a method that takes the entire collection, and subtracts the missing_coordinates.

self.not_missing_coordinates
Places.all - Places.missing_coordinates
end

This lets you call Places.not_missing_coordinates. I looked around the docs and couldn't find anything quite like negation of a scope in the way you've described.

rails inverse of a scope

Try this

scope : all_enrolled, -> { joins(:trainings) }
scope :not_enrolled, -> { where.not(id: all_enrolled) }

Rails enum negative scope (where not)

In Rails 6, negative scopes are added on the enum values.

You can use it like this:

User.not_pending
User.not_approved
User.not_declined

Related pull request

Trying to negate a scope

It works in the console (or a controller for that matter) because you are working on arrays there.
I'm also interested in add/sub scopes from scopes.

Is it possible to invert a named scope in Rails3?

Arel has a not method you could use:

condition = arel_table[:wait_days_preliminary].lteq(::WAIT_TIME_LIMIT.to_i)
scope :within_limit, where(condition) # => "wait_days_preliminary <= x"
scope :above_limit, where(condition.not) # => "NOT(wait_days_preliminary <= x)"

named scope with where and where.not

This will work:

scope :active_without_owner, -> { where(active: true).where.not(role: 'owner')) }

I'd change it up a bit though and do this so you can reuse active:

scope :active, -> { where(active: true) }
scope :active_with_owner, -> { active.where(role: 'owner') }
scope :active_without_owner, -> { active.where.not(role: 'owner')) }

Rails Not query on entire Where clause

If you are using Rails 7, you can use invert_where. This will form the query exactly as you need

! [ (a) && (b) && (c) ]



Related Topics



Leave a reply



Submit