Il framework di aggregazione in MongoDB 3.4 e versioni successive offre il $reduce
operatore che calcola in modo efficiente il totale senza la necessità di tubazioni aggiuntive. Prendi in considerazione l'utilizzo come espressione per restituire le valutazioni totali e ottenere il numero di valutazioni utilizzando $size
. Insieme a $addFields
, la media può quindi essere calcolata utilizzando l'operatore aritmetico $divide
come nella formula 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
]
}
]
}
}
}
])
Risultato campione
{
"_id" : ObjectId("58ab48556da32ab5198623f4"),
"title" : "The Hobbit",
"ratings" : [
{
"title" : "best book ever",
"rating" : 5.0
},
{
"title" : "good book",
"rating" : 3.5
}
],
"rating_average" : 4.25
}
Con le versioni precedenti, dovresti prima applicare il $unwind
operatore sui ratings
campo array prima come passaggio iniziale della pipeline di aggregazione. Questo decostruirà le ratings
campo matrice dai documenti di input per produrre un documento per ogni elemento. Ogni documento di output sostituisce l'array con un valore di elemento.
La seconda fase della pipeline sarebbe il $group
operatore che raggruppa i documenti di input in base a _id
e title
chiavi dell'espressione dell'identificatore e applica il $avg
desiderato accumulatore per ciascun gruppo che calcola la media. C'è un altro operatore di accumulatori $push
che preserva il campo dell'array delle valutazioni originali restituendo un array di tutti i valori risultanti dall'applicazione di un'espressione a ciascun documento nel gruppo precedente.
Il passaggio finale della pipeline è il $project
operatore che quindi rimodella ogni documento nello stream, ad esempio aggiungendo il nuovo campo ratings_average
.
Quindi, se ad esempio hai un documento campione nella tua collezione (come da sopra e così sotto):
db.collection.insert({
"title": "The Hobbit",
"ratings": [
{
"title": "best book ever",
"rating": 5
},
{
"title": "good book",
"rating": 3.5
}
]
})
Per calcolare la media dell'array di valutazioni e proiettare il valore in un altro campo ratings_average
, puoi quindi applicare la seguente pipeline di aggregazione:
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
}
}
])
Risultato :
/* 1 */
{
"result" : [
{
"ratings" : [
{
"title" : "best book ever",
"rating" : 5
},
{
"title" : "good book",
"rating" : 3.5
}
],
"ratings_average" : 4.25,
"title" : "The Hobbit"
}
],
"ok" : 1
}