使用 Express 和 Mongo 制作带有递归表的节点树

Posted

技术标签:

【中文标题】使用 Express 和 Mongo 制作带有递归表的节点树【英文标题】:Make node tree with recursive table with Express and Mongo 【发布时间】:2021-03-16 05:50:03 【问题描述】:

我正在使用 ExpressJS 和 Mongo 使用 REST api,并且我有一个包含 N 个级别的集合。

所以为了解决这个问题,我在 mongo 中使用了一个递归表(或集合),其中一个字段是 id,每个寄存器都有一个 parent_id,它与它的子级处于同一级别。 为了更好地解释这一点,这里是一个 E-R 表示

如你所见,mongo 会像这样保存 json 数据(帐户级别 0 的父级为空)

[
   "id": "45TYYU", "parent_id": null, "name":"account 1", "type": 1, "category": 1 ,
   "id": "45TYYXT", "parent_id": "45TYYU", "name":"account 2", "type": 1, "category": 1 ,
   "id": "45TYYPZ", "parent_id": "45TYYU", "name":"account 3", "type": 1, "category": 1 ,
   "id": "45TYYPZRE", "parent_id": "45TYYPZ", "name":"account 4", "type": 1, "category": 1 ,
   "id": "45TYYPZSX", "parent_id": "45TYYPZ", "name":"account 5", "type": 1, "category": 1 ,
   "id": "45TYYPZGP", "parent_id": "45TYYXT", "name":"account 6", "type": 1, "category": 1 
]

帐户 2 和帐户 3 是帐户 1 的子帐户,而帐户 4 和帐户 5 是帐户树的子帐户,帐户 6 是帐户 2 的子帐户...但是每个寄存器都处于相同的逻辑级别,仅通过 parent_id 标识。

所以我需要将这些数据转换成 GET 方法来重构它,如下所示:

[
     
        "id": "45TYYU",
        "parent_id": null,
        "name":"account 1",
        "type": 1,
        "category": 1,
        "children": [
             
                "id": "45TYYXT",
                "parent_id": "45TYYU",
                "name":"account 2",
                "type": 1,
                "category": 1,
                "children": [
                     "id": "45TYYPZGP", "parent_id": "45TYYXT", "name":"account 6", "type": 1, "category": 1 
                ]
            ,
             
                "id": "45TYYPZ",
                "parent_id": "45TYYU",
                "name":"account 3",
                "type": 1,
                "category": 1,
                "children": [
                     "id": "45TYYPZRE", "parent_id": "45TYYPZ", "name":"account 4", "type": 1, "category": 1 ,
                     "id": "45TYYPZSX", "parent_id": "45TYYPZ", "name":"account 5", "type": 1, "category": 1 
                ]
            
        ]
    ,
     
        "id": "45TFJK",
        "parent_id": null,
        "name":"account 7",
        "type": 1,
        "category": 1,
        "children": [
             
                "id": "47HJJT",
                "parent_id": "45TFJK",
                "name":"account 8",
                "type": 1,
                "category": 1
            ,
             
                "id": "47YHJU",
                "parent_id": "45TFJK",
                "name":"account 8",
                "type": 1,
                "category": 1
            
        ]
    
]

是的...父母级别 0 的 parent_id 为空,我想将其子项放在一个名为“children”的数组中,然后在 GET 响应中像这样发送给我的 UI

在 expressJS 中最好的方法是什么? 是否有允许我这样做的库或组件?

谢谢

【问题讨论】:

【参考方案1】:

您可以使用$graphLookup 和其他有用的数组运算符,

记录只有parent_id$match 过滤器是null $graphLookup 获取depthField中的子记录和深度号level $unwind 解构 children 数组并允许不移除空子节点 $sort 按深度级别字段 level 降序排列 $group by id 字段并重构 children 数组
db.collection.aggregate([
   $match:  parent_id: null  ,
  
    $graphLookup: 
      from: "collection",
      startWith: "$id",
      connectFromField: "id",
      connectToField: "parent_id",
      depthField: "level",
      as: "children"
    
  ,
  
    $unwind: 
      path: "$children",
      preserveNullAndEmptyArrays: true
    
  ,
   $sort:  "children.level": -1  ,
  
    $group: 
      _id: "$id",
      parent_id:  $first: "$parent_id" ,
      name:  $first: "$name" ,
      type:  $first: "$type" ,
      category:  $first: 1 ,
      children:  $push: "$children" 
    
  ,
$addFields 现在找到嵌套级别的子级并分配到它的级别, $reduce 以迭代 children 数组的循环。 初始化默认字段level默认值为-1,presentChild为[],prevChild为[]条件目的 $let 初始化字段: prev 根据条件如果level 相等则返回prevChild 否则返回presentChild current 根据条件如果level 相等则返回presentChild 否则[] in 从初始化字段返回 level 字段和 prevChild 字段 presentChild $filter children 来自prev 数组并返回,使用$mergeObjects 将当前对象与children 数组合并,并使用$concatArrayscurrent 数组合并 $addFields 只返回 presentChild 数组,因为我们只需要处理过的数组
  
    $addFields: 
      children: 
        $reduce: 
          input: "$children",
          initialValue:  level: -1, presentChild: [], prevChild: [] ,
          in: 
            $let: 
              vars: 
                prev: 
                  $cond: [
                     $eq: ["$$value.level", "$$this.level"] ,
                    "$$value.prevChild",
                    "$$value.presentChild"
                  ]
                ,
                current: 
                  $cond: [ $eq: ["$$value.level", "$$this.level"] , "$$value.presentChild", []]
                
              ,
              in: 
                level: "$$this.level",
                prevChild: "$$prev",
                presentChild: 
                  $concatArrays: [
                    "$$current",
                    [
                      
                        $mergeObjects: [
                          "$$this",
                          
                            children: 
                              $filter: 
                                input: "$$prev",
                                as: "e",
                                cond:  $eq: ["$$e.parent_id", "$$this.id"] 
                              
                            
                          
                        ]
                      
                    ]
                  ]
                
              
            
          
        
      
    
  ,
  
    $addFields: 
      id: "$_id",
      children: "$children.presentChild"
    
  
])

Playground

【讨论】:

以上是关于使用 Express 和 Mongo 制作带有递归表的节点树的主要内容,如果未能解决你的问题,请参考以下文章

通过 express、mongoose 和 angular 删除 mongo 文档时出现 404

使用mongo-express管理mongodb数据库

CVE-2019-10758-Mongo-express-远程代码执行

你能告诉我 express-mongo-sanitize 是不是有效吗?

架构:多个 Mongo 数据库+连接 vs 使用 Express 的多个集合

架构:多个 Mongo 数据库+连接 vs 使用 Express 的多个集合