2017-10-11 109 views
5

我試圖用mongodb聚合查詢來連接($查找)兩個集合,然後不同計數連接數組中的所有唯一值。 *注意:我不一定知道metaDataMap數組中的字段(鍵)。而且我不想統計或包含Map中可能存在或可能不存在的字段。所以這就是聚合查詢看起來像這樣的原因。Mongodb聚合管道大小和速度問題

所以我的兩個集合是這樣的:事件 -

{ 
"_id" : "1", 
"name" : "event1", 
"objectsIds" : [ "1", "2", "3" ], 
} 

對象

{ 
"_id" : "1", 
"name" : "object1", 
"metaDataMap" : { 
        "SOURCE" : ["ABC", "DEF"], 
        "DESTINATION" : ["XYZ", "PDQ"], 
        "TYPE" : [] 
       } 
}, 
{ 
"_id" : "2", 
"name" : "object2", 
"metaDataMap" : { 
        "SOURCE" : ["RST", "LNE"], 
        "TYPE" : ["text"] 
       } 
}, 
{ 
"_id" : "3", 
"name" : "object3", 
"metaDataMap" : { 
        "SOURCE" : ["NOP"], 
        "DESTINATION" : ["PHI", "NYC"], 
        "TYPE" : ["video"] 
       } 
} 

我的結果是

{ 
_id:"SOURCE", count:5 
_id:"DESTINATION", count: 4 
_id:"TYPE", count: 2 
} 

我到目前爲止是這樣的:

db.events.aggregate([ 
{$match: {"_id" : id}} 

,{$lookup: {"from" : "objects", 
     "localField" : "objectsIds", 
     "foreignField" : "_id", 
     "as" : "objectResults"}} 

,{$unwind: "$objectResults"} //Line 1 
,{$project: {x: "$objectResults.metaDataMap"}} //Line 2 


,{$unwind: "$x"} 
,{$project: {"_id":0}} 

,{$project: {x: {$objectToArray: "$x"}}} 
,{$unwind: "$x"} 

,{$group: {_id: "$x.k", tmp: {$push: "$x.v"}}} 

,{$addFields: {tmp: {$reduce:{ 
input: "$tmp", 
initialValue:[], 
in:{$concatArrays: [ "$$value", "$$this"]} 
    }} 
}} 

,{$unwind: "$tmp"} 
,{$group: {_id: "$_id", uniqueVals: {$addToSet: "$tmp"}}} 

,{$addFields: {count: {"$size":"$uniqueVals"}}} 
,{$project: {_id: "$_id", count: "$count"}} 
]); 

我的問題是我標記第1行& 2.上述工作,但在metaDataMap數組字段(objectsResults.metaDataMap)25,000個值需要約50秒。所以例如在對象1的metaDataMap SOURCE數組中有25,000個值。這是緩慢的方式。我的另一個更快的方式做到這一點是更換線路1 & 2:

,{$project: {x: "$objectResults.metaDataMap"}} //Line 1 
,{$unwind: "$x"} //Line 2 

這是更快的方式(不足3秒),但只能在具有〜10,000種或更少的數據集運行。任何更高的內容,我會收到一個錯誤,說「超過最大文檔大小」。

請幫忙!

+0

可以在「各種陣列中的25,000個項目」中添加更多的描述? –

+1

只是一個想法。也許你可以嘗試改變你的'metaDataMap'結構爲''metaDataMap「:[」k「:{」SOURCE「,」v「:[」ABC「,」DEF「]} ...]'並插入一個' '$ lookup'之後的$ map'階段。例如'{「$ project」:{「data」:{「$ map」:{「input」:「$ objectResults.metaDataMap」,「as」:「resultom」,「in」:{「$ map」 {「input」:「$$ resultom」,「as」:「resultim」,「in」:{「k」:$$ $$。 $ resultim.v「}}}}}}}}'。我相信你可以通過這種方式獲得大小,並且放鬆速度應該更快。 – Veeram

+0

但我不會得到一個明顯的數量與大小。我會嗎?我需要重複v值。 – Deckard

回答

0

如果你能夠改變的object收集您的架構設計,包括parent_id場,你可以立即刪除您的管道的第4個階段(第一$match$lookup$unwind$project)。這會讓關注Line 1Line 2消失。

例如,object集合中的文件看起來像:

{ 
    "_id": "1", 
    "name": "object1", 
    "metaDataMap": { 
    "SOURCE": [ 
     "ABC", 
     "DEF" 
    ], 
    "DESTINATION": [ 
     "XYZ", 
     "PDQ" 
    ], 
    "TYPE": [ ] 
    }, 
    "parent_id": "1" 
} 

因此你不需要昂貴$lookup$unwind。第4個階段,然後可以替換爲:

{$match: {parent_id: id}} 

基於這個想法,我做的管道,這就造成了進一步優化:

db.objects.aggregate([ 
    {$match: {parent_id: id}} 
    ,{$project: {metaDataMap: {$filter: {input: {$objectToArray: '$metaDataMap'}, cond: {$ne: [[], '$$this.v']}}}}} 
    ,{$unwind: '$metaDataMap'} 
    ,{$unwind: '$metaDataMap.v'} 
    ,{$group: {_id: '$metaDataMap.k', val: {$addToSet: '$metaDataMap.v'}}} 
    ,{$project: {count: {$size: '$val'}}} 
]) 

這將輸出:

{ "_id": "TYPE", "count": 2 } 
{ "_id": "DESTINATION", "count": 4 } 
{ "_id": "SOURCE", "count": 5 }