【问题标题】:Mongodb aggregation pipeline size and speed issueMongodb聚合管道大小和速度问题
【发布时间】:2017-10-11 16:07:40
【问题描述】:

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

所以我的两个集合看起来像这样:events-

{
"_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 行。上述方法有效,但元数据映射数组字段(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 个或更少项目的数据集上运行。任何更高的值,我都会收到一条错误消息,提示“超过最大文档大小”。

请帮忙!

【问题讨论】:

  • 能否在“各种数组中的 25,000 个项目”周围添加更多描述?
  • 只是一个想法。也许您可以尝试将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.k","v":{ "$size": "$$resultim.v"}}}}} }}} 这样的东西。我相信这样你可以获得尺寸并且展开应该更快。
  • 但我不会对尺寸进行明确计数。我会吗?我需要对 v 值进行重复数据删除。

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

如果您能够更改 object 集合上的架构设计以包含 parent_id 字段,则可以立即删除管道的前 4 个阶段(第一个 $match$lookup、@ 987654325@ 和 $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 }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-09
    • 2015-04-24
    • 2020-09-20
    • 2021-04-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多