Struktura agregacji w MongoDB 3.4 i nowszych oferuje $reduce
operatora, który wydajnie oblicza sumę bez potrzeby stosowania dodatkowych rurociągów. Rozważ użycie go jako wyrażenia, aby zwrócić całkowitą ocenę i uzyskać liczbę ocen za pomocą $size
. Razem z $addFields
, średnia może być zatem obliczona za pomocą operatora arytmetycznego $divide
jak w formule 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
]
}
]
}
}
}
])
Przykładowe wyjście
{
"_id" : ObjectId("58ab48556da32ab5198623f4"),
"title" : "The Hobbit",
"ratings" : [
{
"title" : "best book ever",
"rating" : 5.0
},
{
"title" : "good book",
"rating" : 3.5
}
],
"rating_average" : 4.25
}
W przypadku starszych wersji musisz najpierw zastosować $unwind
operator na ratings
pole tablicy jako pierwszy krok potoku agregacji. To zdekonstruuje ratings
pole tablicy z dokumentów wejściowych, aby wyprowadzić dokument dla każdego elementu. Każdy dokument wyjściowy zastępuje tablicę wartością elementu.
Drugim etapem potoku byłaby $group
operator, który grupuje dokumenty wejściowe według _id
i title
klucze wyrażenie identyfikatora i stosuje żądany $avg
wyrażenie akumulacyjne do każdej grupy, która oblicza średnią. Istnieje inny operator akumulatora $push
które zachowuje oryginalne pole tablicy ocen, zwracając tablicę wszystkich wartości, które wynikają z zastosowania wyrażenia do każdego dokumentu w powyższej grupie.
Ostatnim krokiem potoku jest $project
operator, który następnie zmienia kształt każdego dokumentu w strumieniu, na przykład przez dodanie nowego pola ratings_average
.
Tak więc, jeśli na przykład masz w swojej kolekcji przykładowy dokument (jak powyżej i tak poniżej):
db.collection.insert({
"title": "The Hobbit",
"ratings": [
{
"title": "best book ever",
"rating": 5
},
{
"title": "good book",
"rating": 3.5
}
]
})
Aby obliczyć średnią tablicy ocen i wyświetlić wartość w innym polu ratings_average
, możesz następnie zastosować następujący potok agregacji:
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
}
}
])
Wynik :
/* 1 */
{
"result" : [
{
"ratings" : [
{
"title" : "best book ever",
"rating" : 5
},
{
"title" : "good book",
"rating" : 3.5
}
],
"ratings_average" : 4.25,
"title" : "The Hobbit"
}
],
"ok" : 1
}