FlexTree
Before this section, we have been using FlexTreeManager for the examples. From this section on, we introduce FlexTree, an object focused on querying.
TIP
FlexTree supports a single root only. For multi-root trees, use the analogous MultiRootFlexTree — its API is essentially identical, differing only in being multi-root.
Since FlexTree is based on the Left-Right Value algorithm, which is a read-optimized storage structure, it has high query efficiency but lower update efficiency.
So in principle it is especially suited for scenarios where reads outnumber updates. To make it easier to work with the tree, the FlexTree object and the node object FlexTreeNode are introduced.
Creating the Tree Object
FlexTree is a class dedicated to loading a tree into memory and providing more convenient tree APIs.
import type { FlexTreeOptions, IFlexTreeNode } from 'flextree'
import { FlexTreeManager,FlexTree, FlexTreeVerifyError } from 'flextree'
import SqliteAdapter from 'flextree-sqlite-adapter'
const sqliteDriver = new SqliteAdapter()
await sqliteDriver.open()
const tree = new FlexTree('tree', {
adapter: sqliteDriver,
})
await tree.load()Object Tree
After the FlexTree object is loaded, it builds a nested tree of object instances composed of FlexTreeNodes, as follows:
- FlexTreeNodeRoot
- *children[]
- FlexTreeNodeA
- *children[]
- FlexTreeNodeA1
- FlexTreeNodeA2
- FlexTreeNodeA3
- *children[]
- FlexTreeNodeB
- *children[]
- FlexTreeNodeB1
- FlexTreeNodeB2
- FlexTreeNodeB3
- FlexTreeNodeB
- *children[]
- FlexTreeNodeC
- *children[]
- FlexTreeNodeC1
- FlexTreeNodeC2
- FlexTreeNodeC3
- *children[]
- FlexTreeNodeA
- *children[]
Generics
Because FlexTree internally creates a FlexTreeManager object automatically when instantiated, its generics are the same as those of FlexTreeManager.
export class FlexTree<
Fields extends Record<string, any> = object,
KeyFields extends CustomTreeKeyFields = DefaultTreeKeyFields
>The way to customize key fields is the same as well, as follows:
import { FlexTree } from "felxtree"
import PrismaAdapter from "flextree-prisma-adapter"
const tree = new FlexTree<{
size: number
},
{
id:['pk',number],
treeId:['tree',number],
name:"title",
leftValue:'lft',
rightValue:'rgt'
}>('org', {
adapter: new PrismaAdapter(prisma),
fields:{
id:'pk',
treeId:'tree',
name:'title',
leftValue:'lft',
rightValue:"rgt"
}
})See the FlexTreeManager introduction.
Loading
Execute FlexTree.load to load the tree from the database into memory.
Full Load
const tree = new FlexTree('tree')
console.log(tree.status) // == not-loaded
// Load the tree into memory all at once
await tree.load()
console.log(tree.status) // == loaded
// Get the root FlexNode instance
tree.rootLazy Loading
If the tree has too many nodes, you can also enable lazy loading to manually control which nodes are loaded
const tree = new FlexTree('tree',{
lazy:true
})
await tree.load()In lazy loading mode, the above code only loads the root node and its children; the object tree is as follows:
- FlexTreeNodeRoot
- *children[][]
- FlexTreeNodeA
- children[]length=0
- FlexTreeNodeA1Not loaded
- FlexTreeNodeA2Not loaded
- FlexTreeNodeA3Not loaded
- children[]length=0
- FlexTreeNodeB
- children[]length=0
- FlexTreeNodeB1Not loaded
- FlexTreeNodeB2Not loaded
- FlexTreeNodeB3Not loaded
- children[]length=0
- FlexTreeNodeC
- children[]length=0
- FlexTreeNodeC1Not loaded
- FlexTreeNodeC2Not loaded
- FlexTreeNodeC3Not loaded
- children[]length=0
- FlexTreeNodeA
- *children[][]
The three nodes A, B, and C above are in the not-loaded state, and none of their children or descendants are loaded.
Then, you can call FlexTreeNode.load() on demand to load them.
For example, the following code loads node B:
const bnode = tree.getByPath("Root/B")
console.log(bnode.status) // == 'not-loaded'
await bnode.load()
console.log(bnode.status) // == 'loaded'The object tree after node B is loaded:
- FlexTreeNodeRoot
- *children[][]
- FlexTreeNodeA
- children[]length=0
- FlexTreeNodeA1Not loaded
- FlexTreeNodeA2Not loaded
- FlexTreeNodeA3Not loaded
- children[]length=0
- FlexTreeNodeBloaded
- children[]length=3
- FlexTreeNodeB1
- FlexTreeNodeB2
- FlexTreeNodeB3
- children[]length=3
- FlexTreeNodeC
- children[]length=0
- FlexTreeNodeC1Not loaded
- FlexTreeNodeC2Not loaded
- FlexTreeNodeC3Not loaded
- children[]length=0
- FlexTreeNodeA
- *children[][]
Note
Both FlexTree and FlexTreeNode instances have a load method. FlexTree.load is used to load the entire tree, while FlexTreeNode.load only loads a specific node.
Accessing Nodes by Path
When the FlexTree or FlexTreeNode is loaded, you can use the getByPath method on the FlexTree and FlexTreeNode instances to get the node instance at a specified path.
getByPath(
path: string,
options?: { byField?: string, delimiter?: string }
): FlexTreeNode<Fields, KeyFields, TreeNode, NodeId, TreeId> | undefined- Parameters
| Field Name | Data Type | Description |
|---|---|---|
path | string | Position of the node in the tree |
options | object | Options |
options.byField | string | Specifies which field value the path is composed of; defaults to name |
options.delimiter | string | Path delimiter; defaults to / |
- Return Value
Returns the FlexTreeNode instance at the specified path, or undefined if the node does not exist.
- Example
tree.getByPath('/')
tree.getByPath('./')
tree.getByPath('./A')
tree.getByPath('./A/A-1')
tree.getByPath('./A/A-1/A-1-1')
tree.getByPath('A')
tree.getByPath('A/A-1')
tree.getByPath('A/A-1/A-1-1')
const b1 = root.getByPath('B')!
b1.getByPath('../A')
b1.getByPath('../A/A-1')
b1.getByPath('../A/A-1/A-1-1')
b1.getByPath('B-1')
b1.getByPath('B-1/B-1-1')Notes
- Both
FlexTreeandFlexTreeNodeinstances have agetByPathmethod.FlexTree.getByPathsearches the entire tree, whileFlexTreeNode.getByPathuses a path relative to the node. - You can use relative path syntax:
./means the current node,../means the parent node,../../means an ancestor node, and so on.
- Both
Getting a Node
Use the get method on FlexTree and FlexTreeNode instances to return the specified instance among the node itself and its descendants.
get(nodeId: NodeId): FlexTreeNode<Fields, KeyFields, TreeNode, NodeId, TreeId> | undefined- Parameters
| Field Name | Data Type | Description |
|---|---|---|
nodeId | NodeId | Unique identifier of the node |
- Return Value
Returns the FlexTreeNode instance with the specified nodeId, or undefined if the node does not exist.
Node Status
When lazy loading is enabled via FlexTree.options.lazy=true, the FlexTreeNode instance has a status property that indicates the node's loading state.
type FlexTreeNodeStatus = 'not-loaded' | 'loading' | 'loaded' | 'error'- Status values
| Status | Description |
|---|---|
not-loaded | Not loaded |
loading | Loading |
loaded | Loaded |
error | Loading error |
Syncing Data
FlexTree and FlexTreeNode provide a sync method to reload node data from the database.
async sync(includeDescendants: boolean = false):voidFlexTree
- Properties
| Method Name | Return Type | Description |
|---|---|---|
root | FlexTreeNode | Returns the root node |
status | string | Gets the status of the root node |
options | FlexTreeOptions | Gets the options |
manager | FlexTreeManager | Gets the manager |
- Methods
| Method Name | Return Type | Description |
|---|---|---|
load | Promise<void> | Loads the tree into memory |
getByPath | FlexTreeNode | Gets a node by path |
get | FlexTreeNode | Gets a node |
find | FlexTreeNode[] | Finds nodes |
toJson | TreeNode | Serializes the tree to an object |
toList | TreeNode[] | Serializes the tree to a pid array |
on | void | Listens to an event |
off | void | Removes an event listener |
emit | void | Emits an event |
sync | void | Syncs data |
FlexNode
- Properties
| Method Name | Return Type | Description |
|---|---|---|
root | FlexTreeNode | Returns the root node |
status | string | Gets the status of the root node |
options | FlexTreeOptions | Gets the options |
manager | FlexTreeManager | Gets the manager |
- Methods
| Method Name | Return Type | Description |
|---|---|---|
load | Promise<void> | Loads the tree into memory |
getByPath | FlexTreeNode | Gets a node by path |
get | FlexTreeNode | Gets a node |
find | FlexTreeNode[] | Finds nodes |
toJson | TreeNode | Serializes the tree to an object |
toList | TreeNode[] | Serializes the tree to a pid array |
sync | void | Syncs data |