feat: support virtual columns in entity schema (#11597)

* feat: add entity mode virtual-property

* test: virtual columns
This commit is contained in:
chen 2025-08-16 23:06:00 +08:00 committed by GitHub
parent 16983132d3
commit d1e3950907
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 78 additions and 0 deletions

View File

@ -187,4 +187,11 @@ export interface ColumnOptions extends ColumnCommonOptions {
* SRID (Spatial Reference ID (EPSG code))
*/
srid?: number
/**
* Query to be used to populate the column data. This query is used when generating the relational db script.
* The query function is called with the current entities alias either defined by the Entity Decorator or automatically
* @See https://typeorm.io/decorator-reference#virtualcolumn for more details.
*/
query?: (alias: string) => string
}

View File

@ -44,6 +44,11 @@ export interface EntitySchemaColumnOptions extends SpatialColumnOptions {
*/
treeLevel?: boolean
/**
* Indicates if this column is a virtualProperty column.
*/
virtualProperty?: boolean
/**
* Column type. Must be one of the value from the ColumnTypes class.
*/
@ -214,4 +219,11 @@ export interface EntitySchemaColumnOptions extends SpatialColumnOptions {
* Foreign key options of this column.
*/
foreignKey?: EntitySchemaColumnForeignKeyOptions
/**
* Query to be used to populate the column data. This query is used when generating the relational db script.
* The query function is called with the current entities alias either defined by the Entity Decorator or automatically
* @See https://typeorm.io/decorator-reference#virtualcolumn for more details.
*/
query?: (alias: string) => string
}

View File

@ -98,6 +98,7 @@ export class EntitySchemaTransformer {
if (regularColumn.treeChildrenCount) mode = "treeChildrenCount"
if (regularColumn.treeLevel) mode = "treeLevel"
if (regularColumn.objectId) mode = "objectId"
if (regularColumn.virtualProperty) mode = "virtual-property"
const columnArgs: ColumnMetadataArgs = {
target: options.target || options.name,
@ -135,6 +136,7 @@ export class EntitySchemaTransformer {
transformer: regularColumn.transformer,
spatialFeatureType: regularColumn.spatialFeatureType,
srid: regularColumn.srid,
query: regularColumn.query,
},
}
metadataArgsStorage.columns.push(columnArgs)

View File

@ -0,0 +1,22 @@
import { EntitySchema } from "../../../../../../src"
export const Activity = new EntitySchema({
name: "activities",
columns: {
id: {
primary: true,
generated: "increment",
type: "int",
unsigned: true,
},
k1: {
type: "int",
},
vK1: {
type: "int",
virtualProperty: true,
query: (alias) =>
`SELECT k1 FROM "activities" WHERE "k1" = ${alias}."k1"`,
},
},
})

View File

@ -0,0 +1,35 @@
import "reflect-metadata"
import { Activity } from "./entity/Activity"
import {
closeTestingConnections,
createTestingConnections,
reloadTestingDatabases,
} from "../../../../utils/test-utils"
import { DataSource } from "../../../../../src"
import { expect } from "chai"
describe("entity-schema > columns > virtual column", () => {
let connections: DataSource[]
before(
async () =>
(connections = await createTestingConnections({
entities: [<any>Activity],
enabledDrivers: ["better-sqlite3"],
})),
)
beforeEach(() => reloadTestingDatabases(connections))
after(() => closeTestingConnections(connections))
it("should query virtual columns", () => {
return Promise.all(
connections.map(async (connection) => {
const repo = connection.getRepository(Activity)
await repo.save({ id: 0, k1: 1 })
const result = (await repo.findOne({ where: { id: 0 } }))!
expect(result.vK1).eq(result.k1)
expect(result.vK1).eq(1)
}),
)
})
})