Tree Entities

TypeORM supports the Adjacency list and Closure table patterns for storing tree structures. To learn more about hierarchy table take a look at this awesome presentation by Bill Karwin.

Adjacency list

Adjacency list is a simple model with self-referencing. The benefit of this approach is simplicity, drawback is that you can't load big trees in all at once because of join limitations. To learn more about the benefits and use of Adjacency Lists look at this article by Matthew Schinckel. Example:

import {Entity, Column, PrimaryGeneratedColumn, ManyToOne, OneToMany} from "typeorm";

@Entity()
export class Category {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @Column()
    description: string;

    @ManyToOne(type => Category, category => category.children)
    parent: Category;

    @OneToMany(type => Category, category => category.parent)
    children: Category[];
}

Nested set

Nested set is another pattern of storing tree structures in the database. Its very efficient for reads, but bad for writes. You cannot have multiple roots in nested set. Example:

Materialized Path (aka Path Enumeration)

Materialized Path (also called Path Enumeration) is another pattern of storing tree structures in the database. Its simple and effective. Example:

Closure table

Closure table stores relations between parent and child in a separate table in a special way. It's efficient in both reads and writes. Example:

You can specify closure table name and / or closure table columns names by setting optional parameter options into @Tree("closure-table", options). ancestorColumnName and descandantColumnName are callback functions, which receive primary column's metadata and return column's name.

Note:

Updating or removing a component's parent has not been implemented yet (see this issue). The closure table will need to be explicitly updated to do either of these operations.

Working with tree entities

To make bind tree entities to each other its important to set to children entities their parent and save them, for example:

To load such a tree use TreeRepository:

trees will be following:

There are other special methods to work with tree entities through TreeRepository:

  • findTrees - Returns all trees in the database with all their children, children of children, etc.

  • findRoots - Roots are entities that have no ancestors. Finds them all.

    Does not load children leafs.

  • findDescendants - Gets all children (descendants) of the given entity. Returns them all in a flat array.

  • findDescendantsTree - Gets all children (descendants) of the given entity. Returns them in a tree - nested into each other.

  • createDescendantsQueryBuilder - Creates a query builder used to get descendants of the entities in a tree.

  • countDescendants - Gets number of descendants of the entity.

  • findAncestors - Gets all parent (ancestors) of the given entity. Returns them all in a flat array.

  • findAncestorsTree - Gets all parent (ancestors) of the given entity. Returns them in a tree - nested into each other.

  • createAncestorsQueryBuilder - Creates a query builder used to get ancestors of the entities in a tree.

  • countAncestors - Gets the number of ancestors of the entity.

Last updated

Was this helpful?