Le framework d'agrégation de MongoDB 3.4 et plus récent offre le $reduce
opérateur qui calcule efficacement le total sans avoir besoin de pipelines supplémentaires. Envisagez de l'utiliser comme expression pour renvoyer les notes totales et obtenir le nombre de notes à l'aide de $size
. Avec $addFields
, la moyenne peut ainsi être calculée à l'aide de l'opérateur arithmétique $divide
comme dans la 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
]
}
]
}
}
}
])
Exemple de sortie
{
"_id" : ObjectId("58ab48556da32ab5198623f4"),
"title" : "The Hobbit",
"ratings" : [
{
"title" : "best book ever",
"rating" : 5.0
},
{
"title" : "good book",
"rating" : 3.5
}
],
"rating_average" : 4.25
}
Avec les anciennes versions, vous devez d'abord appliquer le $unwind
opérateur sur les ratings
champ de tableau en premier comme étape initiale du pipeline d'agrégation. Cela déconstruira les ratings
champ de tableau des documents d'entrée pour produire un document pour chaque élément. Chaque document de sortie remplace le tableau par une valeur d'élément.
La deuxième étape du pipeline serait le $group
opérateur qui regroupe les documents d'entrée par le _id
et title
l'expression d'identifiant des clés et applique le $avg
souhaité expression d'accumulateur à chaque groupe qui calcule la moyenne. Il existe un autre opérateur d'accumulateur $push
qui préserve le champ de tableau des évaluations d'origine en renvoyant un tableau de toutes les valeurs résultant de l'application d'une expression à chaque document du groupe ci-dessus.
La dernière étape du pipeline est le $project
opérateur qui remodèle ensuite chaque document dans le flux, par exemple en ajoutant le nouveau champ ratings_average
.
Donc, si par exemple vous avez un exemple de document dans votre collection (comme ci-dessus et donc ci-dessous) :
db.collection.insert({
"title": "The Hobbit",
"ratings": [
{
"title": "best book ever",
"rating": 5
},
{
"title": "good book",
"rating": 3.5
}
]
})
Pour calculer la moyenne du tableau des notes et projeter la valeur dans un autre champ ratings_average
, vous pouvez ensuite appliquer le pipeline d'agrégation suivant :
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
}
}
])
Résultat :
/* 1 */
{
"result" : [
{
"ratings" : [
{
"title" : "best book ever",
"rating" : 5
},
{
"title" : "good book",
"rating" : 3.5
}
],
"ratings_average" : 4.25,
"title" : "The Hobbit"
}
],
"ok" : 1
}