findAncestors
查询树形结构实体的所有祖先节点。
方法签名
findAncestors(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 ancestors = await Menu.findAncestors({
entityId: 'menu-child-id'
});
console.log('所有祖先节点:', ancestors);
限制查询深度
// 只查询直接父节点
const parent = await Menu.findAncestors({
entityId: 'menu-child-id',
level: 1
});
// 查询两层祖先节点
const twoLevels = await Menu.findAncestors({
entityId: 'menu-child-id',
level: 2
});
使用过滤条件
// 查询所有启用状态的祖先节点
const activeAncestors = await Menu.findAncestors({
entityId: 'menu-child-id',
where: {
combinator: 'and',
rules: [
{ field: 'enabled', operator: '=', value: true }
]
}
});
// 查询特定类型的祖先节点
const folderAncestors = await Menu.findAncestors({
entityId: 'menu-child-id',
where: {
combinator: 'and',
rules: [
{ field: 'type', operator: '=', value: 'folder' }
]
}
});
响应式查询
使用 RxJS 订阅祖先节点的实时变化:
import { Subscription } from 'rxjs';
// 订阅祖先节点变化
const subscription: Subscription = Menu.findAncestors({
entityId: 'menu-child-id'
}).subscribe({
next: (ancestors) => {
console.log('祖先节点更新:', ancestors);
},
error: (err) => {
console.error('查询错误:', err);
}
});
// 取消订阅
subscription.unsubscribe();
实际应用场景
1. 面包屑导航
@Entity({
type: 'tree',
name: 'Menu',
properties: [
{ name: 'name', type: PropertyType.string, required: true },
{ name: 'path', type: PropertyType.string }
]
})
export class Menu extends TreeEntityBase {}
// 生成面包屑导航
async function generateBreadcrumb(currentMenuId: string) {
const ancestors = await Menu.findAncestors({
entityId: currentMenuId
});
// 祖先节点从根到父,需要反转顺序
const breadcrumb = [...ancestors.reverse(), await Menu.get(currentMenuId)];
return breadcrumb.map(menu => ({
name: menu.name,
path: menu.path
}));
}
// 使用示例
const breadcrumb = await generateBreadcrumb('menu-leaf-id');
// 结果: [
// { name: '首页', path: '/' },
// { name: '产品', path: '/products' },
// { name: '详情', path: '/products/detail' }
// ]
2. 权限继承检查
@Entity({
type: 'tree',
name: 'Department',
properties: [
{ name: 'name', type: PropertyType.string, required: true },
{ name: 'hasSpecialPermission', type: PropertyType.boolean, default: false }
]
})
export class Department extends TreeEntityBase {}
// 检查当前部门或其祖先部门是否有特殊权限
async function hasInheritedPermission(deptId: string): Promise<boolean> {
const ancestors = await Department.findAncestors({
entityId: deptId,
where: {
combinator: 'and',
rules: [
{ field: 'hasSpecialPermission', operator: '=', value: true }
]
}
});
return ancestors.length > 0;
}
3. 文件路径生成
@Entity({
type: 'tree',
name: 'Folder',
properties: [
{ name: 'name', type: PropertyType.string, required: true }
]
})
export class Folder extends TreeEntityBase {}
// 生成完整文件路径
async function getFullPath(folderId: string): Promise<string> {
const ancestors = await Folder.findAncestors({
entityId: folderId
});
const folder = await Folder.get(folderId);
// 从根到当前节点
const pathParts = [
...ancestors.reverse().map(f => f.name),
folder.name
];
return '/' + pathParts.join('/');
}
// 使用示例
const path = await getFullPath('folder-id');
// 结果: '/root/documents/projects/2024'
4. 层级展开状态管理
// 获取需要展开的所有父节点 ID
async function getExpandedNodeIds(currentNodeId: string): Promise<string[]> {
const ancestors = await Menu.findAncestors({
entityId: currentNodeId
});
return ancestors.map(node => node.id);
}
// 在树形组件中使用
async function expandToNode(nodeId: string) {
const ancestorIds = await getExpandedNodeIds(nodeId);
// 设置所有祖先节点为展开状态
ancestorIds.forEach(id => {
expandNode(id);
});
// 选中当前节点
selectNode(nodeId);
}
5. 组织架构层级
@Entity({
type: 'tree',
name: 'Department',
properties: [
{ name: 'name', type: PropertyType.string, required: true },
{ name: 'level', type: PropertyType.string } // 如: 'L1', 'L2', 'L3'
]
})
export class Department extends TreeEntityBase {}
// 获取部门的完整层级信息
async function getDepartmentHierarchy(deptId: string) {
const ancestors = await Department.findAncestors({
entityId: deptId
});
const current = await Department.get(deptId);
return {
current,
ancestors: ancestors.reverse(),
depth: ancestors.length,
rootDepartment: ancestors[0] || current
};
}
性能优化
1. 限制查询深度
对于深层树形结构,限制查询深度可以提高性能:
// ❌ 不推荐:查询所有祖先可能很慢
const allAncestors = await Menu.findAncestors({
entityId: nodeId
});
// ✅ 推荐:只查询必要的层级
const recentAncestors = await Menu.findAncestors({
entityId: nodeId,
level: 3 // 只查询 3 层
});
2. 缓存查询结果
const ancestorCache = new Map<string, Menu[]>();
async function getCachedAncestors(menuId: string) {
if (ancestorCache.has(menuId)) {
return ancestorCache.get(menuId)!;
}
const ancestors = await Menu.findAncestors({
entityId: menuId
});
ancestorCache.set(menuId, ancestors);
return ancestors;
}
// 数据更新时清除缓存
function clearAncestorCache() {
ancestorCache.clear();
}
3. 批量查询优化
// ❌ 不推荐:逐个查询
const allAncestors = [];
for (const nodeId of nodeIds) {
const ancestors = await Menu.findAncestors({ entityId: nodeId });
allAncestors.push(ancestors);
}
// ✅ 推荐:并行查询
const allAncestors = await Promise.all(
nodeIds.map(nodeId =>
Menu.findAncestors({ entityId: nodeId })
)
);
返回结果顺序
findAncestors 返回的祖先节点顺序是从最近的父节点到根节点:
const ancestors = await Menu.findAncestors({
entityId: 'leaf-node-id'
});
// ancestors 顺序:
// [0] 直接父节点
// [1] 父节点的父节点
// [2] 父节点的父节点的父节点
// ...
// [n] 根节点
// 如果需要从根到父的顺序,使用 reverse()
const rootToParent = ancestors.reverse();
与其他方法的组合使用
获取完整的节点路径
// 获取从根到当前节点的完整路径
async function getNodePath(nodeId: string) {
const ancestors = await Menu.findAncestors({ entityId: nodeId });
const current = await Menu.get(nodeId);
// 从根到当前节点
return [...ancestors.reverse(), current];
}
查找共同祖先
// 查找两个节点的最近共同祖先
async function findCommonAncestor(nodeId1: string, nodeId2: string) {
const ancestors1 = await Menu.findAncestors({ entityId: nodeId1 });
const ancestors2 = await Menu.findAncestors({ entityId: nodeId2 });
const ids1 = new Set(ancestors1.map(a => a.id));
// 找到第一个共同的祖先
for (const ancestor of ancestors2) {
if (ids1.has(ancestor.id)) {
return ancestor;
}
}
return null;
}
注意事项
- 返回顺序:祖先节点按照从父到根的顺序返回
- 根节点:根节点没有祖先,返回空数组
- 性能考虑:对于深层树形结构,考虑限制查询深度
- level 参数:
level: 0查询所有祖先,level: 1只查询直接父节点 - 实体定义:只能在使用
@TreeEntity装饰器定义的实体上使用此方法
相关方法
- countAncestors - 统计祖先节点数量
- findDescendants - 查询后代节点
- countDescendants - 统计后代节点数量