我想知道聚合管道上是否有任何跳过阶段的方法,更具体地说,如果$ lookup阶段之一找到了matach,则停止并返回。
我需要一个查询来从其他类型和/或组中检索“继承的”数据。在这种情况下,我有三个不同的表:devices_properties
,types_properties
和group_properties
,其中存储了每个设备,类型或组的属性。
如果设备具有定义的属性,即geofences
,则可以直接从devices_properties
读取,如果没有,则必须检查其type
和/或其{ {1}}来查看是否在此定义。如果找到其类型,则无需签入组。
我有一个查询,该查询通过检查其类型/组并在不同的表上执行group
来工作。然后,通过开关返回相应的文档。但是,它不是最佳方法,因为很多时候该属性将位于第一个表$lookup
上。在这种情况下,由于不需要检查设备类型和组以及检查它们各自的属性,因此它会进行3次不必要的查找。不确定我是否正确解释了。
我正确知道的查询如下。有什么优化方法吗?也就是说,如果有匹配项,请在第一个$ lookup之后停止?
devices_properties
谢谢!
答案 0 :(得分:0)
我怀疑这个特定的查询需要优化,但是聚合管道中的条件阶段通常是一个有趣的问题。
首先,在第一阶段,您已经通过索引字段选择了最多1个文档,这已经是非常理想的。您的所有查找操作都相同,因此即使在大型馆藏中,我们也要讨论整个管道的几十毫秒的幅度。值得优化吗?
对于更普通的情况,当查询确实很昂贵时,可以结合使用$facet运行条件管道和$concatArrays合并结果。
第一次查找保持原样:
db.devices.aggregate([
....
{"$lookup" : {
"from": "devices_properties",
"pipeline": [
{"$match" : {"_id": "alvarolb@esp32:geofences"}},
],
"as": "device"
}},
然后我们添加一个指示符,该指示符是否返回任何结果,因此我们不再需要查找:
{$addFields:{found: {$size: "$device"}}},
然后,我们在构面中定义了2个管道:一个带有下一个查询,另一个没有。要运行的开关是每个管道中的第一个$ match阶段:
{$facet:{
yes:[
{$match: {"$expr" : {$gt:["$found", 0]}}},
],
no:[
{$match: {"$expr" : {$eq:["$found", 0]}}},
{"$lookup" : {
"from": "groups_properties",
"let" : {"asset_group" : "$asset_group"},
"pipeline": [
{"$match" : {"$expr" : { "$eq" : ["$_id", "$$asset_group"]}}}
],
"as": "group"
}}
]
}},
在此阶段之后,我们有2个数组“是”和“否”,其中之一始终为空。合并两者并转换为顶级文档:
{$addFields: {yesno: {$concatArrays:["$yes", "$no"]}}},
{$unwind: "$yesno"},
{"$replaceRoot": { "newRoot": "$yesno"}},
如果到目前为止发现任何内容,请重新计算指标:
{$addFields:{found: {$add: [ "$found", {$size: {$ifNull:["$group", []]}}]}}},
并为下一次查找重复相同的技术:
$facet with $lookup in `groups_properties`
$addFields with $concatArrays
$unwind
$replaceRoot
然后以类似的方式types_properties
并最终确定其投影/替换根,就像在原始管道中一样。