Quick Start
When developing a Nodejs application that needs to store a tree in a database, FlexTree is recommended.
FlexTree is a tree-storage management component based on the Left-Right Value algorithm. It provides efficient tree-structure storage and access and supports a full range of tree operations such as CRUD, traversal, move, and query.
In this tutorial, we'll use developing an organization-chart manager as an example to demonstrate how to use FlexTree.
A simple organization chart tree looks like this:
- A公司
- 行政中心
- 市场中心
- 研发中心
Note
This website is built with vitepress, and the tree above is rendered by the open-source library LiteTree.
Step 1: Install the Core Library
First, install the flextree core library.
npm install flextree
// or
yarn add flextree
// or
pnpm add flextree
// or
bun add flextreeStep 2: Configure a Database Adapter
Next, install the database driver flextree-sqlite-adapter. flextree supports any database for storage; here we use sqlite3 as an example.
You need to install the flextree-sqlite-adapter driver.
npm install flextree-sqlite-adapter
// or
yarn add flextree-sqlite-adapter
// or
pnpm add flextree-sqlite-adapter
// or
bun add flextree-sqlite-adapterflextree-sqlite-driver is the sqlite3 database driver for flextree, storing data in a sqlite3 database.
Note
If you are using a database such as MySQL or PostgreSQL, you can install the corresponding driver such as flextree-prima-driver, or build a custom driver based on the IFlexTreeAdapter provided by flextree.
Step 3: Create the Tree Table
Next, we need to create the organization-chart tree table org in the database.
If you are using an sqlite database, you can create the table with the following sql statement:
import SqliteAdapter from 'flextree-sqlite-adapter';
const sqliteAdapter = new SqliteAdapter("org.db")
await sqliteAdapter.open()
await sqliteAdapter.exec(`
CREATE TABLE IF NOT EXISTS org (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(60),
level INTEGER,
leftValue INTEGER,
rightValue INTEGER,
`)Above, we created an org table with the following fields:
| Field | Type | Description |
|---|---|---|
id | INTEGER | Primary key, auto-increment |
name | VARCHAR(60) | Name |
level | INTEGER | Level |
leftValue | INTEGER | Left value |
rightValue | INTEGER | Right value |
In general, the fields above are required; you may add others based on your actual situation.
Note
In general, creating the table is the application's responsibility—flextree does not create tables. This section only demonstrates the tree-table structure you need to create.
Step 4: Create the Tree Manager
Next, we create an organization-chart tree manager OrgManager to manage the organization tree.
import { FlexTreeManager } from 'flextree';
import SqliteAdapter from 'flextree-sqlite-adapter';
const sqliteAdapter = new SqliteAdapter("org.db")
await sqliteAdapter.open()
const orgManager = new FlexTreeManager("org",{
adapter: sqliteAdapter
})Note
It's recommended to create the manager via the singleton method FlexTreeManager.getInstance("org", { adapter: sqliteAdapter })—the same table name returns the same instance. See Manager for details.
Step 5: Add Tree Nodes
Now we can start adding nodes to the organization tree.
// Create a root node
await orgManager.createRoot({
name: "A公司"
})
// Add the first-level department child nodes of the organization
await orgManager.addNodes([
{ name: "行政中心" },
{ name: "市场中心" },
{ name: "研发中心"}
])
// Add the department child nodes of 行政中心.
const node = await orgManager.findNode({name:"行政中心"})
await orgManager.addNodes( [
{ name: "总裁办" },
{ name: "人力资源部" },
{ name: "财务部" },
{ name: "行政部" },
{ name: "法务部" },
{ name: "审计部" }
],node) // Add as child nodes of nodeWe can use the addNodes method to add nodes to the tree. addNodes supports batch addition and multiple forms of adding child nodes.
Step 6: Access the Tree
Above we created a complete tree; next we can access it in two ways:
- Access the tree via
FlexTreeManager - Access the tree via the
FlexTreeobject
Getting Nodes
// Get all nodes
await orgManager.getNodes()
// Limit by level—fetch only levels 1-3, excluding level 4 and below
await orgManager.getNodes(3)
// Get a node by id
await orgManager.getNode(1)
// Get the root node of the tree
await orgManager.getRoot()
// Get the node whose name is 行政中心
const node = await orgManager.findNode({name:"行政中心"})
// Get the children of the node <行政中心>
await orgManager.getChildren(node)
// Get all descendants of the node <行政中心>
await orgManager.getDescendants(node)
// Get all descendants of the node <行政中心>, including itself
await orgManager.getDescendants(node,{includeSelf:true})
// Get all descendants of the node <行政中心>, with a level limit
await orgManager.getDescendants(node,{level:2})
// Get the children of the node <行政中心>; level=1 is equivalent to fetching only direct children
await orgManager.getDescendants(node,{level:1})
// Get all ancestors of the node <行政中心>
await orgManager.getAncestors(node)
// Get the parent of the node <行政中心>
await orgManager.getParent(node)
// Get all siblings of the node <行政中心>
await orgManager.getSiblings(node)
// Get all siblings of the node <行政中心>, including itself
await orgManager.getSiblings(node,{includeSelf:true})
// Get the next sibling of the node <行政中心>
await orgManager.getNextSibling(node)
// Get the previous sibling of the node <行政中心>
await orgManager.getPrevSibling(node)Finding Nodes
// Find the node whose name is 行政中心; returns only the first matching node
await orgManager.findNode({name:"行政中心"})
// Find all nodes whose level=1
await orgManager.findNodes({level:1})Note
FlexTree provides only simple query capabilities; for more complex queries, use your database's query features.
Moving Nodes
import { FirstChild, LastChild,PreviousSibling,NextSibling } from 'flextree'
const admin = await orgManager.findNode({name:"行政中心"})
const market = await orgManager.findNode({name:"市场中心"})
// Move 行政中心 under 市场中心 as its last child
await orgManager.move(admin,market)
await orgManager.move(admin,market,LastChild) // Equivalent to the above
// Move 行政中心 under 市场中心 as its first child
await orgManager.move(admin,market,FirstChild)
// Move 行政中心 before 市场中心 as its previous sibling
await orgManager.move(admin,market,PreviousSibling)
// Move 行政中心 after 市场中心 as its next sibling
await orgManager.move(admin,market,NextSibling)
// Move 行政中心 up
await orgManager.moveUpNode(admin)
// Move 行政中心 down
await orgManager.moveDownNode(admin)Deleting Nodes
const admin = await orgManager.findNode({name:"行政中心"})
// Delete the 行政中心 node and all its descendants
await orgManager.deleteNode(admin)
// Clear the tree
await orgManager.clear()Querying Node Relations
const admin = await orgManager.findNode({name:"行政中心"})
const market = await orgManager.findNode({name:"市场中心"})
// Return the relation between the admin node and the market node
const relation = await getNodeRelation(admin,market)
// Possible values of relation
export enum FlexTreeNodeRelation {
Self = 0,
Parent = 1,
Child = 2,
Siblings = 3,
Descendants = 4,
Ancestors = 5,
DiffTree = 6,
SameTree = 7,
SameLevel = 8,
Unknow = 9,
}