Рамката за агрегиране в MongoDB 3.4 и по-нови предлага $reduce
оператор, който ефективно изчислява общата сума без нужда от допълнителни тръбопроводи. Помислете да го използвате като израз, за да върнете общите оценки и да получите броя на оценките с помощта на $size
. Заедно с $addFields
, средната стойност може да бъде изчислена с помощта на аритметичния оператор $divide
както във формулата 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
]
}
]
}
}
}
])
Изходен пример
{
"_id" : ObjectId("58ab48556da32ab5198623f4"),
"title" : "The Hobbit",
"ratings" : [
{
"title" : "best book ever",
"rating" : 5.0
},
{
"title" : "good book",
"rating" : 3.5
}
],
"rating_average" : 4.25
}
При по-старите версии ще трябва първо да приложите $unwind
оператор на ratings
поле на масива първо като първоначална стъпка на тръбопровода за агрегиране. Това ще деконструира ratings
поле на масив от входните документи за извеждане на документ за всеки елемент. Всеки изходен документ замества масива със стойност на елемент.
Вторият етап на конвейера ще бъде $group
оператор, който групира входните документи по _id
и title
keys идентификатор и прилага желания $avg
акумулаторен израз към всяка група, която изчислява средната стойност. Има и друг акумулаторен оператор $push
който запазва оригиналното поле на масива с оценки, като връща масив от всички стойности, които са резултат от прилагането на израз към всеки документ в горната група.
Последната стъпка на конвейера е $project
оператор, който след това преоформя всеки документ в потока, като например чрез добавяне на ново поле ratings_average
.
Така че, ако например имате примерен документ във вашата колекция (както отгоре и така отдолу):
db.collection.insert({
"title": "The Hobbit",
"ratings": [
{
"title": "best book ever",
"rating": 5
},
{
"title": "good book",
"rating": 3.5
}
]
})
За изчисляване на средната стойност на масива с рейтинги и проектиране на стойността в друго поле ratings_average
, след това можете да приложите следния конвейер за агрегиране:
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
}
}
])
Резултат :
/* 1 */
{
"result" : [
{
"ratings" : [
{
"title" : "best book ever",
"rating" : 5
},
{
"title" : "good book",
"rating" : 3.5
}
],
"ratings_average" : 4.25,
"title" : "The Hobbit"
}
],
"ok" : 1
}