【问题标题】:How to do recursive lookups while preserving duplicates in MongoDB如何在 MongoDB 中保留重复项的同时进行递归查找
【发布时间】:2022-01-14 10:49:16
【问题描述】:

我有一个这样的 mongo 数据库:

db.templates.insertMany( [
   { _id: 1, uuid: "1", name: "t1", related_templates: [ "2", "2" ] },
   { _id: 2, uuid: "2", name: "t2", related_templates: [ "3", "3" ] },
   { _id: 3, uuid: "3", name: "t3", related_templates: [ "4", "4" ] },
   { _id: 4, uuid: "4", name: "t4"},
] )

如您所见,数据表示一个树结构,但支持对同一个子节点的重复引用。我正在尝试从 t1 开始递归地获取整个树,包括重复引用。

结果如下所示:

{
    "_id" : 1,
    "uuid" : "1",
    "name": "t1",
    "related_templates" : [
        {
            "_id" : 2,
            "uuid" : "2",
            "name" : "t2",
            "related_templates" : [
                {
                    "_id" : 3,
                    "uuid" : "3",
                    "name" : "t3",
                    "related_templates" : [
                        {
                            "_id" : 4,
                            "uuid" : "4",
                            "name" : "t4"
                        },
                        {
                            "_id" : 4,
                            "uuid" : "4",
                            "name" : "t4"
                        }
                    ]
                },
                {
                    "_id" : 3,
                    "uuid" : "3",
                    "name" : "t3",
                    "related_templates" : [
                        {
                            "_id" : 4,
                            "uuid" : "4",
                            "name" : "t4"
                        },
                        {
                            "_id" : 4,
                            "uuid" : "4",
                            "name" : "t4"
                        }
                    ]
                }
            ]
        },
        ...(t2 repeats here)
    ]
}

Mongo 网站上建议的解决方案在这里:https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/#std-label-unwind-example。 如果没有重复引用,则此解决方案效果很好,只需稍作修改,甚至还允许递归查找。但是,在我的情况下,我需要保留重复的查找

我也考虑过使用 unwind + group 的传统解决方案。该解决方案确实保留了重复项,但我还没有弄清楚如何递归地使用它。

我还考虑过使用 mongo 网站上的解决方案来获取不重复的内容,然后使用地图将获取的数据附加到原始的 related_templates 数组中。我认为这会起作用,但它似乎不是很优雅。

有没有我想念的优雅/更简单的解决方案来做到这一点?

【问题讨论】:

    标签: arrays mongodb aggregation-framework tree-structure


    【解决方案1】:

    如果以后有人遇到这个问题,我会继续发布我想出的解决方案:

    db.templates.aggregate([
      {
        "$match": {'uuid': "1"}
      },
      {
        '$lookup': {
          'from': "templates",
          'let': { 'uuids': "$related_templates"},
          'pipeline': [
            { 
              '$match': { 
                '$expr': { 
                  '$and': [
                    { '$in': [ "$uuid",  "$$uuids" ] },
                  ]
                }
              }
            }
          ],
          'as': "related_templates_objects"
        }
      },
      {
        $addFields: {
          "related_templates_objects_uuids": { 
            $map: {
              input: "$related_templates_objects",
              in: "$$this.uuid"
            }
          }
        }
      },
      {
        $addFields: {
          "related_templates": { 
            $map: {
              input: "$related_templates",
              in: {"$arrayElemAt":[
                "$related_templates_objects",
                {"$indexOfArray":["$related_templates_objects_uuids","$$this"]}
              ]}
            }
          }
        }
      },
      {"$project":{"related_templates_objects":0,"related_templates_objects_uuids":0}}
    ])
    

    总结:

    1. 进行不重复的查找,称为related_templates_objects。
    2. 创建一个与related_templates_objects 相同的数组,除了只提取uuid,称为related_templates_objects_uuids。
    3. 通过将related_templates 中的每个原始引用映射到related_templates_objects 中的正确对象(通过related_templates_objects_uuids 找到其索引)来创建所需的对象数组。
    4. 投影出用于创建新的related_templates 的原始两个中间字段。

    当然,这个解决方案不是递归的。可以通过将外部管道的最后 4 个元素复制到内部管道中来递归一次。 然后按照我在项目中编码的相同复制和粘贴公式再递归 x 次。

    希望解决方案对某人有所帮助。

    【讨论】:

      猜你喜欢
      • 2012-02-09
      • 2011-06-28
      • 1970-01-01
      • 1970-01-01
      • 2021-09-19
      • 2018-03-07
      • 2010-12-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多