我正在尝试使用Mongo聚合框架来查找在同一文档中有不同唯一集的记录的位置。一个例子将最好地解释这一点:
这是一份不是我的真实数据的文件,但在概念上是相同的:
db.house.insert(
{
houseId : 123,
rooms: [{ name : 'bedroom',
owns : [
{name : 'bed'},
{name : 'cabinet'}
]},
{ name : 'kitchen',
owns : [
{name : 'sink'},
{name : 'cabinet'}
]}],
uses : [{name : 'sink'},
{name : 'cabinet'},
{name : 'bed'},
{name : 'sofa'}]
}
)
请注意,有两个层次结构具有相似的项目。也可以使用非拥有的物品。我想找到像这样的文件:哪里有一个房子使用它不拥有的东西。
到目前为止,我已经使用如下的聚合框架构建了结构。这让我得到了2套不同的物品。但是我找不到任何可以给我一个交集的结果的东西。请注意,设置大小的简单计数将不起作用,因为这样的事情:['couch','cabinet']与['sofa','cabinet']比较。
{'$unwind':'$uses'}
{'$unwind':'$rooms'}
{'$unwind':'$rooms.owns'}
{'$group' : {_id:'$houseId',
use:{'$addToSet':'$uses.name'},
own:{'$addToSet':'$rooms.owns.name'}}}
产生
{ _id : 123,
use : ['sink', 'cabinet', 'bed', 'sofa'],
own : ['bed', 'cabinet', 'sink']
}
如何在管道的下一阶段找到use和own的集合交集?
答案 0 :(得分:4)
你离聚合框架的完整解决方案还不是很远 - 你需要在$group
步骤之前再做一件事情,这样可以让你看看所有正在使用的东西是否与之匹配拥有的东西。
这是完整的管道
> db.house.aggregate(
{'$unwind':'$uses'},
{'$unwind':'$rooms'},
{'$unwind':'$rooms.owns'},
{$project: { _id:0,
houseId:1,
uses:"$uses.name",
isOkay:{$cond:[{$eq:["$uses.name","$rooms.owns.name"]}, 1, 0]}
}
},
{$group: { _id:{house:"$houseId",item:"$uses"},
hasWhatHeUses:{$sum:"$isOkay"}
}
},
{$match:{hasWhatHeUses:0}})
及其在您的文档上的输出
{
"result" : [
{
"_id" : {
"house" : 123,
"item" : "sofa"
},
"hasWhatHeUses" : 0
}
],
"ok" : 1
}
解释 - 一旦你打开两个数组,你现在想要标记使用的项目等于拥有项目的元素,并给它们一个非0“分数”。现在当你通过houseId重新组合时,你可以检查是否有任何使用过的物品没有得到匹配。使用1和0作为分数允许您进行总和,现在匹配项目,其中总和0表示它已被使用但与“拥有”中的任何内容都不匹配。希望你喜欢这个!
答案 1 :(得分:1)
所以这是一个不使用聚合框架的解决方案。这使用$ where运算符和javascript。这对我来说感觉更加笨拙,但似乎有用,所以如果有其他人遇到这个问题,我想把它放在那里。
db.houses.find({'$where':
function() {
var ownSet = {};
var useSet = {};
for (var i=0;i<obj.uses.length;i++){
useSet[obj.uses[i].name] = true;
}
for (var i=0;i<obj.rooms.length;i++){
var room = obj.rooms[i];
for (var j=0;j<room.owns.length;j++){
ownSet[room.owns[j].name] = true;
}
}
for (var prop in ownSet) {
if (ownSet.hasOwnProperty(prop)) {
if (!useSet[prop]){
return true;
}
}
}
for (var prop in useSet) {
if (useSet.hasOwnProperty(prop)) {
if (!ownSet[prop]){
return true;
}
}
}
return false
}
})
答案 2 :(得分:1)
仅适用于MongoDB 2.6+
从MongoDB 2.6开始,项目管道阶段就有可用的设置操作。使用新操作回答此问题的方法是:
db.house.aggregate([
{'$unwind':'$uses'},
{'$unwind':'$rooms'},
{'$unwind':'$rooms.owns'},
{'$group' : {_id:'$houseId',
use:{'$addToSet':'$uses.name'},
own:{'$addToSet':'$rooms.owns.name'}}},
{'$project': {int:{$setIntersection:["$use","$own"]}}}
]);