Reputation: 13
I'm using Mongoengine with Django.
I have an embedded field in my model. that is a list field of embedded documents.
import mongoengine
class OrderStatusLog(mongoengine.EmbeddedDocument):
status_code = mongoengine.StringField()
class Order(mongoengine.DynamicDocument):
incr_id = mongoengine.SequenceField()
status = mongoengine.ListField(mongoengine.EmbeddedDocumentField(OrderStatusLog))
Now I want to filter the result on Order
collection based on the last value in status
field.
e.g. Order.objects.filter(status__last__status_code="scode")
I guess there is no such thing __last
. I tried the approach mentioned in the docs http://docs.mongoengine.org/guide/querying.html#querying-lists
but didn't work.
I can solve this by looping over all the documents in the collection but thats not efficient, how can we write this query efficiently.
Upvotes: 1
Views: 1965
Reputation: 14704
I'm not sure MongoEngine can do that (yet). AFAIK, you'd need to use the aggregation pipeline.
In the Mongo shell, using the '$slice' and the $arrayElemAt
operators:
db.order.aggregate([{ $project: {last_status: { $arrayElemAt: [{ $slice: [ "$status", -1 ] }, 0 ]} }}, {$match: {'last_status.status_code':"scode"}} ])
And in Python:
pipeline = [
{'$project': {'last_status': { '$arrayElemAt': [{ '$slice': [ "$status", -1 ] }, 0 ]} }},
{'$match': {'last_status.status_code':'scode'}}
]
agg_cursor = Order.objects.aggregate(*pipeline)
result = [ Order.objects.get(id=order['_id']) for order in agg_cursor ]
The trick here is that objects.aggregate
provides a PyMongo
cursor, not a MongoEngine
cursor, so if you need MongoEngine
objects, you can proceed in two steps: first filter using the aggregation framework to get the ids of matched items, then get them through a MongoEngine
query.
This is what I do. From my tests, it had proven to be much more efficient than fetching everything and filtering in the python code.
If there is a simpler way, I'm interested to ear about it. Otherwise, this could be a feature request for MongoEngine. You may want to open an issue there.
Upvotes: 1