MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Consulta Mongo para classificar por contagem distinta


Isso realmente é (ainda) melhor tratado por várias consultas, já que o MongoDB realmente "ainda" não possui os operadores realmente eficientes para fazer isso ainda.

Você pode fazer algo assim com o MongoDB 3.2, mas existem "capturas" óbvias:
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 o problema óbvio é que, não importa o que aconteça, você ainda adiciona todos do conteúdo "url" na matriz agrupada. Isso tem o potencial de exceder o limite BSON de 16 MB. Pode não, mas ainda é um desperdício adicionar "todos" o conteúdo quando você quer apenas "três" deles.

Então, mesmo assim, provavelmente é mais prático consultar os "urls" separadamente em cada um dos 10 principais resultados.

Aqui está uma listagem de node.js que demonstra:
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
                }
            );
        }
     )

});

Sim, são mais chamadas para o banco de dados, mas na verdade são apenas dez e, portanto, não é realmente um problema.

O verdadeiro A resolução para isso é abordada em SERVER-9377 - Estender $push ou $max para permitir a coleta de "top " N valores por chave _id na fase $group . Isso tem o status promissor de "Em andamento", por isso está sendo trabalhado ativamente.

Uma vez que isso seja resolvido, uma única instrução de agregação se torna viável, pois você poderá "limitar" as "urls" resultantes no $push inicial a apenas três entradas, em vez de remover todas, exceto três, após o fato.