feat: entity schema support trees (#11606)

This commit is contained in:
chen 2025-09-29 16:36:35 +08:00 committed by GitHub
parent 4f05718237
commit 925dee002b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 116 additions and 0 deletions

View File

@ -14,6 +14,7 @@ import { EntitySchemaExclusionOptions } from "./EntitySchemaExclusionOptions"
import { EntitySchemaInheritanceOptions } from "./EntitySchemaInheritanceOptions"
import { EntitySchemaRelationIdOptions } from "./EntitySchemaRelationIdOptions"
import { EntitySchemaForeignKeyOptions } from "./EntitySchemaForeignKeyOptions"
import { TreeMetadataArgs } from "../metadata-args/TreeMetadataArgs"
/**
* Interface for entity metadata mappings stored inside "schemas" instead of models decorated by decorators.
@ -135,4 +136,6 @@ export class EntitySchemaOptions<T> {
* Custom discriminator value for Single Table Inheritance.
*/
discriminatorValue?: string
trees?: Omit<TreeMetadataArgs, "target">[]
}

View File

@ -396,5 +396,15 @@ export class EntitySchemaTransformer {
)
})
}
if (options.trees) {
options.trees.forEach((tree) => {
metadataArgsStorage.trees.push({
target: options.target || options.name,
type: tree.type,
options: tree.options,
})
})
}
}
}

View File

@ -0,0 +1,34 @@
import { EntitySchema } from "../../../../../../src"
export const Category = new EntitySchema<{
id: number
name: string
parentCategory: any
childCategories: any[]
}>({
name: "Category",
columns: {
id: {
primary: true,
type: Number,
generated: "increment",
},
name: {
type: String,
},
},
relations: {
parentCategory: {
type: "many-to-one",
treeParent: true,
target: "Category",
},
childCategories: {
type: "one-to-many",
treeChildren: true,
target: "Category",
cascade: true,
},
},
trees: [{ type: "nested-set" }],
})

View File

@ -0,0 +1,69 @@
import "reflect-metadata"
import { Category } from "./entity/Category"
import { DataSource } from "../../../../../src"
import {
createTestingConnections,
reloadTestingDatabases,
closeTestingConnections,
} from "../../../../utils/test-utils"
describe("entity-schema > tree tables > nested-set", () => {
let connections: DataSource[]
before(
async () =>
(connections = await createTestingConnections({
entities: [Category],
enabledDrivers: ["better-sqlite3"],
})),
)
beforeEach(() => reloadTestingDatabases(connections))
after(() => closeTestingConnections(connections))
it("attach should work properly", () =>
Promise.all(
connections.map(async (connection) => {
const categoryRepository =
connection.getTreeRepository(Category)
const a1 = await categoryRepository.save({ name: "a1" })
const a11 = await categoryRepository.save({
name: "a11",
parentCategory: a1,
})
await categoryRepository.save({
name: "a111",
parentCategory: a11,
})
await categoryRepository.save({
name: "a12",
parentCategory: a1,
})
const rootCategories = await categoryRepository.findRoots()
rootCategories.should.be.eql([
{
id: 1,
name: "a1",
},
])
const a11Parent = await categoryRepository.findAncestors(a11)
const a11ParentNames = a11Parent.map((i) => i.name)
a11ParentNames.length.should.be.equal(2)
a11ParentNames.should.deep.include("a1")
a11ParentNames.should.deep.include("a11")
const a1Children = await categoryRepository.findDescendants(a1)
const a1ChildrenNames = a1Children.map((i) => i.name)
a1ChildrenNames.length.should.be.equal(4)
a1ChildrenNames.should.deep.include("a1")
a1ChildrenNames.should.deep.include("a11")
a1ChildrenNames.should.deep.include("a111")
a1ChildrenNames.should.deep.include("a12")
}),
))
})