2016-03-10 6 views
1

J'ai deux champs 'company' et 'url'. Je veux trier par le nombre de fois que 'entreprise' distincte se produit et ensuite afficher trois 'url' correspondant à cette entreprise particulière. Les données sont stockées de la manière suivante:Requête Mongo pour trier par compte distinct

{ 
    "_id" : ObjectId("56c4f73664af6f7305f3670f"), 
    "title" : "Full Stack Software Developer", 
    "url" : "http://www.indeed.com/cmp/Upside-Commerce,-Inc./jobs/Full-Stack-Software-Developer-6e93e36ea5d0e57e?sjdu=QwrRXKrqZ3CNX5W-O9jEvRQls7y2xdBHzhqWkvhd5FFfs8wS9wesfMWXjNNFaUXen2pO-kyc_Qbr7-_3Gf40AvyEQT3jn6IRxIwvw9-aFy8", 
    "company" : "Upside Commerce, Inc." 
} 

La requête suivante compte le nombre de sociétés distinctes.

db.Books.aggregate({$group : { _id : '$company', count : {$sum : 1}}}) 

Voici le résultat:

{ "_id" : "Microsoft", "count" : 14 } 
{ "_id" : "Tableau", "count" : 64 } 
{ "_id" : "Amazon", "count" : 64 } 
{ "_id" : "Dropbox", "count" : 64 } 
{ "_id" : "Amazon Corporate LLC", "count" : 64 } 
{ "_id" : "Electronic Arts", "count" : 64 } 
{ "_id" : "CDK Global", "count" : 65 } 
{ "_id" : "IDC Technologies", "count" : 64 } 
{ "_id" : "Concur", "count" : 64 } 
{ "_id" : "Microsoft", "count" : 14 } 
{ "_id" : "Tableau", "count" : 64 } 
{ "_id" : "Amazon", "count" : 64 } 
{ "_id" : "Dropbox", "count" : 64 } 
{ "_id" : "Amazon Corporate LLC", "count" : 64 } 
{ "_id" : "Electronic Arts", "count" : 64 } 
{ "_id" : "CDK Global", "count" : 65 } 
{ "_id" : "IDC Technologies", "count" : 64 } 
{ "_id" : "Concur", "count" : 64 } 

Cependant, je le veux trier par nombre de sociétés distinctes (limiter au Top 10 les plus élevées des entreprises qui se produisent), puis afficher trois urls correspondant à la société distincte (si compte pour la compagnie distincte est au moins trois). Quelque chose comme:

{for microsoft: 
    {"url" : "https://careers.microsoft.com/jobdetails.aspx?jid=216571&memid=1071484607&utm_source=Indeed"} 
    {"url" : "https://careers.microsoft.com/jobdetails.aspx?jid=216571&memid=1695844082&utm_source=Indeed" } 
    { "url" : "https://careers.microsoft.com/jobdetails.aspx?jid=216571&memid=932148152&utm_source=Indeed"}} 

va de même pour d'autres entreprises

Répondre

1

C'est vraiment (encore) mieux traitées par plusieurs requêtes, depuis MongoDB vraiment « encore » ne pas les opérateurs vraiment efficaces pour le faire encore .

Vous pouvez faire quelque chose comme ça avec MongoDB 3.2 mais, mais il y a des évidentes « prises »:

db.Books.aggregate([ 
    { "$group": { 
     "_id": "$company", 
     "count": { "$sum": 1 }, 
     "urls": { 
      "$push": "$url" 
     } 
    }}, 
    { "$sort": { "count": -1 } }, 
    { "$limit": 10 }, 
    { "$project": { 
     "count": 1, 
     "urls": { "$slice": ["$urls",0, 3] } 
    }} 
]) 

Et le problème évident est que peu importe ce que, vous ajoutez encore tous du " url "contenu dans le tableau groupé. Cela a le potentiel de dépasser la limite BSON de 16 Mo. Ce n'est peut-être pas le cas, mais c'est quand même un peu inutile d'ajouter du contenu "tout" quand on veut seulement "trois" d'entre eux.

Ainsi, même dans ce cas, il est probablement plus pratique d'interroger les "URL" séparément sur chacun des 10 premiers résultats.

Voici une liste de Node.js qui démontre:

var async = require('async'), 
    mongodb = require('mongodb'), 
    MongoClient = mongodb.MongoClient; 

MongoClient.connect("mongodb://localhost/test",function(err,db) { 

    if (err) throw err; 

    // Get the top 10 
    db.collection("Books").aggregate(
     [ 
      { "$group": { 
       "_id": "$company", 
       "count": { "$sum": 1 } 
      }}, 
      { "$sort": { "count": -1 } }, 
      { "$limit": 10 } 
     ],function(err,results) { 
      if (err) throw err; 

      // Query for each result and map query response as urls 
      async.map(
       results, 
       function(result,callback) { 
        db.collection("Books").find({ 
         "company": result.company 
        }).limit(3).toArray(function(err,items) { 
         result.urls = items.map(function(item) { 
          return item.url; 
         }); 
         callback(err,result); 
        }) 
       }, 
       function(err,results) { 
        if (err) throw err; 
        // each result entry has 3 urls 
       } 
      ); 
     } 
    ) 

}); 

Oui, il est plus d'appels à la base de données, mais il est vraiment seulement dix et donc pas vraiment un problème.

La résolution réelle est couverte en SERVER-9377 - Extend $push or $max to allow collecting "top" N values per _id key in $group phase. Cela a le statut prometteur "En cours", donc il est activement travaillé. Une fois cela résolu, alors une seule déclaration d'agrégation devient viable, car alors vous seriez en mesure de "limiter" les "urls" résultantes dans le $push initial à seulement trois entrées, plutôt que de supprimer toutes les trois, après le fait .

+0

Merci beaucoup. Appréciez-le. Très bien expliqué. – dsl1990