Negate ActiveRecord query scope

强颜欢笑 提交于 2019-12-05 02:42:46

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'
potashin

I think what you are looking for is called negating the scope, you can use where_values (or where_values_hash in Rails >= 5):

conditions = Contact.group_search('query').where_values
@contacts = Contact.where.not(conditions.reduce(:and))

For this solution to work in Rails 4.x, you should provide values in the scope as arrays:

scope :groups, -> { where(contact_type: [2308]) }

I'v also found a neat general implementation for negating the scopes, you may also find it interesting.

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

You can use it like this:

Contact.not_active
Contact.not_groups
Contact.not_group_search

Related pull request

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!