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

MongoDB aggregierte Summe jedes Schlüssels in einem Unterdokument

Wie bereits erwähnt, ist die Verarbeitung solcher Dokumente mit dem Aggregations-Framework nicht möglich, es sei denn, Sie geben tatsächlich alle Schlüssel an, wie z. B.:

db.events.aggregate([
   { "$group": {
       "_id": "$app_id",
       "event_count": { "$sum": "$event_count" },
       "0": { "$sum": "$event_count_per_type.0" },
       "10": { "$sum": "$event_count_per_type.10" }
       "20": { "$sum": "$event_count_per_type.20" }
       "30": { "$sum": "$event_count_per_type.30" }
   }}
])

Aber Sie müssen natürlich jeder explizit angeben Schlüssel, an dem Sie arbeiten möchten. Dies gilt sowohl für das Aggregations-Framework als auch für allgemeine Abfrageoperationen in MongoDB, da Sie für den Zugriff auf Elemente, die in diesem „Unterdokument“-Formular notiert sind, den „genauen Pfad“ zu dem Element angeben müssen, um irgendetwas damit zu tun.

Das Aggregations-Framework und allgemeine Abfragen haben kein „Traversal“-Konzept, was bedeutet, dass sie nicht „jeden Schlüssel“ eines Dokuments verarbeiten können. Dazu ist ein Sprachkonstrukt erforderlich, das in diesen Schnittstellen nicht bereitgestellt wird.

Im Allgemeinen ist die Verwendung eines "Schlüsselnamens" als Datenpunkt, dessen Name tatsächlich einen "Wert" darstellt, ein bisschen wie ein "Anti-Muster". Ein besserer Weg, dies zu modellieren, wäre die Verwendung eines Arrays und die Darstellung Ihres "Typs" als eigenen Wert:

{
    "app_id": "DHJFK67JDSJjdasj909",
    "date: ISODate("2014-08-07T00:00:00.000Z"),
    "event_count": 32423,
    "events": [
        { "type": 0,  "value": 322  },
        { "type": 10, "value": 4234 },
        { "type": 20, "value": 653  },
        { "type": 30, "value": 7562 }
    ]
}

Beachten Sie auch, dass das "Datum" jetzt ein richtiges Datumsobjekt und keine Zeichenfolge ist, was auch eine gute Übung ist. Diese Art von Daten ist jedoch mit dem Aggregationsframework einfach zu verarbeiten:

db.events.aggregate([
    { "$unwind": "$events" },
    { "$group": {
        "_id": { 
            "app_id": "$app_id",
            "type": "$events.type"
        },
        "event_count": { "$sum": "$event_count" },
        "value": { "$sum": "$value" }
    }},
    { "$group": {
        "_id": "$_id.app_id",
        "event_count": { "$sum": "$event_count" },
        "events": { "$push": { "type": "$_id.type", "value": "$value" } }
    }}
]) 

Das zeigt eine zweistufige Gruppierung, die zuerst die Summen pro "Typ" erhält, ohne jeden "Schlüssel" anzugeben, da Sie dies nicht mehr tun müssen, und dann als einzelnes Dokument pro "app_id" mit den Ergebnissen in einem Array zurückgibt, wie sie ursprünglich gespeichert wurden. Diese Datenform ist im Allgemeinen viel flexibler, um bestimmte "Typen" oder sogar die "Werte" innerhalb eines bestimmten Bereichs zu betrachten.

Wo Sie die Struktur nicht ändern können, ist mapReduce Ihre einzige Option. Dadurch können Sie das Durchlaufen der Schlüssel "kodieren", aber da dies eine JavaScript-Interpretation und -Ausführung erfordert, ist es nicht so schnell wie das Aggregations-Framework:

db.events.mapReduce(
    function() {
        emit(
            this.app_id,
            {
                "event_count": this.event_count,
                "event_count_per_type": this.event_count_per_type
            }
        );
    },
    function(key,values) {

        var reduced = { "event_count": 0, "event_count_per_type": {} };

        values.forEach(function(value) {
            for ( var k in value.event_count_per_type ) {
                if ( !redcuced.event_count_per_type.hasOwnProperty(k) )
                    reduced.event_count_per_type[k] = 0;
                reduced.event_count_per_type += value.event_count_per_type;
            }
            reduced.event_count += value.event_count;
        })
    },
    {
        "out": { "inline": 1 }
    }
)

Das wird im Wesentlichen die "Schlüssel" durchlaufen und kombinieren und die Werte für jeden gefundenen aufsummieren.

Sie haben also entweder folgende Möglichkeiten:

  1. Ändern Sie die Struktur und arbeiten Sie mit Standardabfragen und Aggregation.
  2. Bleiben Sie bei der Struktur und erfordern Sie JavaScript-Verarbeitung und mapReduce.

Es hängt von Ihren tatsächlichen Bedürfnissen ab, aber in den meisten Fällen bringt eine Umstrukturierung Vorteile.