Zum Hauptinhalt springen

Baum-Entitäten

Inoffizielle Beta-Übersetzung

Diese Seite wurde von PageTurner AI übersetzt (Beta). Nicht offiziell vom Projekt unterstützt. Fehler gefunden? Problem melden →

TypeORM unterstützt Adjacency List und Closure Table Muster zur Speicherung von Baumstrukturen. Weitere Informationen zur Hierarchietabelle finden Sie in dieser hervorragenden Präsentation von Bill Karwin.

Adjacency List

Adjacency List ist ein einfaches Modell mit Selbstreferenzierung. Hinweis: TreeRepository unterstützt Adjacency List nicht. Der Vorteil dieses Ansatzes liegt in der Einfachheit, ein Nachteil ist, dass große Bäume nicht auf einmal geladen werden können aufgrund von JOIN-Beschränkungen. Mehr zu Vorzügen und Einsatz von Adjacency Lists finden Sie in diesem Artikel von Matthew Schinckel. Beispiel:

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 ist ein weiteres Muster zur Speicherung von Baumstrukturen in der Datenbank. Es ist sehr effizient für Lesevorgänge, aber schlecht für Schreibvorgänge. Mehrere Wurzeln sind im Nested Set nicht möglich. Beispiel:

import {
Entity,
Tree,
Column,
PrimaryGeneratedColumn,
TreeChildren,
TreeParent,
TreeLevelColumn,
} from "typeorm"

@Entity()
@Tree("nested-set")
export class Category {
@PrimaryGeneratedColumn()
id: number

@Column()
name: string

@TreeChildren()
children: Category[]

@TreeParent()
parent: Category
}

Materialized Path (auch Path Enumeration)

Materialized Path (auch Path Enumeration genannt) ist ein weiteres Muster zur Speicherung von Baumstrukturen. Es ist einfach und effektiv. Beispiel:

import {
Entity,
Tree,
Column,
PrimaryGeneratedColumn,
TreeChildren,
TreeParent,
TreeLevelColumn,
} from "typeorm"

@Entity()
@Tree("materialized-path")
export class Category {
@PrimaryGeneratedColumn()
id: number

@Column()
name: string

@TreeChildren()
children: Category[]

@TreeParent()
parent: Category
}

Closure Table

Closure Table speichert Beziehungen zwischen Eltern und Kindern in einer separaten Tabelle auf spezielle Weise. Effizient sowohl für Lese- als auch Schreibvorgänge. Beispiel:

import {
Entity,
Tree,
Column,
PrimaryGeneratedColumn,
TreeChildren,
TreeParent,
TreeLevelColumn,
} from "typeorm"

@Entity()
@Tree("closure-table")
export class Category {
@PrimaryGeneratedColumn()
id: number

@Column()
name: string

@TreeChildren()
children: Category[]

@TreeParent()
parent: Category
}

Sie können den Namen der Closure Table und/oder Spaltennamen festlegen, indem Sie den optionalen Parameter options in @Tree("closure-table", options) setzen. ancestorColumnName und descandantColumnName sind Callback-Funktionen, die Metadaten der Primärspalte erhalten und den Spaltennamen zurückgeben.

@Tree("closure-table", {
closureTableName: "category_closure",
ancestorColumnName: (column) => "ancestor_" + column.propertyName,
descendantColumnName: (column) => "descendant_" + column.propertyName,
})

Arbeiten mit Baum-Entitäten

Um Baum-Entitäten miteinander zu verbinden, muss das Elternelement in der Kind-Entität gesetzt und anschließend gespeichert werden. Beispiel:

const a1 = new Category()
a1.name = "a1"
await dataSource.manager.save(a1)

const a11 = new Category()
a11.name = "a11"
a11.parent = a1
await dataSource.manager.save(a11)

const a12 = new Category()
a12.name = "a12"
a12.parent = a1
await dataSource.manager.save(a12)

const a111 = new Category()
a111.name = "a111"
a111.parent = a11
await dataSource.manager.save(a111)

const a112 = new Category()
a112.name = "a112"
a112.parent = a11
await dataSource.manager.save(a112)

Verwenden Sie zum Laden eines solchen Baums TreeRepository:

const trees = await dataSource.manager.getTreeRepository(Category).findTrees()

trees wird wie folgt aussehen:

[
{
"id": 1,
"name": "a1",
"children": [
{
"id": 2,
"name": "a11",
"children": [
{
"id": 4,
"name": "a111"
},
{
"id": 5,
"name": "a112"
}
]
},
{
"id": 3,
"name": "a12"
}
]
}
]

Weitere spezielle Methoden für Baum-Entitäten in TreeRepository:

  • findTrees - Gibt alle Bäume in der Datenbank mit allen Kindern, Kindeskindern usw. zurück.
const treeCategories = await dataSource.manager
.getTreeRepository(Category)
.findTrees()
// returns root categories with sub categories inside

const treeCategoriesWithLimitedDepth = await dataSource.manager
.getTreeRepository(Category)
.findTrees({ depth: 2 })
// returns root categories with sub categories inside, up to depth 2
  • findRoots - Wurzeln sind Entitäten ohne Vorfahren. Findet alle Wurzeln. Lädt keine Blattknoten der Kinder.
const rootCategories = await dataSource.manager
.getTreeRepository(Category)
.findRoots()
// returns root categories without sub categories inside
  • findDescendants - Ruft alle Kinder (Nachkommen) der angegebenen Entität ab. Gibt sie in einem flachen Array zurück.
const children = await dataSource.manager
.getTreeRepository(Category)
.findDescendants(parentCategory)
// returns all direct subcategories (without its nested categories) of a parentCategory
  • findDescendantsTree - Ruft alle Kinder (Nachkommen) der angegebenen Entität ab. Gibt sie als verschachtelten Baum zurück.
const childrenTree = await repository.findDescendantsTree(parentCategory)
// returns all direct subcategories (with its nested categories) of a parentCategory
const childrenTreeWithLimitedDepth = await repository.findDescendantsTree(
parentCategory,
{ depth: 2 },
)
// returns all direct subcategories (with its nested categories) of a parentCategory, up to depth 2
  • createDescendantsQueryBuilder - Erstellt einen Query Builder zum Abrufen der Nachkommen von Entitäten in einem Baum.
const children = await repository
.createDescendantsQueryBuilder(
"category",
"categoryClosure",
parentCategory,
)
.andWhere("category.type = 'secondary'")
.getMany()
  • countDescendants - Ermittelt die Anzahl der Nachkommen der Entität.
const childrenCount = await dataSource.manager
.getTreeRepository(Category)
.countDescendants(parentCategory)
  • findAncestors - Ruft alle Eltern (Vorfahren) der angegebenen Entität ab. Gibt sie in einem flachen Array zurück.
const parents = await repository.findAncestors(childCategory)
// returns all direct childCategory's parent categories (without "parent of parents")
  • findAncestorsTree - Ruft alle Eltern (Vorfahren) der angegebenen Entität ab. Gibt sie als verschachtelten Baum zurück.
const parentsTree = await dataSource.manager
.getTreeRepository(Category)
.findAncestorsTree(childCategory)
// returns all direct childCategory's parent categories (with "parent of parents")
  • createAncestorsQueryBuilder - Erstellt einen Query Builder zum Abrufen der Vorfahren von Entitäten in einem Baum.
const parents = await repository
.createAncestorsQueryBuilder("category", "categoryClosure", childCategory)
.andWhere("category.type = 'secondary'")
.getMany()
  • countAncestors - Ermittelt die Anzahl der Vorfahren der Entität.
const parentsCount = await dataSource.manager
.getTreeRepository(Category)
.countAncestors(childCategory)

Für folgende Methoden können Optionen übergeben werden:

  • findTrees

  • findRoots

  • findDescendants

  • findDescendantsTree

  • findAncestors

  • findAncestorsTree

Folgende Optionen sind verfügbar:

  • relations - Legt fest, welche Relationen der Entität geladen werden sollen (vereinfachte LEFT JOIN-Form).

Beispiele:

const treeCategoriesWithRelations = await dataSource.manager
.getTreeRepository(Category)
.findTrees({
relations: ["sites"],
})
// automatically joins the sites relation

const parentsWithRelations = await dataSource.manager
.getTreeRepository(Category)
.findAncestors(childCategory, {
relations: ["members"],
})
// returns all direct childCategory's parent categories (without "parent of parents") and joins the 'members' relation