MongoDB 3.4 和更新版本中的aggregation framework 提供了 $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
}
对于旧版本,您需要首先在 ratings 数组字段上应用 $unwind 运算符作为初始聚合管道步骤。这将从输入文档中解构 ratings 数组字段以输出每个元素的文档。每个输出文档都将数组替换为一个元素值。
第二个管道阶段是$group 运算符,它按_id 和title 键标识符表达式对输入文档进行分组,并将所需的$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
}