As far as I can tell, multiple chained invocations of Criteria#not have never worked correctly (looking as far back as 7.4). The parameterless version of not is (according to code comments and documentation) intended to apply only to the next query element in the chain, but this is not (and has never been) the case; once enabled, it applies to all subsequent query elements.
The current behavior can be demonstrated by a few examples:
- Foo.not.a is NOT a in all versions. (This is correct behavior.)
- Foo.not.a.b is NOT a AND NOT b in all versions. According to the documentation, it is intended to be NOT a AND b, but that is not the case.
- Foo.not.a.not.b is NOT a AND NOT b (in 7.4, 7.5, and 8.0), but not for the reasons people expect; not persists across all subsequent conditions, so the second not is effectively ignored. In 8.1 and later, it is `NOT a AND b`, which is definitely not correct--this was due to an attempt to make not toggle the negation state, which had unintended side effects (see
MONGOID-5780). - Foo.not.not.a is NOT a in 7.4, 7.5, and 8.0, but reduces to simply a in 8.1 and later. (Again, see
MONGOID-5780).
We could attempt to make parameterless Criteria#not work as documented (applying only to the next query element in the chain), but it is unclear whether that would actually be better. I feel like the inline not is potentially ambiguous and confusing. It should be noted that ActiveRecord does not support this syntax, perhaps for good reason.
I think we ought to deprecate and eventually remove parameterless not.
A possible replacement could be to expand the not(...) syntax so you can pass a criteria object to it, and have that criteria's selector negated and appended. E.g. Foo.not(Foo.a) would be equivalent to Foo.not.a. This would allow you to apply negation to named scopes, which is otherwise not possible.
- is related to
-
MONGOID-5889 Query negation with allow_scopes_to_unset_default_scope
-
- Closed
-