Mongodb 几个数组的交集
我有一些文档具有数组属性项。 我想得到n个文档之间的截距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]})
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
]
}