11

我遇到了一些特殊目的的集合操作实现,但一般情况下没有。执行集合操作的一般情况是什么(特别是交集、联合、对称差)。这更容易在 $where 或 map reduce 中使用 javascript,但我想知道如何在聚合中执行此操作以获得本机性能。

说明这个问题的更好方法是举个例子。假设我有 2 个数组/集的记录:

db.colors.insert({
    _id: 1,
    left : ['red', 'green'],
    right : ['green', 'blue']
});

我想找到“左”和“右”数组的并集、交集和差异。更好的是,我想从图形上找到:

联合--> ['red', 'green', 'blue']

联盟

交叉口--> ['green']

在此处输入图像描述

对称差--> ['red', 'blue']

在此处输入图像描述

4

2 回答 2

4

仅 2.6+ 版:

从 MongoDB 2.6 版开始,这变得容易得多。您现在可以执行以下操作来解决此问题:

联盟

db.colors.aggregate([
    {'$project': {  
                    union:{$setUnion:["$left","$right"]}
                 }
    }
]);

路口

db.colors.aggregate([
    {'$project': {  
                  int:{$setIntersection:["$left","$right"]}
                 }
    }
]);

相对补

db.colors.aggregate([
    {'$project': {  
                    diff:{$setDifference:["$left","$right"]}
                 }
    }
]);

对称差

db.colors.aggregate([
    {'$project': {  
                    diff:{$setUnion:[{$setDifference:["$left","$right"]}, {$setDifference:["$right","$left"]}]}
                 }
    }
]);

注意:有一张要求将对称差异添加为核心功能,而不必将两组差异合并。

于 2013-09-23T23:41:26.883 回答
3

这三个中最容易使用聚合的是交集**。一般情况可以使用聚合来完成,如下所示:

路口:

db.colors.aggregate([
    {'$unwind' : "$left"},
    {'$unwind' : "$right"},
    {'$project': {  
                    value:"$left", 
                    same:{$cond:[{$eq:["$left","$right"]}, 1, 0]}
                 }
    },
    {'$group'  : { 
                    _id: {id:'$_id', val:'$value'}, 
                    doesMatch:{$max:"$same"}
                 }
    },
    {'$match'   :{doesMatch:1}},
]);

另外两个变得有点棘手。据我所知,没有办法将同一文档中的两个单独字段组合在一起。在 $project 管道阶段有一个 $add、$combine 或 $addToSet 会很好,但是这不存在。因此,我们能做的最好的事情就是说出某物是否相交。我们可以从以下两个聚合开始:

db.colors.aggregate([
    {'$unwind' : "$left"},
    {'$unwind' : "$right"},
    {'$project': {  
                    left:"$left",
                    right:'$right',
                    same:{$cond:[{$eq:["$left","$right"]}, 1, 0]}
                 }
    },
    {'$group'  : {
                    _id:{id:'$_id', left:'$left'},
                    right:{'$addToSet':'$right'},
                    sum: {'$sum':'$same'},
                 }
    },
    {'$project': {  
                    left:{val:"$_id.left",inter:"$sum"},
                    right:'$right',
                 }
    },
    {'$unwind' : "$right"},
    {'$project': {  
                    left:"$left",
                    right:'$right',
                    same:{$cond:[{$eq:["$left.val","$right"]}, 1, 0]}
                 }
    },
    {'$group'  : {
                    _id:{id:'$_id.id', right:'$right'},
                    left:{'$addToSet':'$left'},
                    sum: {'$sum':'$same'},
                 }
    },
    {'$project': {  
                    right:{val:"$_id.right",inter:"$sum"},
                    left:'$left',
                 }
    },
    {'$unwind' : "$left"},
    {'$group'  : {
                    _id:'$_id.id',
                    left:{'$addToSet':'$left'},
                    right: {'$addToSet':'$right'},
                 }
    },
]);

问题中提供的样本的这种聚合将给出如下结果:

{
        "_id" : 1,
        "left" : [
                {
                        "val" : "green",
                        "inter" : 1
                },
                {
                        "val" : "red",
                        "inter" : 0
                }
        ],
        "right" : [
                {
                        "val" : "blue",
                        "inter" : 0
                },
                {
                        "val" : "green",
                        "inter" : 1
                }
        ]
}

从这里我们可以通过将以下内容添加到聚合中来获得交集:

{'$project': {  
                    left:"$left"
                 }
    },
    {'$unwind' : "$left"},
    {'$match'  : {'left.inter': 1}},
    {'$group'  : {
                    _id:'$_id',
                    left:{'$addToSet':'$left'},
                 }
    },

我们可以通过将以下内容添加到基本聚合的末尾来找到差异以及相对补充:

在此处输入图像描述

{'$unwind' : "$left"},
    {'$match'  : {'left.inter': 0}},
    {'$unwind' : "$right"},
    {'$match'  : {'right.inter': 0}},
    {'$group'  : {
                    _id:'$_id',
                    left:{'$addToSet':'$left'},
                    right:{'$addToSet':'$right'},
                 }
    },

不幸的是,似乎没有一种将来自不同领域的不同项目组合在一起的好方法。为了获得工会,似乎最好从客户那里做到这一点。或者,如果您想要过滤,请分别对每个集合进行过滤。

于 2013-06-24T05:52:46.723 回答