I have a requirement where I need to run a MongoDB query like the following:
db.collection.find({ $or : [{"field1" : "value1"}, {"field2" : "value2"}], $or : [{"field3" : "value3"}, {"field4" : "value4"}]})
i.e.
(field1 == value 1 or field2 == value2) and (field3 == value3 or field4 == value4)
I want to achieve this through criteria chaining because the query gets formed dynamically from different parts of the code. But if I try doing something like the following
criteria = Collection.any_of({"field1" => "value1"}, {"field2" => "value2"}) criteria = criteria.any_of({"field3" => "value3"}, {"f开发者_运维技巧ield4" => "value4"})
I get the resultant query where all these are combined into a single $or statement like
db.collection.find({ $or : [{"field1" : "value1"}, {"field2" : "value2"}, {"field3" : "value3"}, {"field4" : "value4"}]})
What is the way to achieve "and" of the two "any_of" using criteria chaining?
You can do it with avoid any_of.
criteria = Collection.where('$or' => [{"field1" => "value1"}, {"field2" => "value2"}])
criteria = criteria.where('$or' => [{"field3" => "value3"}, {"field4" => "value4"}])
you can write this with mongoid 2.4.0:
Collection.all_of({"$or" => [{"field1" => "value1"}, {"field2" => "value2"}]}, {"$or" => [{"field3" => "value3"}, {"field4" => "value4"}]})
For Mongoid 3 and 4 there is a relatively nice way of chaining or criteria without merging.
TL;DR
MyModel.all_of(MyModel.xxx.selector, MyModel.yyy.selector)
Mongoid maintainer Durran showed the above trick in this Github issue: https://github.com/mongoid/mongoid/issues/2170
You can arbitrarily chain criteria using the same technique and some array trickery like this:
selectors = []
selectors << MyModel.search(term) if term
selectors << MyModel.some_any_of if some_condition
selectors << MyModel.some_other_any_of if some_other_condition
...
MyMode..all_of(*selectors)
Each row will add another and condition.
It may be worth noting too that you do not have to build each selector from the model. You can get an initial scope back from your permission system or something ans just call .selector on it before adding on more criteria.
It took me forever to get this right, but this is what worked for me:
scope :for_user, lambda {|user|
any_of(
{recipient_id: Moped::BSON::ObjectId.from_string(user.id)},
{sender_id: Moped::BSON::ObjectId.from_string(user.id)},
{sender_email: user.email},
{recipient_email: user.email}
)
}
You have to make sure to wrap the individual criteria for your $or
condition in {}
so that it nests the OR properly. (Using Mongoid3/Moped)
This is still the case. I was able to work around this only by using the Ruby Mongo Driver API directly.
精彩评论