Agregační rámec v MongoDB 3.4 a novějších nabízí $reduce
operátor, který efektivně vypočítá součet bez potřeby dalších potrubí. Zvažte jeho použití jako výraz pro vrácení celkového hodnocení a získání počtu hodnocení pomocí $size
. Společně s $addFields
, průměr lze tedy vypočítat pomocí aritmetického operátoru $divide
jako ve vzorci 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
]
}
]
}
}
}
])
Ukázkový výstup
{
"_id" : ObjectId("58ab48556da32ab5198623f4"),
"title" : "The Hobbit",
"ratings" : [
{
"title" : "best book ever",
"rating" : 5.0
},
{
"title" : "good book",
"rating" : 3.5
}
],
"rating_average" : 4.25
}
U starších verzí byste museli nejprve použít $unwind
operátor na ratings
pole pole jako první krok agregačního kanálu. Tím se dekonstruují ratings
pole ze vstupních dokumentů pro výstup dokumentu pro každý prvek. Každý výstupní dokument nahradí pole hodnotou prvku.
Druhá fáze kanálu by byla $group
operátor, který seskupuje vstupní dokumenty podle _id
a title
klíčový výraz identifikátoru a použije požadovaný $avg
akumulátorový výraz ke každé skupině, která počítá průměr. Existuje další operátor akumulátoru $push
který zachová původní pole pole hodnocení tím, že vrátí pole všech hodnot, které jsou výsledkem použití výrazu na každý dokument ve výše uvedené skupině.
Posledním krokem v kanálu je $project
operátor, který poté změní tvar každého dokumentu ve streamu, například přidáním nového pole ratings_average
.
Pokud tedy například máte ve sbírce vzorový dokument (jak shora, tak níže):
db.collection.insert({
"title": "The Hobbit",
"ratings": [
{
"title": "best book ever",
"rating": 5
},
{
"title": "good book",
"rating": 3.5
}
]
})
Pro výpočet průměru pole hodnocení a promítnutí hodnoty do jiného pole ratings_average
, pak můžete použít následující agregační kanál:
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
}
}
])
Výsledek :
/* 1 */
{
"result" : [
{
"ratings" : [
{
"title" : "best book ever",
"rating" : 5
},
{
"title" : "good book",
"rating" : 3.5
}
],
"ratings_average" : 4.25,
"title" : "The Hobbit"
}
],
"ok" : 1
}