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

如何在TypeORM v0.3.10中递归查询实体的子节点

解决方案:TypeORM v0.3.10 递归加载所有子节点(含自身)

方法一:使用TypeORM Tree实体(推荐)

你的实体目前缺少Tree相关装饰器配置,补充后可直接借助内置方法实现递归查询:

1. 修改实体定义

import { Entity, PrimaryGeneratedColumn, Column, Tree, TreeParent, TreeChildren } from "typeorm";
import { TableNamesConstants } from "./your-path"; // 替换为实际路径

@Entity(TableNamesConstants.GROUPS)
@Tree("closure-table") // 采用闭包表模式,适配递归查询场景
export class Group {
  @PrimaryGeneratedColumn()
  id: number;

  @Column('int', { nullable: true })
  parentId?: number;

  @TreeParent() // 标记父节点关联
  @ManyToOne(() => Group, { nullable: true, onDelete: "CASCADE" })
  @JoinColumn({ name: 'parentId' })
  parent?: Group;

  @TreeChildren() // 标记子节点集合
  children: Group[];
}

注意:使用闭包表模式需先运行迁移命令,TypeORM会自动生成对应的group_closure闭包表。

2. 查询所有子节点(含自身)

通过TreeRepository的findDescendants方法,直接获取当前节点及所有层级的子节点:

import { getRepository } from "typeorm";
import { Group } from "./your-path";

async function getGroupAndAllDescendants(groupId: number) {
  const treeRepository = getRepository(Group).extend({});
  const group = await treeRepository.findOne({ where: { id: groupId } });
  if (!group) return [];
  
  // 获取当前节点及所有后代
  const descendants = await treeRepository.findDescendants(group);
  // 提取ID列表
  return descendants.map(item => item.id);
}

// 调用示例
getGroupAndAllDescendants(1).then(ids => console.log(ids)); // 输出 [1,2,3]
getGroupAndAllDescendants(2).then(ids => console.log(ids)); // 输出 [2,3]

方法二:使用原生SQL CTE(公共表表达式)

若Tree实体模式不适用,可直接用递归CTE编写原生查询,通过TypeORM QueryBuilder执行:

import { getConnection } from "typeorm";
import { Group } from "./your-path";

async function getGroupAndAllDescendants(groupId: number) {
  const result = await getConnection()
    .createQueryBuilder()
    .withRecursive("cte", () => {
      return this.select("id", "parentId")
        .from(Group, "g")
        .where("g.id = :groupId", { groupId })
        .unionAll(
          this.select("child.id", "child.parentId")
            .from(Group, "child")
            .innerJoin("cte", "parent", "parent.id = child.parentId")
        );
    })
    .select("c.id")
    .from("cte", "c")
    .getRawMany();

  // 提取ID列表
  return result.map(item => item.id);
}

// 调用示例
getGroupAndAllDescendants(1).then(ids => console.log(ids)); // 输出 [1,2,3]
getGroupAndAllDescendants(2).then(ids => console.log(ids)); // 输出 [2,3]

注意:需确保你的数据库版本支持递归CTE(如MySQL 8.0+、PostgreSQL等)。

常见问题排查

  • 使用Tree实体查询为空时,检查是否已运行迁移生成闭包表,以及实体关联配置是否正确。
  • 使用CTE时,确认传入的groupId存在,且数据库兼容递归语法。

内容的提问来源于stack exchange,提问作者Yoshi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 05:20:41