跳到主要内容

findDescendants

查询树形结构实体的所有后代节点。

方法签名

findDescendants(options: FindTreeOptions): Promise<EntityType[]>

参数说明

FindTreeOptions

interface FindTreeOptions<T extends EntityType = any> {
/**
* 查询的根实体 ID
*/
entityId?: string;

/**
* 查询条件
*/
where?: RuleGroup<InstanceType<T>>;

/**
* 查询级别(深度)
* @default 0 表示查询所有层级
*/
level?: number;
}
参数类型必填默认值说明
entityIdstring-根节点的 ID,如果不指定则查询所有根节点的后代
whereRuleGroup-额外的过滤条件
levelnumber0查询深度,0 表示查询所有层级,1 表示只查询直接子节点,以此类推

基础用法

查询所有后代节点

import { Menu } from './entities/Menu';

// 查询指定菜单项的所有后代节点
const descendants = await Menu.findDescendants({
entityId: 'menu-root-id'
});

console.log('所有后代节点:', descendants);

限制查询深度

// 只查询直接子节点(第一层)
const directChildren = await Menu.findDescendants({
entityId: 'menu-root-id',
level: 1
});

// 查询两层深度的后代节点
const twoLevels = await Menu.findDescendants({
entityId: 'menu-root-id',
level: 2
});

使用过滤条件

// 查询所有启用状态的后代节点
const activeDescendants = await Menu.findDescendants({
entityId: 'menu-root-id',
where: {
combinator: 'and',
rules: [
{ field: 'enabled', operator: '=', value: true }
]
}
});

// 查询特定类型的后代节点
const folderDescendants = await Menu.findDescendants({
entityId: 'menu-root-id',
where: {
combinator: 'and',
rules: [
{ field: 'type', operator: '=', value: 'folder' }
]
},
level: 3 // 最多查询 3 层
});

响应式查询

使用 RxJS 订阅后代节点的实时变化:

import { Subscription } from 'rxjs';

// 订阅后代节点变化
const subscription: Subscription = Menu.findDescendants({
entityId: 'menu-root-id',
level: 2
}).subscribe({
next: (descendants) => {
console.log('后代节点更新:', descendants);
},
error: (err) => {
console.error('查询错误:', err);
}
});

// 取消订阅
subscription.unsubscribe();

实际应用场景

1. 渲染树形菜单

```typescript
@Entity({
type: 'tree',
name: 'Menu',
properties: [
{ name: 'name', type: PropertyType.string, required: true },
{ name: 'enabled', type: PropertyType.boolean, default: true },
{ name: 'icon', type: PropertyType.string }
]
})
export class Menu extends TreeEntityBase {}

// 获取某个菜单项下的所有子菜单
async function loadSubMenus(menuId: string) {
const subMenus = await Menu.findDescendants({
entityId: menuId,
where: {
combinator: 'and',
rules: [
{ field: 'enabled', operator: '=', value: true }
]
}
});

return subMenus;
}

2. 文件夹结构

@Entity({
type: 'tree',
name: 'Folder',
properties: [
{ name: 'name', type: PropertyType.string, required: true },
{ name: 'type', type: PropertyType.string }
]
})
export class Folder extends TreeEntityBase {}

// 获取文件夹下的所有文件
async function getAllFiles(folderId: string) {
const items = await Folder.findDescendants({
entityId: folderId,
where: {
combinator: 'and',
rules: [
{ field: 'type', operator: '=', value: 'file' }
]
}
});

return items;
}

// 只获取直接子项
async function getDirectChildren(folderId: string) {
const children = await Folder.findDescendants({
entityId: folderId,
level: 1
});

return children;
}

3. 组织架构

```typescript
@Entity({
type: 'tree',
name: 'Department',
properties: [
{ name: 'name', type: PropertyType.string, required: true },
{ name: 'employeeCount', type: PropertyType.number }
]
})
export class Department extends TreeEntityBase {}

// 获取某个部门下的所有子部门
async function getAllSubDepartments(deptId: string) {
const subDepts = await Department.findDescendants({
entityId: deptId
});

// 计算总人数
const totalEmployees = subDepts.reduce(
(sum, dept) => sum + dept.employeeCount,
0
);

return {
departments: subDepts,
totalEmployees
};
}

性能优化

1. 限制查询深度

对于大型树形结构,避免一次性查询所有后代节点:

// ❌ 不推荐:查询所有层级可能很慢
const allDescendants = await Menu.findDescendants({
entityId: rootId
});

// ✅ 推荐:按需加载,限制深度
const firstLevel = await Menu.findDescendants({
entityId: rootId,
level: 1
});

// 用户展开时再加载下一层
const secondLevel = await Menu.findDescendants({
entityId: selectedNodeId,
level: 1
});

2. 使用过滤条件减少数据量

// 只查询需要的节点
const visibleDescendants = await Menu.findDescendants({
entityId: rootId,
where: {
combinator: 'and',
rules: [
{ field: 'enabled', operator: '=', value: true },
{ field: 'visible', operator: '=', value: true }
]
}
});

3. 缓存查询结果

const cache = new Map<string, Menu[]>();

async function getCachedDescendants(menuId: string) {
if (cache.has(menuId)) {
return cache.get(menuId)!;
}

const descendants = await Menu.findDescendants({
entityId: menuId,
level: 2
});

cache.set(menuId, descendants);
return descendants;
}

注意事项

  1. 循环引用:确保树形结构没有循环引用,否则可能导致无限递归
  2. 性能考虑:对于大型树形结构,避免一次性查询所有层级
  3. 数据一致性:后代节点的查询结果会随着树结构的变化而更新
  4. level 参数level: 0 表示查询所有层级,level: 1 表示只查询直接子节点
  5. 实体定义:只能在使用 @TreeEntity 装饰器定义的实体上使用此方法

相关方法

参考