【问题标题】:how to count number of keys in subdocument using aggregation pipeline?如何使用聚合管道计算子文档中的键数?
【发布时间】:2017-02-10 23:34:34
【问题描述】:

假设我有这样的文件:

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

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

【问题讨论】:

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


    【解决方案1】:

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

    获取数组后,您可以利用 $addFields 管道步骤创建一个保存计数的字段,并使用 $size 运算符得出实际计数.

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

    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 管道以获取所有使用 null _id 的文档的累积总数:

    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 } }
    )
    

    【讨论】:

      【解决方案2】:

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

      在 mongo 外壳上:

      var json=db.sof.findOne().answer_records;
      
      Object.keys(json).length;
      

      打印2表示该文档中的答案记录数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-05
        • 1970-01-01
        • 2016-02-06
        相关资源
        最近更新 更多