In Mongo, how do I only display documents with the

2019-05-29 07:25发布

问题:

Say I have the following four documents in a collection called "Store":

{ item: 'chair', modelNum: 1154, votes: 75 }
{ item: 'chair', modelNum: 1152, votes: 16 }
{ item: 'table', modelNum: 1017, votes: 24 }
{ item: 'table', modelNum: 1097, votes: 52 }

I would like to find only the documents with the highest number of votes for each item type. The result of this simple example would return modelNum: 1154 and modelNum: 1097. Showing me the most popular model of chair and table, based on the customer inputed vote score.

What is the best way write this query and sort them by vote in descending order? I'm developing using meteor, but I don't think that should have an impact.

Store.find({????}).sort({votes: -1});

回答1:

You can use $first or $last aggregation operators to achieve what you want. These operators are only useful when $group follows $sort. An example using $first:

db.collection.aggregate([
    // Sort by "item" ASC, "votes" DESC
    {"$sort" : {item : 1, votes : -1}}, 
    // Group by "item" and pick the first "modelNum" (which will have the highest votes)
    {"$group" : {_id : "$item", modelNum : {"$first" : "$modelNum"}}}
])

Here's the output:

{
        "result" : [
                {
                        "_id" : "table",
                        "modelNum" : 1097
                },
                {
                        "_id" : "chair",
                        "modelNum" : 1154
                }
        ],
        "ok" : 1
}


回答2:

If you are looking to do this in Meteor and on the client I would just use an each loop and basic find. Minimongo keeps the data in memory so I don't think additional find calls are expensive.

like this:

 Template.itemsList.helpers({
   items: function(){
     var itemNames = Store.find({}, {fields: {item: 1}}).map( 
       function( item ) { return item.item; }
     );
     var itemsMostVotes = _.uniq( itemNames ).map( 
       function( item ) {
         return Store.findOne({item: item}, {sort: {votes: -1}});
       }
     );
     return itemsMostVotes;
   }
 });

I have switched to findOne so this returns an array of objects rather than a cursor as find would. If you really want the cursor then you could query minimongo with the _ids from itemMostVotes.

You could also use the underscore groupBy and sortBy functions to do this.



回答3:

You would need to use the aggregation framework.

So

db.Store.aggregate(
    {$group:{_id:"$item", "maxVotes": {$max:"$votes"}}}
);