Mongo在单个字段上设置交集,这是一个二维数组

时间:2016-02-09 07:30:03

标签: javascript mongodb set mongodb-query aggregation-framework

我有mongo,以下结构的文件。我想获得父母中孩子的所有日期。

{"parent": 1, "child": "a", "date": "2016-02-01"},
{"parent": 1, "child": "a", "date": "2016-02-02"},
{"parent": 1, "child": "a", "date": "2016-02-03"},
{"parent": 1, "child": "b", "date": "2016-02-01"},
{"parent": 1, "child": "b", "date": "2016-02-03"},
{"parent": 2, "child": "a", "date": "2016-02-02"},
{"parent": 2, "child": "a", "date": "2016-02-03"},
{"parent": 2, "child": "b", "date": "2016-02-01"},
{"parent": 2, "child": "b", "date": "2016-02-02"}

为此,我使用聚合框架和以下管道来获取日期数组的数组。

{
    $group: {
        _id: {
            parent: "$parent",
            child: "$child"
        },
        dates: {
            $push: "$date"
        }
    }
}, {
    $group: {
        _id: "$_id.parent",
        dates: {
            $push: "$dates"
        }
    }
}

输出结果如下:

[ 
    {
        "_id" : 1,
        "dates" : [ 
            [ 
                "2016-02-01", 
                "2016-02-03"
            ], 
            [ 
                "2016-02-01", 
                "2016-02-02", 
                "2016-02-03"
            ]
        ]
    }, 
    {
        "_id" : 2,
        "dates" : [ 
            [ 
                "2016-02-01", 
                "2016-02-02"
            ], 
            [ 
                "2016-02-02", 
                "2016-02-03"
            ]
        ]
    }
]

我现在想要获取每个文档的2d数组中的所有常见日期,并尝试使用$projection添加$setIntersection阶段。但据我所知,$setIntersection需要一组定义明确的字段或数组 - 使用$setIntersection: "$dates"不能按预期工作。

感谢任何帮助!

附加信息:子类型的数量是可变的

预期产出:

[ 
    {
        "_id" : 1.0000000000000000,
        "dates" : [
            "2016-02-01", 
            "2016-02-03"
        ]
    }, 
    {
        "_id" : 2.0000000000000000,
        "dates" : [ 
            "2016-02-02"
        ]
    }
]

2 个答案:

答案 0 :(得分:1)

使用MongoDB 3.2,您可以使用$arrayElemAt获取两个维度的每个元素并将其提供给$setIntersection

db.collection.aggregate([
    { "$group": {
        "_id": {
            "parent": "$parent",
            "child": "$child"
        },
        "dates": { "$push": "$date" }
    }}, 
    { "$group": {
        "_id": "$_id.parent",
        "dates": { "$push": "$dates" }
    }},
    { "$project": {
        "dates": {
            "$setIntersection": [
                { "$arrayElemAt": [ "$dates", 0 ] },
                { "$arrayElemAt": [ "$dates", 1 ] }
            ]
        }
    }}
])

但实际上,您正在考虑的特定问题可以通过更加简化的方式解决。基本上你需要做的就是计算父母每个日期的孩子出生率。任何超过一个表示有两个或更多孩子分享日期:

db.collection.aggregate([
    { "$group": {
        "_id": {
            "parent": "$parent",
            "date": "$date"
        },
        "count": { "$sum": 1 }
    }},
    { "$match": { "count": { "$gt": 1 } } },
    { "$group": {
        "_id": "$_id.parent",
        "dates": { "$push": "$_id.date" }
    }}
])    

所以没有理由比较数组,因为分组元素的简单计数告诉你"设置交集"会的。

同样的原则适用于引入聚合框架的每个MongoDB版本。

两者都给你相同的结果:

{ "_id" : 1, "dates" : [ "2016-02-03", "2016-02-01" ] }
{ "_id" : 2, "dates" : [ "2016-02-02" ] }

同时注意到"设置"不被认为是有序的,也不是从$group发出的键的顺序。

答案 1 :(得分:1)

这里可以做的是计算父母子女的数量,并计算父母每个孩子的日期数。

然后获取所有日期,其中日期的数量等于父项的子项数,这将给出预期的输出。

这是我尝试过的,但可能有更好的解决方案。

db.coll.aggregate([
    {
        $group: {
            _id: {
                parent: "$parent",
                child: "$child"
            },
            dates: {
                $push: "$date"
            }
        }
    },
    {
        $group: {
            _id: "$_id.parent",
            total_children: {$sum : 1},
            dates: {
                $push: "$dates"
            }
        }
    },
    {
        $unwind : "$dates"
    },
    {
        $unwind : "$dates"
    },
    {
        $group : {
            _id : {
                parent : "$_id",
                dates : "$dates"
            },
            total_children : {$first : "$total_children"},
            total_dates : {$sum : 1}
        }
    },
    {
        $project : {
            _id : 1,
            tempEq : {$eq : ["$total_children", "$total_dates"]}
        }
    },
    {
        $match : {'tempEq' : true}
    },
    {
        $group : {
            _id : '$_id.parent',
            dates : {$addToSet : "$_id.dates"}
        }
    }
])

这给出了以下输出:

{ "_id" : 1, "dates" : [ "2016-02-01", "2016-02-03" ] }
{ "_id" : 2, "dates" : [ "2016-02-02" ] }

希望这有帮助。