2013-06-24 103 views
10

我遇到了set操作的一些特殊目的實現,但對於一般情況沒有任何作用。執行集合操作的一般情況是什麼(特別是交集,聯合,對稱差異)。這很容易弄清楚在$ where或map reduce中使用javascript,但我想知道如何在聚合中執行此操作以獲得本機性能。如何使用MongoDB聚合進行通用集合操作(​​union,intersection,difference)

說明這個問題的更好的方法是舉一個例子。說我有一個2列/套的紀錄:

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

我想要找的「左」和「右」的陣列的集,交集和差異性。更妙的是,形象地我想找到:

聯盟 - > [ '紅', '綠', '蘭']

union

路口 - > ['綠色']

enter image description here

對稱差 - > [' 紅」, '藍']

enter image description here

+0

這個問題的靈感來自於這[答案](http://stackoverflow.com/a/17266323/311525)。我發現了幾個具體的案例,但沒有找到我正在尋找的一般案例。 – Scott

+0

[使用MongoDB聚合在同一文檔中查找兩個集合的交集]的可能重複(http://stackoverflow.com/questions/17264017/use-mongodb-aggregation-to-find-set-intersection-of-two -sets-within-the-same-doc) – WiredPrairie

+0

@WiredPrairie,考慮到你鏈接的是我的,我可以向你保證他們不是重複的問題。你鏈接到的是一個非常特殊的情況下找到一個子集。這是一個更廣泛的關於交叉點,工會和差異的更廣泛的案例問題。 – Scott

回答

4

版本2。6+ Only:

從2.6版的MongoDB開始,這變得容易多了。現在,您可以執行以下操作來解決這個問題:

聯盟

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"]}]} 
       } 
    } 
]); 

注:有一個ticket請求對稱差被添加爲核心功能,而不必做兩個集不同的工會。

2

最簡單的這三個使用聚合的是交叉點**。對於一般的情況下,可以使用聚合像這樣做:

交叉路口:

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'}, 
       } 
    }, 

我們可以通過將以下內容添加到基本聚合的末尾來找到差異以及相關補充:

enter image description here

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

遺憾的是,似乎沒有要到來自不同領域對不同的項目結合起來的好方法。爲了得到工會,最好從客戶那裏做到這一點。或者,如果您想過濾,請分別在每一組上進行。

+0

認真?我指出你對「工會」的回答,給你相對補充和交集的答案。是否真的需要另外一個問題呢? –

+0

@Asya,這不會讓你失望。我在上面的評論中給了你信任。我補充說的原因是其他人是更具體的情況。第一個是你想要解析出哪裏有交集的地方。第二種情況你幫我找到左補。我認爲有一個用於另一個問題,因爲其他問題是非常具體的用例。我試圖幫助社區,而不是從你發佈的答案中拿走任何信用。這個問題服務器更好的社區維基類型。你應該更開心,我正在幫你的公司付錢。 – Scott

+0

如果可能,這是我預先計算和存儲的事物的類型。我不想在生產系統中遇到這樣的聚合管道。 – WiredPrairie