Mongodb聚合管道大小和速度问题

时间:2017-10-11 16:07:41

标签: mongodb mongodb-query aggregation-framework

我正在尝试使用mongodb聚合查询来连接($ lookup)两个集合,然后对连接数组中的所有唯一值进行非重复计数。 *注意:我不一定知道metaDataMap数组中的字段(键)。而且我不想计算或包含地图中可能存在或可能不存在的字段。这就是聚合查询看起来像它的原因。

所以我的两个系列看起来像这样:事件 -

{
"_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件或更少的数据集上运行。任何更高的东西,我得到一个错误说“超过最大文件大小”。

请帮忙!

1 个答案:

答案 0 :(得分:0)

如果您能够在object集合上更改架构设计以包含parent_id字段,则可以立即删除管道的前4个阶段(第一个{{1 },$match$lookup$unwind)。这会使对$projectLine 1的担忧消失。

例如,Line 2集合中的文档如下所示:

object

因此,您不需要昂贵的{ "_id": "1", "name": "object1", "metaDataMap": { "SOURCE": [ "ABC", "DEF" ], "DESTINATION": [ "XYZ", "PDQ" ], "TYPE": [ ] }, "parent_id": "1" } $lookup。然后可以将前4个阶段替换为:

$unwind

基于这个想法,我做了进一步的管道优化,结果是:

{$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'}}}
])