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
aggregate
function is just a shortcut to the underlying pymongo function.The documents that come back from
aggregate
may involve some$group
or 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
$project
stage to return a new type of document which only hasname
andbrating
fields.Mongoengine isn't going to be able to do what you want here so you have a couple options:
Store the
brating
field on thePost
documents. Initialise the rating at 0 when the post is created and when$total_score
or$total_votes
are 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
.objects
query 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: