2012-12-24 2 views
4

J'ai un document sous-document ressemble à:MongoDB chercher des documents avec tri par nombre

{ 
    "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") } 
    ]  
} 

Est-il possible de récupérer les documents « les plus populaires » (valeur moyenne pour les 7 derniers jours) et trier par le nombre?

+2

Oui, vous pouvez utiliser le [cadre d'agrégation] (http://docs.mongodb.org/manual/applications/aggregation/) pour effectuer des requêtes de ce type. – JohnnyHK

Répondre

8

Il existe plusieurs façons de résoudre ce problème. La solution sur laquelle je vais me concentrer utilise le cadre d'agrégation de mongodb. Tout d'abord, voici un pipeline d'agrégation qui résoudra votre problème, après il y aura une explication/décomposition de ce qui se passe dans la commande.

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

Ce pipeline a 3 commandes principales:

1) Unwind: ce divise le champ 'aime' pour qu'il y ait 1 'comme' élément par document

2) La groupe: cette regroupe le document en utilisant le champ _id, en incrémentant le champ numLikes pour chaque document trouvé. Cela provoquera numLikes à être rempli avec un nombre égal au nombre d'éléments qui étaient dans "j'aime" avant

3) Trier: Enfin, nous trions les valeurs de retour dans l'ordre croissant basé sur numLikes. Dans un test j'ai couru la sortie de cette commande est:

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

Ceci est pour les données insérées par:

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}}) 
    } 
} 

Notez que cela ne répond pas complètement à votre question, car il évite la question de choisir le date, mais il devrait vous aider à démarrer et à avancer dans la bonne direction.

Bien sûr, il existe d'autres moyens de résoudre ce problème. Une solution pourrait être de faire tout le tri et les manipulations côté client. Ceci est juste une méthode pour obtenir l'information que vous désirez. EDIT: Si vous trouvez cela un peu fastidieux, il y a un ticket pour ajouter un opérateur de taille $ à l'infrastructure d'agrégation, je vous invite à regarder et potentiellement à l'augmenter pour essayer d'accélérer l'ajout de cet opérateur si vous êtes intéressé.

7

Une meilleure solution serait de garder un champ de comptage qui enregistre le nombre de gens pour ce document. Bien que vous puissiez utiliser l'agrégation pour cela, les performances ne seront probablement pas très bonnes. Avoir un index sur le champ de compte rendra l'opération de lecture rapide, et vous pouvez employer l'opération atomique pour incrémenter le compteur en insérant de nouveaux goûts.

1

Vous pouvez utiliser cette simplifier la requête d'agrégation ci-dessus par ce qui suit à partir mongodb v3.4 partir:

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

{ "_id" : ObjectId("5864edbfa4d3847e80147698"), "count" : 4 } 

également comme @ACE a dit que vous pouvez maintenant utiliser $ taille dans une projection à la place:

db.test.aggregate([ 
    { $project: { count: { $size : "$like" } } } 
]); 

{ "_id" : ObjectId("5864edbfa4d3847e80147698"), "count" : 4 } 
Questions connexes