Reputation: 33
Suppose we need to write a range query in Mongoid. Let the field to be queried be range_field
, then we do something like this
where(:range_field.lte => some-date-time, :range_field.gte => some-date-time)
But if i want to run a query to choose any of multiple ranges, i'd have to do
.or({:range_field.lte => some-date-time1, :range_field.gte => some-date-time2},{:range_field.lte => some-date-time3, :range_field.gte => some-date-time4})
This apparently doesn't work. How can I run such queries with Mongoid?
Upvotes: 1
Views: 751
Reputation: 434585
When you say:
:range_field.lte => some_date_time
you're calling a method, lte
, that Mongoid monkey patches in Symbol
. That method returns an Origin::Key
instance that is wrapped around the underlying $lte
operator. Somewhere inside Mongoid that Origin::Key
will be converted to something that MongoDB will understand:
{ range_field: { $lte: some_date_time } }
If you look at what
where(:range_field.lte => t1, :range_field.gte => t2)
becomes by calling selector
on the result, you'll see something like this:
{
"created_at" => {
:$gte => t2,
:$lte => t1
}
}
and everything will work fine.
However, if we use #or
and call selector
to see the underlying query, we see that Mongoid is expanding the Origin::Key
s one by one and merging the results:
or({:range_field.lte => t1, :range_field.gte => t2})
# is expanded as though as you said
or({ :range_field => { :$lte => t1 }, :range_field => { :$gte => t2 } })
# which is the same as
or({ :range_field => { :$gte => t2 } })
Essentially, Mongoid is being inconsistent as to how it expands the Origin::Key
s. You'll even get the same confusing result if you use :$or
instead of #or
:
where(:$or => [ {:range_field.lte => t1, :range_field.gte => t2} ]).selector
will say:
{ "$or" => [ { "range_field" => { "$gte" => t2 } } ] }
The solution is to not use the Symbol
monkey patched methods and do that part by hand:
or(
{ :range_field => { :$lte => t1, :$gte => t2 } },
{ :range_field => { :$lte => t3, :$gte => t4 } },
...
)
Upvotes: 1