MongoDB聚合查询优化:$match、$lookup和double $unwind

MongoDB aggregation query optimization: $match, $lookup and double $unwind

假设我们有两个集合:

额外信息:

我的工作是创建一个查询以生成所有这些设备中所有现有卡片的摘要,每个条目都使用 interfaces 集合中的信息进行丰富。我还需要能够对查询进行参数化(如果我只对具有特定名称的特定设备感兴趣,只对特定型号的卡等感兴趣)

到目前为止,我有这个:

mongo_client.devices.aggregate([
    # Retrieve all the devices having the cards field
    {
        "$match": {
            # "name": "<device-name>",
            "cards": {
                "$exists": "true"
            }
        }
    },
    
    # Group current content with every cards object
    {
         "$unwind": "$cards"
    },
    
    # Only take the ones having "slot" a number
    {
        "$match": {
            "cards.slot": {
                "$regex": "^\d+$"
            }
        }
    },
    
    # Retrieve the device's interfaces
    {
        "$lookup": {
            "from": "interfaces",
            "let": {
                "owner": "$name",
            },
            "as": "interfaces",
            "pipeline": [{
                "$match": {
                    "$expr": {
                        "$eq": ["$owner", "$$owner"]
                    },
                },
            }]
        }
    },
    
    {
        "$unwind": "$interfaces"
    },
    
    {
        "$match": {
            "$expr": {
                "$eq": ["$interfaces.name", {
                    "$concat": ["s", "$cards.slot", "p1"]
                }]
            }
        }
    },
    
    # Build the final object
    {
        "$project": {
            # Card related fields
            "slot": "$cards.slot",
            "model": "$cards.model",

            
            # Device related fields
            "device_name": "$name",
           
            # Fields from interfaces
           "interface_field_x": "$interfaces.interface_field_x",
           "interface_field_y": "$interfaces.interface_field_y",
        }
    },
])

查询有效,而且速度非常快,但我有一个问题:

  1. 有什么办法可以避免第二次$unwind?如果每个 device 有 50-150 个 interface 对象,其中 owner 是该设备的名称,我觉得我正在减慢它的速度。每个设备都有一个名为 s[slot]p1 的唯一接口。我怎样才能以更好的方式获得那个特定的对象?我尝试在$lookup甚至$regex$regexMatch内部的$match中使用两个$eq表达式,但我无法使用外部[=16] =] 字段,即使我把它放在 let.

    里面
  2. 如果我想参数化我的查询以在需要时过滤数据,您会添加匹配表达式作为中间步骤还是只在最后过滤?

欢迎对查询进行任何其他改进。我也对如何使其防错感兴趣(如果错误 cards 丢失或 s1p1 接口未找到。

谢谢!

您的问题缺少查询的示例数据,但是:

  • 将第三阶段合并到第一阶段,去掉$exists
  • 使用 localField+foreignField 而不是管道,管道要慢得多

查询中展开的次数应与结果集中所需的对象相对应:

  • 0 台设备展开
  • 1 张纸牌放松
  • 2 接口展开

为了匹配所需的条件,不需要平仓。