MongoDB跳过管道阶段吗?

时间:2020-09-09 17:09:44

标签: mongodb mongodb-query aggregation-framework

我想知道聚合管道上是否有任何跳过阶段的方法,更具体地说,如果$ lookup阶段之一找到了matach,则停止并返回。

我需要一个查询来从其他类型和/或组中检索“继承的”数据。在这种情况下,我有三个不同的表:devices_propertiestypes_propertiesgroup_properties,其中存储了每个设备,类型或组的属性。

如果设备具有定义的属性,即geofences,则可以直接从devices_properties读取,如果没有,则必须检查其type和/或其{ {1}}来查看是否在此定义。如果找到其类型,则无需签入组。

我有一个查询,该查询通过检查其类型/组并在不同的表上执行group来工作。然后,通过开关返回相应的文档。但是,它不是最佳方法,因为很多时候该属性将位于第一个表$lookup上。在这种情况下,由于不需要检查设备类型和组以及检查它们各自的属性,因此它会进行3次不必要的查找。不确定我是否正确解释了。

我正确知道的查询如下。有什么优化方法吗?也就是说,如果有匹配项,请在第一个$ lookup之后停止?

devices_properties

谢谢!

1 个答案:

答案 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并最终确定其投影/替换根,就像在原始管道中一样。