我想用数组评级中的评级字段计算此对象的rating_average字段。你能帮我理解如何使用$ avg聚合?
{
"title": "The Hobbit",
"rating_average": "???",
"ratings": [
{
"title": "best book ever",
"rating": 5
},
{
"title": "good book",
"rating": 3.5
}
]
}
答案 0 :(得分:11)
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
}
答案 1 :(得分:4)
这真的可以写得那么短,在撰写本文时甚至都是如此。如果您想要“平均”,只需使用$avg
:
db.collection.aggregate([
{ "$addFields": {
"rating_average": { "$avg": "$ratings.rating" }
}}
])
原因是,从MongoDB 3.2开始,$avg
运营商获得了“两件”的东西:
以“表达式”形式处理参数“数组”的能力,而不仅仅是$group
的累加器
MongoDB 3.2的功能带来的好处,它允许数组表达式的“速记”表示法。在组成中:
{ "array": [ "$fielda", "$fieldb" ] }
或从数组中注释单个属性作为该属性值的数组:
{ "$avg": "$ratings.rating" } // equal to { "$avg": [ 5, 3.5 ] }
在早期版本中,您必须使用$map
才能访问每个数组元素中的"rating"
属性。现在你没有。
为了记录,即使$reduce
用法也可以简化:
db.collection.aggregate([
{ "$addFields": {
"rating_average": {
"$reduce": {
"input": "$ratings",
"initialValue": 0,
"in": {
"$add": [
"$$value",
{ "$divide": [
"$$this.rating",
{ "$size": { "$ifNull": [ "$ratings", [] ] } }
]}
]
}
}
}
}}
])
如上所述,这实际上只是重新实现了现有的$avg
功能,因此,由于该运营商可用,因此它应该是应该使用的。
答案 2 :(得分:2)
由于数组中有待计算的平均数据,首先需要将其展开。通过在聚合管道中使用$unwind
来执行此操作:
{$unwind: "$ratings"}
然后,您可以在聚合的结果文档中将数组的每个元素作为嵌入文档访问,其中包含键ratings
。然后,您需要$group
title
并计算$avg
:
{$group: {_id: "$title", ratings: {$push: "$ratings"}, average: {$avg: "$ratings.rating"}}}
然后只需恢复您的title
字段:
{$project: {_id: 0, title: "$_id", ratings: 1, average: 1}}
所以这是你的结果汇总管道:
db.yourCollection.aggregate([
{$unwind: "$ratings"},
{$group: {_id: "$title",
ratings: {$push: "$ratings"},
average: {$avg: "$ratings.rating"}
}
},
{$project: {_id: 0, title: "$_id", ratings: 1, average: 1}}
])