如何使用聚合管道计算子文档中的键数?

时间:2016-10-03 04:17:45

标签: mongodb aggregation-framework pymongo-3.x

假设我有这样的文件:

filter:

我想计算集合中答案记录的数量。显然,该文件提供了两个答案记录,即“1”和“3”。所以,我的问题是如何使用聚合管道来实现这一点。

2 个答案:

答案 0 :(得分:1)

对于MongoDB 3.6及更高版本,请使用聚合管道中的$objectToArray运算符将文档转换为数组。返回数组包含原始文档中每个字段/值对的元素。返回数组中的每个元素都是包含两个字段kv的文档。

在获取数组时,您可以利用$addFields管道步骤创建一个包含计数的字段,并使用 $size <派生实际计数/ strong>运营商。

所有这些都可以通过嵌套表达式在单个管道中完成:

db.collection.aggregate([
    {
        "$addFields": {
            "answers_count": {
                "$size": { 
                    "$objectToArray": "$answer_records"
                }
            }
        }
    }     
])

示例输出

{
    "_id" : ObjectId("57eb386e37b4842ff5f386c9"),
    "lesson_id" : ObjectId("57e27cd190e6993e393f5c74"),
    "student_id" : ObjectId("57d3c3f590e6995fe8de7932"),
    "answer_records" : {
        "1" : {
            "answer" : [ 
                "A"
            ]
        },
        "3" : {
            "answer" : [ 
                "C"
            ]
        }
    },
    "answers_count": 2
}

对于不支持上述运算符的MongoDB服务器版本,您需要更改架构设计,以便使用聚合框架执行高效查询。因为它是你现在所需要的 使用JavaScript在客户端或服务器上预处理文档,因此您无法充分利用MongoDB为更快查询而构建的更好的基础架构。

理想的设计如下:

{
    "_id" : ObjectId("57eb386e37b4842ff5f386c9"),
    "lesson_id" : ObjectId("57e27cd190e6993e393f5c74"),
    "student_id" : ObjectId("57d3c3f590e6995fe8de7932"),
    "answer_records" : [
        { "id": "1", "answer": "A" }
        { "id": "3", "answer": "C" }
    ]
}

然后您可以简单地应用聚合的 $project 管道,该管道使用 $size 运算符来返回每个文档的answer_records数组:

db.collection.aggregate([
    { 
        "$project": {
            "lesson_id": 1,
            "student_id": 1,
            "count": { "$size": "$answer_records" }
        }
    }
])

如果您想要整个集合的答案记录总数,请添加另一个 $group 管道,以使用_id为null获取所有文档的累计总数:

db.collection.aggregate([
    { 
        "$project": {           
            "count": { "$size": "$answer_records" }
        }
    },
    {
        "$group": {
            "_id": null,
            "total_answers": { "$sum": "$count" }
        }
    }
])

否则,对于当前设计,您唯一的选择是MapReduce,它要慢得多:

db.collection.mapReduce(
    function() {
        emit(this._id, Object.keys(this.answer_records).length);
    },
    function() { },
    { "out": { "inline": 1 } }
)

示例输出:

{
    "results" : [ 
        {
            "_id" : ObjectId("57eb386e37b4842ff5f386c9"),
            "value" : 2
        }
    ],
    ....
}

要获取集合中所有文档的总数,请运行此mapReduce操作:

db.collection.mapReduce(
    function() {
        emit(null, Object.keys(this.answer_records).length);
    },
    function(key, values) {
        return Array.sum(values);
    },
    { "out": { "inline": 1 } }
)

答案 1 :(得分:1)

在您的情况下,使用JS要容易得多。

在mongo shell上:

var json=db.sof.findOne().answer_records;

Object.keys(json).length;

打印2表示上述文件中的答案记录数。