我试图使用MongoDB聚合查询来联接($lookup)两个集合,然后对联接数组中的所有唯一值进行DISTINCT计数。*注意:我不一定知道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)中的25000个值大约需要50秒。例如,在对象1元数据映射源数组中有25000个值。这是慢下来的方法。我的另一个更快的方法是将第1行和第2行替换为:
,{$project: {x: "$objectResults.metaDataMap"}} //Line 1
,{$unwind: "$x"} //Line 2
这要快得多(不到3秒),但只能在包含约10000个项或更少项的数据集上运行。如果再高一点,就会出现一个错误,说“超过了最大文档大小”。
请帮忙!
最佳答案
如果您能够将object
集合上的模式设计更改为包含parent_id
字段,则可以立即移除管道的前4个阶段(第一个$match
、$lookup
、$unwind
和$project
)。这将使对Line 1
和Line 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 }