1

我正在尝试使用 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'}]
}
)

请注意,有两个具有相似项目的层次结构。也可以使用不属于自己的物品。我想找到像这样的文件:那里有一所房子使用了它不拥有的东西。

到目前为止,我已经使用如下聚合框架构建了结构。这让我得到了两组不同的项目。但是,我找不到任何可以给我设置交集结果的东西。请注意,由于以下情况,简单的设置大小计数将不起作用:['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']
}

那么我如何在流水线的下一阶段找到集合的使用和拥有的交集?

4

3 回答 3

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 的项目意味着它已被使用但不匹配“拥有”中的任何内容。希望你喜欢这个!

于 2013-06-23T23:41:43.517 回答
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"]}}}
]);
于 2013-09-24T00:24:32.770 回答
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
    }
})
于 2013-06-23T19:59:30.950 回答