-
Type: Bug
-
Resolution: Fixed
-
Priority: Major - P3
-
Affects Version/s: None
-
Component/s: None
-
None
-
Major Change
Currently, when any_of is used with multiple arguments (which causes the arguments to be combined with $or), and the criteria object already has a top-level $or, the new arguments will be erroneously added to the existing $or instead of getting their own $or condition:
irb(main):004:0> Band.any_of({a:1}, {aa:2}).any_of({b:1}, {bb:2}) => #<Mongoid::Criteria selector: {"$or"=>[{"a"=>1}, {"aa"=>2}, {"b"=>1}, {"bb"=>2}]} options: {} class: Band embedded: false>
Expected behavior:
irb(main):001:0> Band.any_of({a:1}, {aa:2}).any_of({b:1}, {bb:2}) => #<Mongoid::Criteria selector: {"$or"=>[{"a"=>1}, {"aa"=>2}], "$and"=>[{"$or"=>[{"b"=>1}, {"bb"=>2}]}]} options: {} class: Band embedded: false>
When any_of has only one argument it works correctly:
irb(main):005:0> Band.any_of({a:1}, {aa:2}).any_of({b:1}) => #<Mongoid::Criteria selector: {"$or"=>[{"a"=>1}, {"aa"=>2}], "b"=>1} options: {} class: Band embedded: false>
When there is no existing $or, any_of also works correctly:
irb(main):006:0> Band.any_of({a:1}).any_of({b:1}, {bb:2}) => #<Mongoid::Criteria selector: {"a"=>1, "$or"=>[{"b"=>1}, {"bb"=>2}]} options: {} class: Band embedded: false>
---------------------------
Original report:
Currently, a Criteria two chained calls to the `#or` method will merge them into one big combined "OR":
Dog.or([color: :red, size: :big]).or([breed: "Chihuahua"]) #=> Dog '$or' => [color: :red, size: :big, breed: "Chihuahua"]
I guess this is related to ActiveRecord's behavior, and it makes some sense to behave this way if you read it as a sentence in English: "X .or Y". If I'm not mistaken this was introduced in Mongoid 7.0?
`#any_of` is an alias for `#or`, so this happens:
Dog.any_of([color: :red, size: :big]).any_of([breed: "Chihuahua"]) #=> Dog '$or' => [color: :red, size: :big, breed: "Chihuahua"]
This makes a lot less sense in English; I read it as "any_of X AND any_of Y", and I was surprised when the criteria were combined. I propose that we should intentionally make `#any_of` chain as an AND:
Dog.any_of([color: :red, size: :big]).any_of([breed: "Chihuahua"]) #=> Dog. '$and' => [ '$or' => [color: :red, size: :big], '$or' => [breed: "Chihuahua"] ]
We can keep the `#or` behavior the same, so users can choose the appropriate method for their preferences.
- is related to
-
MONGOID-5097 any_of with multiple conditions and :exists in the first one on a Time field attempts to evolve true|false to Time
- Closed
- links to