MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Mongo-Abfrage zum Sortieren nach eindeutiger Anzahl

Dies wird wirklich (noch) am besten durch mehrere Abfragen gehandhabt, da MongoDB wirklich "noch" nicht über die wirklich effizienten Operatoren verfügt, um dies zu tun.

Sie können so etwas zwar mit MongoDB 3.2 machen, aber es gibt offensichtliche „Fänge“:

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

Und das offensichtliche Problem ist, dass Sie, egal was passiert, immer noch alle hinzufügen des "url"-Inhalts in das gruppierte Array. Dies kann das BSON-Limit von 16 MB überschreiten. Vielleicht nicht, aber es ist immer noch etwas verschwenderisch, "alle" Inhalte hinzuzufügen, wenn Sie nur "drei" davon möchten.

Selbst dann ist es wahrscheinlich praktischer, die "URLs" für jedes der Top-10-Ergebnisse separat abzufragen.

Hier ist eine Auflistung für node.js, die Folgendes demonstriert:

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

});

Ja, es sind mehr Aufrufe an die Datenbank, aber es sind wirklich nur zehn und daher eigentlich kein Thema.

Das Echte Die Lösung dafür wird in SERVER-9377 - Erweitern Sie $push oder $max, um das Sammeln von "top " N Werte pro _id-Schlüssel in der $group-Phase . Diese hat den vielversprechenden Status „In Bearbeitung“, es wird also aktiv daran gearbeitet.

Sobald dies gelöst ist, wird eine einzelne Aggregationsanweisung praktikabel, da Sie dann in der Lage wären, die resultierenden "URLs" im anfänglichen $push zu "begrenzen". auf nur drei Einträge, anstatt alle bis auf drei nachträglich zu entfernen.