You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js实现数据库层级化分组数据获取的问题求助

解决MongoDB分组树形层级结构构建问题

需求回顾

需要从MongoDB中获取层级化分组数据,格式要求如下:

data = [{ 
  name: "Admin", 
  id: '1', 
  children: [ 
    { name: "Admin", id: "1" }, 
    { name: "groupe1", id: "2" }, 
    { name: "groupe2", id: "1455", children: [ 
      { name: "groupe2", id: "1455" }, 
      { name: "gro", id: "5444" }, 
      { name: "hhrr", id: "45" } 
    ] } 
  ] 
}]

核心逻辑:

  • 每个分组存在父分组,需构建树形层级
  • 若分组是父分组(包含子分组),则在父分组的children数组中添加自身信息,并为该分组添加children属性继续递归子节点
  • 若仅为子分组,直接添加到父分组的children数组中

原代码问题分析

  1. 多次嵌套数据库查询:每次递归都执行两次查询,性能低下且容易引发异步顺序问题
  2. 递归参数错误:调用findParent时传入parentResult.children[i],但parentResult是数组,不存在children属性
  3. 冗余逻辑:重复查询子节点判断是否为父分组,浪费资源
  4. 位置追踪混乱:itemPos变量的使用逻辑不清晰,完全没必要

优化解决方案

实现思路

  1. 一次性拉取所有有效分组:减少数据库IO操作,提升性能
  2. 构建分组映射表:用Map存储所有分组,实现O(1)时间复杂度的查找
  3. 递归构建树形结构:从根节点出发,递归处理每个节点,按照需求生成层级结构

完整代码实现

const getHierarchicalGroups = async ({ name, _id }) => {
  // 1. 一次性获取所有启用状态的分组
  const allGroups = await Models.GroupModel.find({ status: true }).select('name _id parent');
  
  // 2. 构建分组映射表,key为分组_id的字符串形式,value为分组对象
  const groupMap = new Map();
  allGroups.forEach(group => {
    groupMap.set(group._id.toString(), group);
  });

  // 3. 递归构建树形结构的核心函数
  const buildTree = (groupId) => {
    const group = groupMap.get(groupId);
    if (!group) return null;

    // 筛选当前分组的所有直接子分组
    const childGroups = allGroups.filter(g => g.parent?.toString() === groupId);

    // 构建当前节点的基础结构
    const node = {
      id: group._id.toString(),
      name: group.name
    };

    // 如果当前分组有子分组,生成children数组(先放入自身,再递归子节点)
    if (childGroups.length > 0) {
      node.children = [
        { id: group._id.toString(), name: group.name },
        ...childGroups.map(child => buildTree(child._id.toString()))
      ];
    }

    return node;
  };

  // 从传入的根节点开始构建,返回数组格式的结果
  return [buildTree(_id.toString())];
};

代码说明

  • 一次性查询:仅执行一次数据库查询,获取所有status: true的分组,避免多次IO开销
  • 映射表优化:使用Map存储分组,快速通过_id查找对应的分组对象,提升查找效率
  • 递归逻辑:
    • 对于有子分组的节点,先在children数组中放入自身信息,再递归处理每个子节点
    • 无子女分组的节点直接返回基础结构,会被父节点的children数组包含
  • 类型处理:将ObjectId转为字符串,避免MongoDB对象ID与字符串匹配问题

调用示例

// 传入根分组的name和_id,获取层级结构数据
const result = await getHierarchicalGroups({ name: "Admin", _id: "1" });
console.log(result);

内容的提问来源于stack exchange,提问作者Mohamed Borhene Hamedi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 14:20:22