跳到主要内容

findDescendants

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

方法签名

findDescendants(options: FindTreeOptions): Observable<InstanceType<T>[]>

参数说明

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';
import { firstValueFrom } from 'rxjs';

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

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

限制查询深度

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

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

使用过滤条件

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

// 查询特定类型的后代节点
const folderDescendants = await firstValueFrom(
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. 渲染树形菜单

@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 firstValueFrom(
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 firstValueFrom(
Folder.findDescendants({
entityId: folderId,
where: {
combinator: 'and',
rules: [{ field: 'type', operator: '=', value: 'file' }]
}
})
);

return items;
}

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

return children;
}

3. 组织架构

@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 firstValueFrom(
Department.findDescendants({
entityId: deptId
})
);

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

return {
departments: subDepts,
totalEmployees
};
}

性能优化

1. 限制查询深度

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

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

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

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

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

// 只查询需要的节点
const visibleDescendants = await firstValueFrom(
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 firstValueFrom(
Menu.findDescendants({
entityId: menuId,
level: 2
})
);

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

注意事项

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

相关方法

参考