sql >> Base de Datos >  >> NoSQL >> MongoDB

Consulta de Mongo para ordenar por recuento distinto

Esto realmente (todavía) es mejor manejado por múltiples consultas, ya que MongoDB realmente "todavía" no tiene los operadores realmente eficientes para hacer esto todavía.

Sin embargo, puede hacer algo como esto con MongoDB 3.2, pero hay "trampas" obvias:

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

Y el problema obvio es que pase lo que pase, sigues agregando todos del contenido de la "url" en la matriz agrupada. Esto tiene el potencial de superar el límite de BSON de 16 MB. Puede que no, pero sigue siendo un desperdicio agregar "todo" el contenido cuando solo quieres "tres".

Entonces, incluso entonces, probablemente sea más práctico consultar las "URL" por separado en cada uno de los 10 resultados principales.

Aquí hay una lista de node.js que demuestra:

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í, son más llamadas a la base de datos, pero en realidad son solo diez y por lo tanto no es realmente un problema.

El verdadero la resolución para esto está cubierta en SERVER-9377 - Extend $push o $max para permitir la recopilación "top "N valores por clave _id en $fase de grupo . Esto tiene el prometedor estado "En progreso", por lo que se está trabajando activamente.

Una vez que se resuelva, entonces una declaración de agregación única se vuelve viable, ya que entonces podría "limitar" las "URL" resultantes en el $push inicial a solo tres entradas, en lugar de eliminar todas menos tres después del hecho.