【问题标题】:Grouping records in nested documents对嵌套文档中的记录进行分组
【发布时间】:2014-08-15 06:56:57
【问题描述】:

我有这样的文件:

{
"_id" : ObjectId("533e6ab0ef2188940b00002c"),
"uin" : "1396599472869",
"vm" : {
    "0" : {
        "draw" : "01s",
        "count" : "2",
        "type" : "",
        "data" : {
            "title" : "K1"
        },
        "child" : [ 
            "1407484608965"
        ]
    },
    "1407484608965" : {
        "data" : {
            "title" : "K2",
            "draw" : "1407473540857",
            "count" : "1",
            "type" : "Block"
        },
        "child" : [ 
            "1407484647012"
        ]
    },
    "1407484647012" : {
        "data" : {
            "title" : "K3",
            "draw" : "03.8878.98",
            "count" : "1",
            "type" : "SB"
        },
        "child" : [ 
            "1407484762473"
        ]
    },
    "1407484762473" : {
        "data" : {
            "type" : "SB",
            "title" : "D1",
            "draw" : "7984",
            "count" : "1"
        },
        "child" : []
    }
}
}

如何根据条件(type="Block")对所有记录进行分组?

我试过了: db.ITR.aggregate({$match:{"uin":"1396599472869"}},{$project:{"vm":1}},{$group:{_id:null,r1:{$ push:"$vm"}}},{$unwind:"$r1"},{$group:{_id:null,r2:{$push:"$r1"}}},{$unwind:"$r2 "})

但是结果仍然是一个对象的形式,而不是一个数组。使用“MapReduce”我没有得到。

【问题讨论】:

  • "vm" 是一个对象而不是一个数组 "vm.child" 是一个数组。你看出区别了吗?
  • 我知道“vm”不是一个数组,所以困难在于我无法获得日期字段(就像通过列表“$”中的运算符位置处理数组时一样)。如何获取文档中的“类型”字段:vm:{0:{data:{type:"Block"}}} vm->0->data->type
  • 您的问题不是很清楚,您似乎在评论中提出了其他问题。像这样的“子文档”条目需要访问“精确”路径。我认为您应该进行编辑以清楚地说明您正在尝试做什么。一个很好的理由是您的示例聚合语句在第二组阶段与第一个阶段没有什么不同。所以很难确切地看到你想要什么。可能会显示预期的示例结果。
  • 查询:选择 type="Block" 的所有记录。我的案例门的结果必须是这样的:“1407484608965”:{“data”:{“title”:“K2”,“draw”:“1407473540857”,“count”:“1”,“type”:“Block " }

标签: mongodb mapreduce aggregation-framework


【解决方案1】:

您的问题基本上与您当前的文档结构方式有关。此处“vm”下实际识别数据点的“keys”的使用通常与标准查询表单和聚合框架不兼容。

这通常也不是一个很好的模式,因为要访问“vm”下的任何部分,您需要指定数据的“确切路径”。所以寻找“Block”类型需要这样:

db.collection.find({
    "$or": [
        { "vm.0.type": "Block" },
        { "vm.1407484608965.type": "Block" }
        { ... }
    ]
})

等等。您不能像这样“通配符”字段名称,因此需要确切的路径。

一种更好的建模方法是使用数组,然后将内部键移动到文档中:

{
    "_id" : ObjectId("533e6ab0ef2188940b00002c"),
    "uin" : "1396599472869",
    "vm" : [
        {
            "key": 0,
            "draw" : "01s",
            "count" : "2",
            "type" : "",
            "data" : {
                "title" : "K1"
            },
            "child" : [ 
                "1407484608965"
            ]
        },
        {
            "key": "1407484608965",
            "title" : "K2",
            "draw" : "1407473540857",
            "count" : "1",
            "type" : "Block",
            "child" : [ 
                "1407484647012"
            ]
        },
        {
            "key": "1407484647012",
            "title" : "K3",
            "draw" : "03.8878.98",
            "count" : "1",
            "type" : "SB",
            "child" : [ 
                "1407484762473"
            ]
        }
    ]
}

这允许您通过公共路径查询包含匹配属性的文档,这大大简化了事情:

db.collection.find({ "vm.type": "Block" })

或者,如果您想“过滤”数组内容,以便只返回匹配的“子文档”,您可以这样做:

db.collection.aggregate([
    { "$match": { "vm.type": "Block" } },
    { "$unwind": "$vm" },
    { "$match": { "vm.type": "Block" } },
    { "$group": {
        "_id": "$_id",
        "uin": { "$first": "$uin" },
        "vm": { "$push": "$vm" }
    }}
])

或者甚至可能使用 MongoDB 2.6 或更高版本:

db.collection.aggregate([
    { "$match": { "vm.type": "Block" } },
    { "$project": {
        "uin": 1,
        "vm": {
            "$setDifference": [
                { "$map": {
                    "input": "$vm",
                    "as": "el",
                    "in": {"$cond": [
                        { "$eq": [ "$$el.type", "Block" ] },
                        "$$el",
                        false
                    ]}
                }},
                [false]
            ]
        }
    }}
])

或任何其他操作,现在可以简化为遍历数据的结构。但是由于您的数据目前代表您“遍历键”的唯一选择是使用 JavaScript 操作,这比能够以正确方式查询要慢得多:

db.collection.find(function() {
    return Object.keys(this.vm).some(function(x) { 
        return this.vm[x].type == "Block" 
    })
})

或者使用 mapReduce 进行类似的对象处理,但基本上没有其他方法可以访问具有始终变化的固定路径的字段。

也许这是为了避免放置“子”元素的“嵌套数组”而进行的设计。当然,这会给更新带来问题。但实际上,如果任何元素不应该是数组,它可能是“内部”元素,例如“child”,它可能具有某种不使用数组的结构。

因此,关键是考虑重组,因为这可能适合您想要的模式,而不会导致 JavaScript 遍历将引入的性能问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-24
    相关资源
    最近更新 更多