如何在 MongoDB 中保留重复项的同时进行递归查找

Posted

技术标签:

【中文标题】如何在 MongoDB 中保留重复项的同时进行递归查找【英文标题】:How to do recursive lookups while preserving duplicates in 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 数组中。我认为这会起作用,但它似乎不是很优雅。

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

【问题讨论】:

【参考方案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
])

总结:

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

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

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

【讨论】:

以上是关于如何在 MongoDB 中保留重复项的同时进行递归查找的主要内容,如果未能解决你的问题,请参考以下文章

Python List:这是在保留顺序的同时删除重复项的最佳方法吗? [复制]

用于复制和保留重复项的 Windows 批处理文件

MongoDb:查找具有重复项的精确数组匹配

具有递归 CTE 的 Postgres:在保留树结构的同时按受欢迎程度对子节点进行排序/排序(父节点始终高于子节点)

如何在保留顺序的同时删除列表中的重复元素?

如何删除列表中重复的构造对象,同时保留顺序并在飞镖中返回列表?