Aggregeringsramverket i MongoDB 3.4 och nyare erbjuder $reduce
operatör som effektivt beräknar totalsumman utan behov av extra rörledningar. Överväg att använda det som ett uttryck för att returnera de totala betygen och få antalet betyg med $size
. Tillsammans med $addFields
, medelvärdet kan alltså beräknas med den aritmetiska operatorn $divide
som i formeln 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
]
}
]
}
}
}
])
Exempel på utdata
{
"_id" : ObjectId("58ab48556da32ab5198623f4"),
"title" : "The Hobbit",
"ratings" : [
{
"title" : "best book ever",
"rating" : 5.0
},
{
"title" : "good book",
"rating" : 3.5
}
],
"rating_average" : 4.25
}
Med äldre versioner måste du först tillämpa $unwind
operatorn på ratings
arrayfältet först som ditt första aggregeringspipelinesteg. Detta kommer att dekonstruera ratings
matrisfält från inmatningsdokumenten för att mata ut ett dokument för varje element. Varje utdatadokument ersätter arrayen med ett elementvärde.
Det andra pipelinesteget skulle vara $group
operator som grupperar inmatade dokument efter _id
och title
keys identifier uttryck och tillämpar önskad $avg
ackumulatoruttryck till varje grupp som beräknar medelvärdet. Det finns en annan ackumulatoroperatör $push
som bevarar det ursprungliga klassificeringsfältet genom att returnera en matris med alla värden som är resultatet av att ett uttryck tillämpas på varje dokument i ovanstående grupp.
Det sista pipelinesteget är $project
operator som sedan omformar varje dokument i flödet, till exempel genom att lägga till det nya fältet ratings_average
.
Så, om du till exempel har ett exempeldokument i din samling (som från ovan och så nedan):
db.collection.insert({
"title": "The Hobbit",
"ratings": [
{
"title": "best book ever",
"rating": 5
},
{
"title": "good book",
"rating": 3.5
}
]
})
För att beräkna genomsnittet för betygsmatrisen och projicera värdet i ett annat fält ratings_average
, kan du sedan använda följande aggregeringspipeline:
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
}
}
])
Resultat :
/* 1 */
{
"result" : [
{
"ratings" : [
{
"title" : "best book ever",
"rating" : 5
},
{
"title" : "good book",
"rating" : 3.5
}
],
"ratings_average" : 4.25,
"title" : "The Hobbit"
}
],
"ok" : 1
}