如何用GraphQL与NestJS将部门数据解析为动态树形结构?
NestJS实现扁平化部门JSON转多级GraphQL树形结构
需求说明
给定扁平化的部门JSON数据:
{ "departments": [ { "id": 1, "name": "department 1", "parent_id": "" }, { "id": 2, "name": "department 2", "parent_id": "1" }, { "id": 3, "name": "department 3", "parent_id": "1" }, { "id": 4, "name": "department 4", "parent_id": "2" } ] }
需要转换为带多级子节点的GraphQL树形结构:
{ "id": 1, "name": "department 1", "children": [ { "id": 2, "name": "department 2", "children": [ { "id": 4, "name": "department 4" } ] }, { "id": 3, "name": "department 3" } ] }
且部门规模庞大,需支持动态多级子节点结构。
解决方案
1. 定义GraphQL ObjectType
使用@ObjectType装饰器递归定义部门类型,通过延迟函数() => [DepartmentType]避免循环引用问题:
import { ObjectType, Field, Int } from '@nestjs/graphql'; @ObjectType() export class DepartmentType { @Field(() => Int) id: number; @Field() name: string; @Field(() => [DepartmentType], { nullable: true }) children?: DepartmentType[]; }
2. 实现扁平化转树形结构的工具函数
通过Map存储部门实现O(1)查找,仅遍历两次数据完成树形构建,适合大规模数据处理:
import { DepartmentType } from './department.type'; // 定义原始部门数据类型 type RawDepartment = { id: number; name: string; parent_id: string; }; export function convertToTree(rawDepartments: RawDepartment[]): DepartmentType[] { const departmentMap = new Map<number, DepartmentType>(); const rootDepartments: DepartmentType[] = []; // 初始化所有部门节点并存入Map rawDepartments.forEach(dept => { departmentMap.set(dept.id, { id: dept.id, name: dept.name, children: [], }); }); // 挂载子节点到对应父节点 rawDepartments.forEach(dept => { const parentId = dept.parent_id ? parseInt(dept.parent_id, 10) : null; if (parentId === null) { rootDepartments.push(departmentMap.get(dept.id)!); } else { const parentDept = departmentMap.get(parentId); parentDept?.children?.push(departmentMap.get(dept.id)!); } }); return rootDepartments; }
3. 在GraphQL Resolver中使用
在Resolver中调用转换函数,返回树形结构数据:
import { Query, Resolver } from '@nestjs/graphql'; import { DepartmentType } from './department.type'; import { convertToTree } from './department.util'; import { DepartmentService } from './department.service'; @Resolver(() => DepartmentType) export class DepartmentResolver { constructor(private readonly departmentService: DepartmentService) {} @Query(() => [DepartmentType]) async departmentsTree() { // 获取原始扁平化部门数据 const rawDepartments = await this.departmentService.getRawDepartments(); return convertToTree(rawDepartments); } }
内容的提问来源于stack exchange,提问作者Jingqiang Zhang
相关产品推荐
相关产品推荐

