使用Mongo聚合框架的多个组操作

时间:2012-06-14 20:11:52

标签: mongodb

给出一组与调查和类别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
}
>

1 个答案:

答案 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
 }