Reputation: 538
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'>
Upvotes: 1
Views: 913
Reputation: 2925
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 has name
and brating
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 the Post
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']
or post['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:
posts = Post.objects(types=type).only("name", "total_score", "total_votes")
top_posts = sorted(list(posts),key=lambda p: (p.total_score+60)/(p.total_votes+20))[:100]
Upvotes: 1