Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/mongodb/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mongodb 几个数组的交集_Mongodb_Aggregation Framework - Fatal编程技术网

Mongodb 几个数组的交集

Mongodb 几个数组的交集,mongodb,aggregation-framework,Mongodb,Aggregation Framework,我有一些文档具有数组属性项。 我想得到n个文档之间的截距 db.things.insert({name:"A", items:[1,2,3,4,5]}) db.things.insert({name:"B", items:[2,4,6,8]}) db.things.insert({name:"C", items:[1,2]}) db.things.insert({name:"D", items:[5,6]}) db.things.insert({name:"E", items:[9,10]})

我有一些文档具有数组属性项。 我想得到n个文档之间的截距

db.things.insert({name:"A", items:[1,2,3,4,5]})
db.things.insert({name:"B", items:[2,4,6,8]})
db.things.insert({name:"C", items:[1,2]})
db.things.insert({name:"D", items:[5,6]})
db.things.insert({name:"E", items:[9,10]})
db.things.insert({name:"F", items:[1,5]})
数据:

例如: 事物。鬃毛。A截取事物。鬃毛。C截取事物。鬃毛。F:

[1,2,3,4,5]截取[1,2]截取[1,5]

必须是:[1]

我认为使用$setIntersection是可行的,但我找不到方法

我可以用两个文档来完成,但是如何用更多文档来完成呢

    db.things.aggregate({$match:{"name":{$in:["A", "F"]}}}, 
    {$group:{_id:null, "setA":{$first:"$items"}, "setF":{$last:"$items"} } },
    {
            "$project": { 
                "set1": 1, 
                "set2": 1, 
                "commonToBoth": { "$setIntersection": [ "$setA", "$setF" ] }, 
                "_id": 0 
            }
        }
    )

{ "commonToBoth" : [ 5, 1 ] }

如果您使用的是mongo 3.2,则可以使用
arrayElemAt
精确计算
$setIntersection
的所有参数:

db.things.aggregate([{
    $match: {
        "name": {
            $in: ["A", "B", "C"]
        }
    }
}, {
    $group: {
        _id: 0,
        elements: {
            $push: "$items"
        }
    }
}, {
    $project: {
        intersect: {
            $setIntersection: [{
                "$arrayElemAt": ["$elements", 0]
            }, {
                "$arrayElemAt": ["$elements", 1]
            }, {
                "$arrayElemAt": ["$elements", 2]
            }]
        },
    }
}]);
您必须动态添加所需数量的JsonObject,索引如下:

{
    "$arrayElemAt": ["$elements", <index>]
}

这不是一个干净的解决方案,但它是有效的

一个不特定于输入项数量的解决方案可能是这样的:

db.things.aggregate(
    {
        $match: {
            "name": {
                $in: ["A", "F"]
            }
        }
    },
    {
        $group: {
            _id: "$items",
            count: {
                $sum: 1
            }
        }
    },
    { 
        $group: { 
            _id: null,
            totalCount: {
                $sum: "$count"
            },
            items: {
                $push: "$_id"
            }
        }
    },
    {
        $unwind: {
            path: "$items"
        }
    },
    { 
        $unwind: {
            path: "$items"
        }
    },
    { 
        $group: {
            _id: "$items",
            totalCount: {
                $first: "$totalCount"
            },            
            count: { 
                $sum: 1
            }
        }
    },
    {
        $project: {
            _id: 1,
            presentInAllDocs: {
                $eq: ["$totalCount", "$count"]
            }
        }
    },
    {
        $match: {
            presentInAllDocs: true
        }
    },
    {
        $group: {
            _id: null,
            items: {
                $push: "$_id"
            }
        }
    }
)
哪个会输出这个

{
    "_id" : null,
    "items" : [ 
        5, 
        1
    ]
}
当然,您可以添加最后一个
$project
阶段,以将结果转化为所需的形状


解释

这背后的基本思想是,当我们计算文档数量和每个项目的出现次数时,计数等于总文档计数的项目出现在每个文档中,因此出现在相交结果中。
这个想法有一个重要的假设:您的
数组中没有重复项(即它们是集合)。如果这个假设是错误的,那么您必须在管道的开头插入一个额外的阶段,以将阵列转换为集合。
我们也可以用一种不同的、可能更短的方式构建这个管道,但我试图将资源使用率保持在尽可能低的水平,因此添加了可能不必要的(从功能角度来看)阶段。例如,第二阶段按
数组分组,因为我的假设是,与文档相比,不同的值/数组要少得多,因此管道的其余部分必须使用初始文档计数的一小部分。然而,从功能的角度来看,我们只需要文档的总数,因此我们可以跳过该阶段,只需做一个
$group
阶段,对所有文档进行计数,并将它们放入一个数组中供以后使用-这当然是内存消耗的一大打击,因为我们现在有了一个包含所有可能文档的数组

db.things.aggregate(
    {
        $match: {
            "name": {
                $in: ["A", "F"]
            }
        }
    },
    {
        $group: {
            _id: "$items",
            count: {
                $sum: 1
            }
        }
    },
    { 
        $group: { 
            _id: null,
            totalCount: {
                $sum: "$count"
            },
            items: {
                $push: "$_id"
            }
        }
    },
    {
        $unwind: {
            path: "$items"
        }
    },
    { 
        $unwind: {
            path: "$items"
        }
    },
    { 
        $group: {
            _id: "$items",
            totalCount: {
                $first: "$totalCount"
            },            
            count: { 
                $sum: 1
            }
        }
    },
    {
        $project: {
            _id: 1,
            presentInAllDocs: {
                $eq: ["$totalCount", "$count"]
            }
        }
    },
    {
        $match: {
            presentInAllDocs: true
        }
    },
    {
        $group: {
            _id: null,
            items: {
                $push: "$_id"
            }
        }
    }
)
{
    "_id" : null,
    "items" : [ 
        5, 
        1
    ]
}