rxdb-plugin-tree
Implements: US-025 核心插件化拆分
RxDB 树形结构插件:邻接表模型的实体基类、装饰器、TreeRepository,以及四个树查询的真增量 merge。
这套能力曾经内置在 @aiao/rxdb。核心里躺着 4 个树 task 类型、3 个 merge switch 的树分支和约 1100 行树专属合并逻辑——它们从永远加载的 switch 里可达,tree-shaking 甩不掉,不用树的应用照样付这份体积。外移之后,装插件才有树。
安装
pnpm add @aiao/rxdb @aiao/rxdb-adapter-wa-sqlite @aiao/rxdb-plugin-tree
使用
import { PropertyType, RxDB, SyncType } from '@aiao/rxdb';
import { RxDBAdapterWaSqlite } from '@aiao/rxdb-adapter-wa-sqlite';
import { TreeAdjacencyListEntityBase, TreeEntity, rxDBPluginTree } from '@aiao/rxdb-plugin-tree';
@TreeEntity({
name: 'Category',
properties: [{ name: 'name', type: PropertyType.string }],
features: `{ tree: { hasChildren: true } }`
})
class Category extends TreeAdjacencyListEntityBase {
name!: string;
}
const rxdb = new RxDB({
dbName: 'catalog',
entities: [Category],
sync: `{ type: SyncType.None, local: { adapter: 'wa-sqlite' } }`
});
// 必须在 init() 之前 use():仓储注册发生在 init() 内部,早于实体元数据校验。
rxdb.use(rxDBPluginTree).adapter('wa-sqlite', db => new RxDBAdapterWaSqlite(db, { vfs: 'MemoryAsyncVFS' }));
await rxdb.connect('wa-sqlite');
rxdb.init();
Category.findDescendants({ entityId: root.id, level: 2 }).subscribe(nodes => {
console.log(nodes.length);
});
不装插件而实体声明了 repository: 'TreeRepository'(@TreeEntity 会自动写上),init() 直接抛错并列出当前已注册的仓储名。
公开面
| 分类 | 符号 |
|---|---|
| 插件 | RxDBPluginTree、rxDBPluginTree |
| 实体 | TreeAdjacencyListEntityBase、TREE_ADJACENCY_LIST_ENTITY_BASE_OPTIONS、@TreeEntity |
| 类型 | ITreeEntity、ISortableTreeEntity、TreeEntityType、EntityMetadataTreeFeatures |
| 仓储 | TreeRepository、ITreeRepository、FindTreeOptions |
| 查询 | FindDescendantsQuery、FindAncestorsQuery、CountDescendantsQuery、CountAncestorsQuery |
四个查询 task 类型经 RepositoryQueryExtensions 模块增强挂进核心的 QueryOptions 联合——这是插件给核心闭合联合加分支的唯一缝。
查询语义
level 包含当前节点本身:level: 0 只有当前节点,level: 1 是当前节点 + 直接子节点。数字以外的值(字符串、小数、NaN)在适配器层直接抛 RxDBError,没有兜底。
findDescendants/countDescendants:指定entityId时不含当前节点;不指定时覆盖所有根节点及其后代。findAncestors/countAncestors:指定entityId时含当前节点。
增量 merge
树查询与图查询不同:图查询一律回 SQL refresh(),树查询走真增量。TreeRepository 构造时按 task 类型注册 merge_create / merge_update / merge_remove,三道前置守卫之后才进增量路径:
task.result === undefined→ refresh;- 事件里有过期实体(
isStaleEntityEvent)→ refresh; findAncestors且parentId变过 → refresh(祖先链已被改写,增量无从谈起)。
find* 走 recalculate,count* 走 refresh。用到的 merge 原语(prepareIncrementalUpdate / applyExternalEntityUpdate / getEntityId 等)由 @aiao/rxdb 公开导出,进基线后受兼容承诺约束;classifyUpdates 与 invalidateEntityFingerprint 是核心内部装配细节,不在公开面上。
连接纪元
插件声明 lifecycle: 'scoped',唯一的宿主改动——注册 TreeRepository——登记在 install(scope) 收到的作用域上,disconnectAll() 时随作用域一起撤销。重新 connect() 会重新注册。
框架绑定
四个树查询 hook 在各自的框架包里,三端同名同形:
- Angular:
@aiao/rxdb-plugin-tree-angular - React:
@aiao/rxdb-plugin-tree-react - Vue:
@aiao/rxdb-plugin-tree-vue
适配器
TreeRepository 的名字没变,适配器仍按字符串 'TreeRepository' 分发,公开 API 一字未动。PGlite / SQLite / Supabase 三家的 *TreeRepository 只把类型来源改到了本包。
迁移
从内置树升级见 tree-split 迁移说明。
开发命令
pnpm nx test rxdb-plugin-tree # node 环境,只跑构建期生成器 spec
pnpm nx run rxdb-plugin-tree:test-browser # 先跑 node 趟(依赖 coverage target),再在真实 chromium 里跑运行时 spec
vite.config.mts 按 VITEST_BROWSER 环境变量把测试拆成两趟:不设该变量时(nx test 走的路径)排除全部 *.browser.spec.ts,只剩 src/__tests__/generator/ 下的构建期生成器 spec;查询 / 合并 / 仓储等运行时 spec 都以 .browser.spec.ts 结尾,只有 VITEST_BROWSER=true(即 test-browser)才会执行。
所以 pnpm nx test rxdb-plugin-tree --watch 做 TDD 会静默跳过全部运行时 spec——改了查询 / 合并 / 仓储代码,红没红只有 test-browser 看得到。
License
RxDB Tree 插件 树形结构插件,提供邻接表模型的实体基类、装饰器、仓储与增量查询合并
主要功能:
- 树形实体基类与装饰器(
TreeAdjacencyListEntityBase/@TreeEntity) - 四个树查询(
findDescendants/countDescendants/findAncestors/countAncestors) - 树查询的真增量 merge(不像图查询一律回 SQL 刷新)
Classes
| Class | Description |
|---|---|
| RxDBPluginTree | 树形结构插件。 |
| TreeAdjacencyListEntityBase | 树形实体装饰器配置 定义了树形结构所需的父子关系 |
| TreeRepository | 树结构实体仓库 根据配置决策实体的具体操作 |
Interfaces
| Interface | Description |
|---|---|
| CountAncestorsQuery | CountAncestors 查询任务 统计指定实体的祖先节点数量 |
| CountDescendantsQuery | CountDescendants 查询任务 统计指定实体的后代节点数量 |
| EntityMetadataTreeFeatures | 实体元数据树形结构特性接口 |
| FindAncestorsQuery | FindAncestors 查询任务 查找指定实体的所有祖先节点 |
| FindDescendantsQuery | FindDescendants 查询任务 查找指定实体的所有后代节点 |
| FindTreeOptions | 树结构查询配置 |
| ISortableTreeEntity | 支持手动排序的树形实体接口。 |
| ITreeEntity | 实体基础接口 定义所有实体共有的基本属性,为了能更好地与远程数据同步 所有实体类都应该实现这个接口 |
| ITreeRepository | 树结构仓库接口 |
Type Aliases
| Type Alias | Description |
|---|---|
| TreeEntityType | 树形实体类型 |
| TreeQuery | 四支树查询任务的联合。 |
| TreeQueryType | 树查询任务类型的联合,由 TREE_QUERY_TYPE_LIST 派生。 |
Variables
| Variable | Description |
|---|---|
| rxDBPluginTree | RxDBPluginTree 的工厂,交给 RxDB.use()。 |
| TREE_ADJACENCY_LIST_ENTITY_BASE_OPTIONS | TreeAdjacencyListEntityBase 的 @Entity 元数据。 |
| TREE_QUERY_TYPE_LIST | 树查询任务类型的单一来源。 |
| TREE_QUERY_TYPES | 树查询任务类型集合 |
| TreeEntity | 树形实体装饰器 用于将类标记为树形结构实体,并处理树形特定的元数据 |
Functions
| Function | Description |
|---|---|
| assertTreeLevel | 校验树查询深度,未提供时保留不限深度的语义。 |