给出一组与调查和类别ID相关联的问题:
> db.questions.find().toArray();
[
{
"_id" : ObjectId("4fda05bc322b1c95b531ac25"),
"id" : 1,
"name" : "Question 1",
"category_id" : 1,
"survey_id" : 1,
"score" : 5
},
{
"_id" : ObjectId("4fda05cb322b1c95b531ac26"),
"id" : 2,
"name" : "Question 2",
"category_id" : 1,
"survey_id" : 1,
"score" : 3
},
{
"_id" : ObjectId("4fda05d9322b1c95b531ac27"),
"id" : 3,
"name" : "Question 3",
"category_id" : 2,
"survey_id" : 1,
"score" : 4
},
{
"_id" : ObjectId("4fda4287322b1c95b531ac28"),
"id" : 4,
"name" : "Question 4",
"category_id" : 2,
"survey_id" : 1,
"score" : 7
}
]
我可以找到类别平均值:
db.questions.aggregate(
{ $group : {
_id : "$category_id",
avg_score : { $avg : "$score" }
}
}
);
{
"result" : [
{
"_id" : 1,
"avg_score" : 4
},
{
"_id" : 2,
"avg_score" : 5.5
}
],
"ok" : 1
}
如何获得类别平均值的平均值(注意这与简单平均所有问题不同)?我会假设我会进行多组操作,但这会失败:
> db.questions.aggregate(
... { $group : {
... _id : "$category_id",
... avg_score : { $avg : "$score" },
... }},
... { $group : {
... _id : "$survey_id",
... avg_score : { $avg : "$score" },
... }}
... );
{
"errmsg" : "exception: the _id field for a group must not be undefined",
"code" : 15956,
"ok" : 0
}
>
答案 0 :(得分:32)
了解aggregate()参数中的操作形成管道非常重要。这意味着管道的任何元素的输入是管道中前一个元素生成的文档流。
在您的示例中,您的第一个查询会创建一个如下所示的文档管道:
{
"_id" : 2,
"avg_score" : 5.5
},
{
"_id" : 1,
"avg_score" : 4
}
这意味着pipline的第二个元素是查看一系列文档,其中唯一的键是“_id”和“avg_score”。此文档流中的“category_id”和“score” 键不再存在 。
如果要在此流上进一步聚合,则必须使用管道中此阶段看到的密钥进行聚合。由于您希望平均平均值,因此需要为_id字段输入单个常量值,以便将所有输入文档分组为单个结果。
以下代码生成正确的结果:
db.questions.aggregate(
{ $group : {
_id : "$category_id",
avg_score : { $avg : "$score" },
}
},
{ $group : {
_id : "all",
avg_score : { $avg : "$avg_score" },
}
}
);
运行时,会产生以下输出:
{
"result" : [
{
"_id" : "all",
"avg_score" : 4.75
}
],
"ok" : 1
}