findDescendants
查询树形结构实体的所有后代节点。
方法签名
findDescendants(options: FindTreeOptions): Promise<EntityType[]>
参数说明
FindTreeOptions
interface FindTreeOptions<T extends EntityType = any> {
/**
* 查询的根实体 ID
*/
entityId?: string;
/**
* 查询条件
*/
where?: RuleGroup<InstanceType<T>>;
/**
* 查询级别(深度)
* @default 0 表示查询所有层级
*/
level?: number;
}
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| entityId | string | 否 | - | 根节点的 ID,如果不指定则查询所有根节点的后代 |
| where | RuleGroup | 否 | - | 额外的过滤条件 |
| level | number | 否 | 0 | 查询深度,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;
}
注意事项
- 循环引用:确保树形结构没有循环引用,否则可能导致无限递归
- 性能考虑:对于大型树形结构,避免一次性查询所有层级
- 数据一致性:后代节点的查询结果会随着树结构的变化而更新
- level 参数:
level: 0表示查询所有层级,level: 1表示只查询直接子节点 - 实体定义:只能在使用
@TreeEntity装饰器定义的实体上使用此方法
相关方法
- countDescendants - 统计后代节点数量
- findAncestors - 查询祖先节点
- countAncestors - 统计祖先节点数量