TL;DR
Collection.with_missing_coins.invert_where
More details
Rails 7 introduced invert_where
It allows you to invert an entire where clause instead of manually applying conditions
class User < ApplicationRecord
scope :active, -> { where(accepted: true, locked: false) }
end
User.where(accepted: true)
# SELECT * FROM users WHERE accepted = TRUE
User.where(accepted: true).invert_where
# SELECT * FROM users WHERE accepted != TRUE
User.active
# SELECT * FROM users WHERE accepted = TRUE AND locked = FALSE
User.active.invert_where
# SELECT * FROM users WHERE NOT (accepted = TRUE AND locked = FALSE)
Be careful, compare these variants
User.where(role: "admin").active.invert_where
# SELECT * FROM users WHERE NOT (role = 'admin' AND accepted = TRUE AND locked = FALSE)
User.active.invert_where.where(role: "admin")
# SELECT * FROM users WHERE NOT (accepted = TRUE AND locked = FALSE) AND role = 'admin'
And look at this
User.where(accepted: true).invert_where.where(locked: false).invert_where
# SELECT * FROM users WHERE NOT (accepted != TRUE AND locked = FALSE)
invert_where
inverts all where conditions before it. Therefore it's better to avoid using this method in scopes because it could lead to unexpected results
Much better to use this method explicitly and only in the beginning of where chain immediately after it is necessary to invert all the previous conditions