24

给定一组链接调查和类别 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
}
>
4

1 回答 1

34

重要的是要了解 aggregate() 参数中的操作形成一个pipeline。这意味着管道中任何元素的输入都是管道中前一个元素生成的文档流。

在您的示例中,您的第一个查询创建了一个如下所示的文档管道:

{
    "_id" : 2,
    "avg_score" : 5.5
},
{
    "_id" : 1,
    "avg_score" : 4
}

这意味着管道的第二个元素是查看一系列文档,其中唯一的键是“_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
 }
于 2012-06-15T17:42:44.723 回答