MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Query Mongo per ordinare per conteggio distinto

Questo è davvero (ancora) meglio gestito da più query, dal momento che MongoDB davvero "ancora" non ha ancora gli operatori davvero efficienti per farlo.

Puoi fare qualcosa del genere con MongoDB 3.2, ma ci sono ovvi "casi":

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

E il problema ovvio è che, in ogni caso, stai ancora aggiungendo tutto del contenuto "url" nell'array raggruppato. Questo ha il potenziale per superare il limite BSON di 16 MB. Potrebbe non essere così, ma è comunque un po' dispendioso aggiungere "tutti" i contenuti quando ne vuoi solo "tre".

Quindi anche allora è probabilmente più pratico interrogare solo gli "url" separatamente su ciascuno dei primi 10 risultati.

Ecco un elenco per node.js che dimostra:

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

});

Sì, sono più chiamate al database, ma in realtà sono solo dieci e quindi non è un vero problema.

Il reale la risoluzione per questo è trattata in SERVER-9377 - Estendi $push o $max per consentire la raccolta "top " N valori per _id chiave nella $fase di gruppo . Questo ha il promettente stato "In corso", quindi si sta lavorando attivamente.

Una volta risolto, diventa possibile una singola istruzione di aggregazione, da allora potresti "limitare" gli "url" risultanti nel $push iniziale a sole tre voci, invece di rimuoverle tutte tranne tre dopo il fatto.