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

Berechnen Sie den Durchschnitt der Felder in eingebetteten Dokumenten/Arrays

Das Aggregationsframework in MongoDB 3.4 und höher bietet den $reduce Betreiber, der die Gesamtsumme effizient berechnet, ohne dass zusätzliche Pipelines erforderlich sind. Erwägen Sie die Verwendung als Ausdruck, um die Gesamtbewertungen zurückzugeben und die Anzahl der Bewertungen mit $size abzurufen . Zusammen mit $addFields , der Durchschnitt kann also mit dem arithmetischen Operator $divide berechnet werden wie in der Formel average = total ratings/number of ratings :

db.collection.aggregate([
    { 
        "$addFields": { 
            "rating_average": {
                "$divide": [
                    { // expression returns total
                        "$reduce": {
                            "input": "$ratings",
                            "initialValue": 0,
                            "in": { "$add": ["$$value", "$$this.rating"] }
                        }
                    },
                    { // expression returns ratings count
                        "$cond": [
                            { "$ne": [ { "$size": "$ratings" }, 0 ] },
                            { "$size": "$ratings" }, 
                            1
                        ]
                    }
                ]
            }
        }
    }           
])

Beispielausgabe

{
    "_id" : ObjectId("58ab48556da32ab5198623f4"),
    "title" : "The Hobbit",
    "ratings" : [ 
        {
            "title" : "best book ever",
            "rating" : 5.0
        }, 
        {
            "title" : "good book",
            "rating" : 3.5
        }
    ],
    "rating_average" : 4.25
}

Bei älteren Versionen müssten Sie zuerst $unwind anwenden Operator auf die ratings Array-Feld zuerst als ersten Aggregations-Pipeline-Schritt. Dadurch werden die ratings dekonstruiert Array-Feld aus den Eingabedokumenten, um für jedes Element ein Dokument auszugeben. Jedes Ausgabedokument ersetzt das Array durch einen Elementwert.

Die zweite Pipelinestufe wäre die $group Operator, der Eingabedokumente nach _id gruppiert und title Schlüsselidentifikationsausdruck und wendet den gewünschten $avg an Akkumulatorausdruck für jede Gruppe, die den Durchschnitt berechnet. Es gibt einen weiteren Akkumulator-Operator $push das das ursprüngliche Bewertungsarrayfeld beibehält, indem es ein Array aller Werte zurückgibt, die sich aus der Anwendung eines Ausdrucks auf jedes Dokument in der obigen Gruppe ergeben.

Der letzte Pipelineschritt ist das $project -Operator, der dann jedes Dokument im Stream umgestaltet, z. B. durch Hinzufügen des neuen Felds ratings_average .

Wenn Sie also beispielsweise ein Beispieldokument in Ihrer Sammlung haben (wie oben und so unten):

db.collection.insert({
    "title": "The Hobbit",

    "ratings": [
        {
            "title": "best book ever",
            "rating": 5
        },
        {
            "title": "good book",
            "rating": 3.5
        }
    ]
})

Um den Durchschnitt des Bewertungsarrays zu berechnen und den Wert in ein anderes Feld zu projizieren ratings_average , können Sie dann die folgende Aggregationspipeline anwenden:

db.collection.aggregate([
    {
        "$unwind": "$ratings"
    },
    {
        "$group": {
            "_id": {
                "_id": "$_id",
                "title": "$title"
            },
            "ratings":{
                "$push": "$ratings"
            },
            "ratings_average": {
                "$avg": "$ratings.rating"
            }
        }
    },
    {
        "$project": {
            "_id": 0,
            "title": "$_id.title",
            "ratings_average": 1,
            "ratings": 1
        }
    }
])

Ergebnis :

/* 1 */
{
    "result" : [ 
        {
            "ratings" : [ 
                {
                    "title" : "best book ever",
                    "rating" : 5
                }, 
                {
                    "title" : "good book",
                    "rating" : 3.5
                }
            ],
            "ratings_average" : 4.25,
            "title" : "The Hobbit"
        }
    ],
    "ok" : 1
}