2017-10-11 8 views
5

私はmongodbアグリゲーションクエリを使用して2つのコレクションに結合し、結合された配列のすべての一意の値を区別しています。 *注:私は必ずしも、どのフィールド(キー)がmetaDataMap配列にあるのかわかりません。そして、私はマップに存在していてもいなくてもよいフィールドを数えたり含める必要はありません。そのため、集約クエリは、そうであるように見えます。Mongodbアグリゲーションパイプラインのサイズと速度の問題

だから私の2つのコレクションは、次のようになります。

:イベントを紹介

{ 
"_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の値があるとします。それは遅くする方法です。これは(3秒未満)の方法より高速であるだけ〜万個のアイテム以下を持っているデータセット上で実行することができます

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

:それを行うには私の他のより高速な方法はとライン1 & 2を交換しました。それ以上の高さで、「最大ドキュメントサイズを超えています」というエラーが表示されます。

助けてください!

+0

「さまざまな配列の25,000個のアイテム」の説明をもう少し追加できますか? –

+1

ちょっと考えました。あなたの 'metaDataMap'構造体を' 'metaDataMap ':[" k ":{" SOURCE "、" v ":[" ABC "、" DEF "]} ...]'に変更して、 '$ lookup'の後の' $ map'ステージです。 "{" $ map ":{" $ map ":{" $ map ":" $ " "$" resultim.k "、" v ":{\t" $ size ":\t" $ "resultom"、 "as" $ resultim.v "}}}}}}}}}'となります。私はあなたがサイズを得ることができるこの方法を信じて、巻き戻しは速くなければなりません。 – Veeram

+0

しかし、私はサイズとは別のカウントを取得しません。私は?私はv値を重複する必要があります。 – Deckard

答えて

0

あなたがparent_idフィールドを含めるようにobjectコレクションであなたのスキーマ設計を変えることができるしている場合、あなたはすぐにあなたのパイプラインの最初の4つの段階(第一$match$lookup$unwind、および$project)を削除することができます。これにより、Line 1Line 2の心配がなくなります。例えば

objectコレクション内のドキュメントは、次のようになります。したがって、あなたは高価な$lookupを必要と$unwindません

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

。最初の4つのステージは、その後に置き換えることができます:

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 } 
この考えに基づき

{$match: {parent_id: id}} 

、私は、以下のような結果にパイプラインのさらなる最適化を行いました

関連する問題