【问题标题】:MongoDB aggregation query optimization: $match, $lookup and double $unwindMongoDB聚合查询优化:$match、$lookup和double $unwind
【发布时间】:2021-05-04 22:47:26
【问题描述】:

假设我们有两个集合:

  • devices:此集合中的对象具有(除其他外)字段name(字符串)和cards(数组);该数组中的每个部分都有字段modelslot。这些卡片不是另一个集合,它只是一些嵌套数据。
  • interfaces:此集合中的对象具有(以及其他)字段nameowner

额外信息:

  • 对于cards,我只对slot 是数字的那些感兴趣
  • 对于与先前条件匹配的devicepart,在另一个集合中有一个interface 对象,其中owner 字段的值是devicename,原因并且名字是s[slot]p1(字符's'+那个部分的槽+'p1')

我的工作是创建一个查询以生成所有这些设备中所有现有卡片的摘要,每个条目都包含来自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 表达式,但我无法使用外部slot 字段,即使我将其放入@987654351 @。

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

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

谢谢!

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework pymongo pymongo-3.x


    【解决方案1】:

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

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

    查询中展开的次数应与您想要的结果集中的对象相对应:

    • 0 为设备展开
    • 1 张牌张开
    • 接口展开 2 次​​li>

    为了匹配所需的条件,不需要展开。

    【讨论】:

    • 嘿!谢谢你的建议。我完全错过了我可以在展开之前过滤slots 的事实。 localField + foreignFields 是我以前的,但我认为pipeline 可能会更好。所以我想我可以获取设备的所有接口,然后使用$match 来获取s[slot]p1 一个。在这种情况下,$unwind 仍然是必要的还是有更好的方法来做到这一点? (查找后的匹配应该只返回一个元素,如果出现问题,可能返回 0)。
    • 我不明白你在说什么。如果您想修改您的问题,请随时修改您的问题以说出您想要表达的内容。
    • 我在问是否可以避免第二个$unwind(用于接口),因为总是只有一个名为s[slot]p1 的对象或根本没有对象。展开具有单个元素的数组似乎太多了。数组索引更好/更安全还是我应该坚持放松?
    • 您应该索引用于在第一阶段检索文档的字段。 Unwind 不检索文档,因此不受索引的影响。
    猜你喜欢
    • 2020-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-03
    • 1970-01-01
    • 2020-09-22
    • 2020-07-09
    • 2021-09-03
    相关资源
    最近更新 更多