mongodb 用Express和Mongo制作递归表节点树

relj7zay  于 2023-02-18  发布在  Go
关注(0)|答案(2)|浏览(106)

我正在使用一个带有ExpressJS和Mongo的REST API,我有一个包含N个级别的集合。
为了解决这个问题,我在mongo中使用了一个递归表(或集合),其中一个字段是id,每个寄存器都有一个parent_id,它和它的查尔兹处于同一级别。

所以当你看到的时候,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中做这件事的最好方法是什么?有没有一个库或组件允许我做这件事?
谢谢

jvidinwx

jvidinwx1#

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

  • 记录仅具有parent_id$match筛选器为null
  • $graphLookup以获取子记录和depthField中的深度编号level
  • $unwind解构children数组并允许不删除空子级
  • $sort乘以深度水平场level,按降序排列
  • $group乘以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字段
  • prev数组中的presentChild$filterchildren并返回,使用$mergeObjects将当前对象与children数组合并,并使用$concatArrays与let的current数组连接
  • $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

0ejtzxu1

0ejtzxu12#

@turivishal我在后端节点中使用相同的架构我只显示null对象,而不是使用相同聚合的父子关系

this.tickets.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: {
            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: {
            children: "$children.presentChild"
          }
        }
      ]).then((result) => {
        
        console.log('test',result);
        
        // callback(result);

    }).catch((error) => {
        callback(error);
    });

输出:

[

{标识号:'45TYYU',父标识号:空,名称:"帐户1",类型:1、类别:1、儿童:[]},{_ ID:'45TYYUA',父代标识:空,名称:"帐户1",类型:1、类别:1、儿童:[]

相关问题