From 4f7bbf1f294e72fdb2f317e7f05f322e3f34a9d4 Mon Sep 17 00:00:00 2001 From: Harminder Virk Date: Tue, 18 Jun 2024 13:41:16 +0530 Subject: [PATCH] feat: add support for indexes (#7756) --- .../src/dml/__tests__/entity-builder.spec.ts | 85 +++++++++++++++++++ .../dml/helpers/create-mikro-orm-entity.ts | 31 ++++++- packages/core/utils/src/dml/schema/base.ts | 16 ++++ packages/core/utils/src/dml/types.ts | 4 +- 4 files changed, 133 insertions(+), 3 deletions(-) diff --git a/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts b/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts index a0b858e1f9..d2a2723766 100644 --- a/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts +++ b/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts @@ -479,6 +479,91 @@ describe("Entity builder", () => { }) }) + describe("Entity builder | indexes", () => { + test("define index on a field", () => { + const model = new EntityBuilder() + const user = model.define("user", { + id: model.number().index(), + username: model.text(), + email: model.text().unique(), + }) + + const entityBuilder = createMikrORMEntity() + const User = entityBuilder(user) + expectTypeOf(new User()).toMatchTypeOf<{ + id: number + username: string + email: string + deleted_at: Date | null + }>() + + const metaData = MetadataStorage.getMetadataFromDecorator(User) + expect(metaData.className).toEqual("User") + expect(metaData.path).toEqual("User") + + expect(metaData.indexes).toEqual([ + { + name: "IDX_users_id", + expression: + 'CREATE INDEX IF NOT EXISTS "IDX_users_id" ON "users" (id) WHERE deleted_at IS NULL', + }, + { + name: "IDX_users_email", + expression: + 'CREATE UNIQUE INDEX IF NOT EXISTS "IDX_users_email" ON "users" (email) WHERE deleted_at IS NULL', + }, + ]) + + expect(metaData.filters).toEqual({ + softDeletable: { + name: "softDeletable", + cond: expect.any(Function), + default: true, + args: false, + }, + }) + + expect(metaData.properties).toEqual({ + id: { + reference: "scalar", + type: "number", + columnType: "integer", + name: "id", + nullable: false, + getter: false, + setter: false, + }, + username: { + reference: "scalar", + type: "string", + columnType: "text", + name: "username", + nullable: false, + getter: false, + setter: false, + }, + email: { + reference: "scalar", + type: "string", + columnType: "text", + name: "email", + nullable: false, + getter: false, + setter: false, + }, + deleted_at: { + reference: "scalar", + type: "date", + columnType: "timestamptz", + name: "deleted_at", + nullable: true, + getter: false, + setter: false, + }, + }) + }) + }) + describe("Entity builder | hasOne", () => { test("define hasOne relationship", () => { const model = new EntityBuilder() diff --git a/packages/core/utils/src/dml/helpers/create-mikro-orm-entity.ts b/packages/core/utils/src/dml/helpers/create-mikro-orm-entity.ts index 323505a02f..8619defc13 100644 --- a/packages/core/utils/src/dml/helpers/create-mikro-orm-entity.ts +++ b/packages/core/utils/src/dml/helpers/create-mikro-orm-entity.ts @@ -9,7 +9,11 @@ import { Filter, } from "@mikro-orm/core" import { DmlEntity } from "../entity" -import { camelToSnakeCase, pluralize } from "../../common" +import { + pluralize, + camelToSnakeCase, + createPsqlIndexStatementHelper, +} from "../../common" import { upperCaseFirst } from "../../common/upper-case-first" import type { Infer, @@ -114,6 +118,30 @@ export function createMikrORMEntity() { })(MikroORMEntity.prototype, field.fieldName) } + /** + * Prepares indexes for a given field + */ + function applyIndexes( + MikroORMEntity: EntityConstructor, + tableName: string, + field: SchemaMetadata + ) { + field.indexes.forEach((index) => { + const name = + index.name || `IDX_${tableName}_${camelToSnakeCase(field.fieldName)}` + + const providerEntityIdIndexStatement = createPsqlIndexStatementHelper({ + name, + tableName, + columns: [field.fieldName], + unique: index.type === "unique", + where: "deleted_at IS NULL", + }) + + providerEntityIdIndexStatement.MikroORMIndex()(MikroORMEntity) + }) + } + /** * Defines has one relationship on the Mikro ORM entity. */ @@ -421,6 +449,7 @@ export function createMikrORMEntity() { const field = property.parse(name) if ("fieldName" in field) { defineProperty(MikroORMEntity, field) + applyIndexes(MikroORMEntity, tableName, field) } else { defineRelationship(MikroORMEntity, field, cascades) } diff --git a/packages/core/utils/src/dml/schema/base.ts b/packages/core/utils/src/dml/schema/base.ts index 3558e481c0..aa620146e1 100644 --- a/packages/core/utils/src/dml/schema/base.ts +++ b/packages/core/utils/src/dml/schema/base.ts @@ -29,6 +29,22 @@ export abstract class BaseSchema implements SchemaType { return new NullableModifier(this) } + /** + * Define an index on the property + */ + index(name?: string) { + this.#indexes.push({ name, type: "index" }) + return this + } + + /** + * Define a unique index on the property + */ + unique(name?: string) { + this.#indexes.push({ name, type: "unique" }) + return this + } + /** * Define default value for the property */ diff --git a/packages/core/utils/src/dml/types.ts b/packages/core/utils/src/dml/types.ts index 51cbe0f9ab..3f1bdbbda9 100644 --- a/packages/core/utils/src/dml/types.ts +++ b/packages/core/utils/src/dml/types.ts @@ -42,8 +42,8 @@ export type SchemaMetadata = MaybeFieldMetadata & { options?: Record } indexes: { - name: string - type: string + name?: string + type: "index" | "unique" }[] relationships: RelationshipMetadata[] }