MongoDB retrieves documents sorted by invoice

I have a document with a sub-document that looks something like this:

{ "name" : "some name1" "like" : [ { "date" : ISODate("2012-11-30T19:00:00Z") }, { "date" : ISODate("2012-12-02T19:00:00Z") }, { "date" : ISODate("2012-12-01T19:00:00Z") }, { "date" : ISODate("2012-12-03T19:00:00Z") } ] } 

Is it possible to get the documents “most liked” (average value for the last 7 days) and sort by invoice?

+4
source share
3 answers

There are several ways to solve this problem. The solution I will focus on is using the mongodb aggregation structure. Firstly, here is the aggregation pipeline that will solve your problem, after which there will be an explanation / breakdown of what is happening in the team.

 db.testagg.aggregate( { $unwind : '$likes' }, { $group : { _id : '$_id', numlikes : { $sum : 1 }}}, { $sort : { 'numlikes' : 1}}) 

This pipeline has three main commands:

1) Defrost: this divides the “likes” field, so there is one element “per document”

2) Group: this groups the document using the _id field, increasing the number of numLikes for each document found. This will cause numLikes to be filled with a number equal to the number of items that were “liked” before

3) Sort: Finally, we sort the return values ​​in ascending order based on numLikes. In the test, I executed the output of this command:

 {"result" : [ { "_id" : 1, "numlikes" : 1 }, { "_id" : 2, "numlikes" : 2 }, { "_id" : 3, "numlikes" : 3 }, { "_id" : 4, "numlikes" : 4 }.... 

This is for data inserted through:

 for (var i=0; i < 100; i++) { db.testagg.insert({_id : i}) for (var j=0; j < i; j++) { db.testagg.update({_id : i}, {'$push' : {'likes' : j}}) } } 

Please note that this does not fully answer your question, as this avoids the question of choosing a date range, but it will hopefully make you start and move in the right direction.

Of course, there are other ways to solve this problem. One solution could simply be to do all the sorting and manipulation on the client side. This is just one way to get the information you want.

EDIT: if you find this somewhat tedious, there is a ticket to add a size operator to the aggregation structure, I invite you to look and potentially increase it to try to speed up the addition of this new operator if you are interested.

https://jira.mongodb.org/browse/SERVER-4899

+8
source

A better solution would be to keep the count field, which will record how many people like this document. Although you can use aggregation to do this, performance is likely to be not very good. Having an index in the count field will make the read operation quick, and you can use the atomic operation to increase the counter when inserting new ones you like.

+7
source

You can use this to simplify the aggregation request above as follows: mongodb v3.4 onwards:

 > db.test.aggregate([ { $unwind: "$like" }, { $sortByCount: "$_id" } ]).pretty() { "_id" : ObjectId("5864edbfa4d3847e80147698"), "count" : 4 } 

Just like @ACE said you can now use $ size within projection:

 db.test.aggregate([ { $project: { count: { $size : "$like" } } } ]); { "_id" : ObjectId("5864edbfa4d3847e80147698"), "count" : 4 } 
+1
source

All Articles