Il aggregation framework in MongoDB 3.4 e successivi offre all'operatore $reduce
che calcola in modo efficiente il totale senza la necessità di tubazioni in più. Considera di usarlo come espressione per restituire il punteggio totale e ottenere il numero di valutazioni usando $size
. Insieme a $addFields
, la media può quindi essere calcolato 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
]
}
]
}
}
}
])
Esempio di output
{
"_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, sarà necessario prima applicare l'operatore $unwind
sul ratings
campo array prima come passaggio iniziale della pipeline di aggregazione. Questo decostruirà il campo dell'array ratings
dai documenti di input per produrre un documento per ciascun elemento. Ogni documento di output sostituisce l'array con un valore di elemento.
Il secondo stadio della pipeline sarebbe l'operatore $group
quali documenti gruppi di ingresso dall'espressione identificatore _id
e title
chiavi e applica l'espressione $avg
accumulatore desiderato per ogni gruppo che calcola la media. Esiste un altro operatore di accumulatori $push
che conserva il campo dell'array di classificazione originale restituendo un array di tutti i valori che risultano dall'applicazione di un'espressione a ciascun documento nel gruppo precedente.
Il passaggio della pipeline finale è l'operatore $project
che quindi rimodella ogni documento nello stream, ad esempio aggiungendo il nuovo campo ratings_average
.
Quindi, se per esempio si dispone di un documento di esempio nella vostra collezione (come dall'alto e così di seguito):
db.collection.insert({
"title": "The Hobbit",
"ratings": [
{
"title": "best book ever",
"rating": 5
},
{
"title": "good book",
"rating": 3.5
}
]
})
per calcolare la media feedback array e proiettando il valore in un altro campo ratings_average
, è possibile quindi applicare la seguente gasdotto 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
}
Grazie, chridam :) – retrobitguy
@retrobitguy Nessun problema :-) – chridam
Questo è stato molto utile e chiaro! Grazie molto! – retrobitguy