When use aggregate with mongoengine, it return a CommandCursor instead of mongoengine object list, which mean that the mongonengine is not really be used,
For example: if some document doesn't has a title field, a error will be raised. How can I convert my results to mongoengine object?
class Post(Document):
title = StringField(max_length=120, required=True)
author = ReferenceField(User)
Host.objects()
# [<Post: Post object>, <Post: Post object>, ...]
pipeline = [
{
"$match": {
'types': type,
}
},
{
"$project": {
"name": 1,
'brating': {
"$divide": [
{"$add": ["$total_score", 60]},
{"$add": ["$total_votes", 20]}
]
}
}
},
{"$sort": {"brating": -1}},
{"$limit": 100}
]
Host.objects.aggregate(*pipeline)
# <class 'pymongo.command_cursor.CommandCursor'>
list(Host.objects.aggregate(*pipeline))
# <class 'list'>
The
aggregatefunction is just a shortcut to the underlying pymongo function.The documents that come back from
aggregatemay involve some$groupor other stage that means they bear no relation to your object model so mongoengine couldn't convert them to mongoengine objects.In the case of your pipeline you are using a
$projectstage to return a new type of document which only hasnameandbratingfields.Mongoengine isn't going to be able to do what you want here so you have a couple options:
Store the
bratingfield on thePostdocuments. Initialise the rating at 0 when the post is created and when$total_scoreor$total_votesare updated, also update the rating.Accept that you are getting back non-mongoengine objects and handle them accordingly. The cursor will yield normal python dictionaries which you can then access the fields
post['name']orpost['brating']in your client code.Use a normal
.objectsquery and sort on the client side.The final step will obliviously be a problem if you have lots of documents but for a small number try something like: