Prisma递归关联查询如何获取Group模型下全层级子分组数据?
Prisma自关联模型全层级子分组查询方案
首先纠正你的推测:该单层返回结果并非Prisma的无限循环保护机制,只是include: { Subgroups: true }默认仅加载第一层关联数据,该限制完全可以突破,可根据业务场景选择以下三种实现方案:
方案1:固定层级嵌套include
适用于提前知道分组最大层级的场景,写法最简单,直接嵌套声明需要加载的Subgroups深度即可:
// 示例为加载3层级子分组,可根据实际业务调整嵌套深度 GroupModel.findFirst({ where: { id: _id }, include: { Subgroups: { include: { Subgroups: { include: { Subgroups: true } } } } } });
- 优点:无需额外逻辑,代码改动量最小
- 缺点:仅支持固定深度的查询,无法适配层级不确定的场景
方案2:递归查询拼装数据
适用于分组层级不确定的场景,通过递归函数批量查询所有层级子分组后拼装为树结构:
async function getGroupWithAllSubgroups(groupId) { // 查询当前层级分组基础信息及直属子分组 const group = await GroupModel.findFirst({ where: { id: groupId }, include: { Subgroups: true } }); // 递归终止条件:分组不存在/没有子分组 if (!group || group.Subgroups.length === 0) return group; // 递归遍历所有子分组,加载其下级分组 group.Subgroups = await Promise.all( group.Subgroups.map(subgroup => getGroupWithAllSubgroups(subgroup.id)) ); return group; } // 调用方法 const fullGroupTree = await getGroupWithAllSubgroups(_id);
- 优点:支持任意深度的递归查询,无需提前感知分组层级
- 缺点:会发起多次数据库查询请求,分组量级较大时性能损耗明显
方案3:原生SQL递归查询(性能最优)
适用于数据量级较大的场景,通过数据库原生支持的递归CTE(公用表表达式)一次查询拿到所有层级分组数据,再自行转换为嵌套结构即可,以下为PostgreSQL语法示例:
const flatGroupList = await GroupModel.$queryRaw` WITH RECURSIVE GroupHierarchy AS ( -- 锚点查询:匹配目标根分组 SELECT id, "parentId" -- 可补充其他需要返回的Group字段 FROM "Group" WHERE id = ${_id} UNION ALL -- 递归查询:匹配所有子层级分组 SELECT g.id, g."parentId" FROM "Group" g INNER JOIN GroupHierarchy gh ON g."parentId" = gh.id ) SELECT * FROM GroupHierarchy; `;
查询结果为扁平化的全量分组列表,自行编写树结构转换函数即可得到嵌套格式的分组树。
- 优点:仅发起一次数据库查询,性能最优,支持任意深度查询
- 缺点:需要编写原生SQL,不同数据库的递归CTE语法存在细微差异,需要适配当前使用的数据库类型
内容的提问来源于stack exchange,提问作者Hugo Bois
相关产品推荐
相关产品推荐

