Add indexes to custom relations (#7156)

TODO: command to retro-actively create indexes to existing custom
objects
This commit is contained in:
Weiko
2024-09-20 15:06:26 +02:00
committed by GitHub
parent f845187f8e
commit bebeb1515b
4 changed files with 148 additions and 18 deletions

View File

@ -2,10 +2,15 @@ import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity'; import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
import { IndexMetadataService } from 'src/engine/metadata-modules/index-metadata/index-metadata.service';
import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
@Module({ @Module({
imports: [TypeOrmModule.forFeature([IndexMetadataEntity], 'metadata')], imports: [
providers: [], TypeOrmModule.forFeature([IndexMetadataEntity], 'metadata'),
exports: [], WorkspaceMigrationModule,
],
providers: [IndexMetadataService],
exports: [IndexMetadataService],
}) })
export class IndexMetadataModule {} export class IndexMetadataModule {}

View File

@ -0,0 +1,87 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
import { generateDeterministicIndexName } from 'src/engine/metadata-modules/index-metadata/utils/generate-deterministic-index-name';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
import {
WorkspaceMigrationIndexActionType,
WorkspaceMigrationTableAction,
WorkspaceMigrationTableActionType,
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
@Injectable()
export class IndexMetadataService {
constructor(
@InjectRepository(IndexMetadataEntity, 'metadata')
private readonly indexMetadataRepository: Repository<IndexMetadataEntity>,
private readonly workspaceMigrationService: WorkspaceMigrationService,
) {}
async createIndex(
workspaceId: string,
objectMetadata: ObjectMetadataEntity,
fieldMetadataToIndex: Partial<FieldMetadataEntity>[],
) {
const tableName = computeObjectTargetTable(objectMetadata);
const columnNames: string[] = fieldMetadataToIndex.map(
(fieldMetadata) => fieldMetadata.name as string,
);
const indexName = `IDX_${generateDeterministicIndexName([tableName, ...columnNames])}`;
let savedIndexMetadata: IndexMetadataEntity;
try {
savedIndexMetadata = await this.indexMetadataRepository.save({
name: indexName,
tableName,
indexFieldMetadatas: fieldMetadataToIndex.map(
(fieldMetadata, index) => {
return {
fieldMetadataId: fieldMetadata.id,
order: index,
};
},
),
workspaceId,
objectMetadataId: objectMetadata.id,
});
} catch (error) {
throw new Error(
`Failed to create index ${indexName} on object metadata ${objectMetadata.nameSingular}`,
);
}
if (!savedIndexMetadata) {
throw new Error(
`Failed to return saved index ${indexName} on object metadata ${objectMetadata.nameSingular}`,
);
}
const migration = {
name: tableName,
action: WorkspaceMigrationTableActionType.ALTER_INDEXES,
indexes: [
{
action: WorkspaceMigrationIndexActionType.CREATE,
columns: columnNames,
name: indexName,
},
],
} satisfies WorkspaceMigrationTableAction;
await this.workspaceMigrationService.createCustomMigration(
generateMigrationName(`create-${objectMetadata.nameSingular}-index`),
workspaceId,
[migration],
);
}
}

View File

@ -9,6 +9,7 @@ import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/field-metadata.module'; import { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/field-metadata.module';
import { IndexMetadataModule } from 'src/engine/metadata-modules/index-metadata/index-metadata.module';
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module'; import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
import { RelationMetadataGraphqlApiExceptionInterceptor } from 'src/engine/metadata-modules/relation-metadata/interceptors/relation-metadata-graphql-api-exception.interceptor'; import { RelationMetadataGraphqlApiExceptionInterceptor } from 'src/engine/metadata-modules/relation-metadata/interceptors/relation-metadata-graphql-api-exception.interceptor';
import { RelationMetadataResolver } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.resolver'; import { RelationMetadataResolver } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.resolver';
@ -33,6 +34,7 @@ import { RelationMetadataDTO } from './dtos/relation-metadata.dto';
), ),
ObjectMetadataModule, ObjectMetadataModule,
FieldMetadataModule, FieldMetadataModule,
IndexMetadataModule,
WorkspaceMigrationRunnerModule, WorkspaceMigrationRunnerModule,
WorkspaceMigrationModule, WorkspaceMigrationModule,
WorkspaceCacheStorageModule, WorkspaceCacheStorageModule,

View File

@ -13,6 +13,7 @@ import {
FieldMetadataType, FieldMetadataType,
} from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/field-metadata.service'; import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/field-metadata.service';
import { IndexMetadataService } from 'src/engine/metadata-modules/index-metadata/index-metadata.service';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service'; import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { CreateRelationInput } from 'src/engine/metadata-modules/relation-metadata/dtos/create-relation.input'; import { CreateRelationInput } from 'src/engine/metadata-modules/relation-metadata/dtos/create-relation.input';
@ -34,6 +35,7 @@ import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util'; import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service'; import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service'; import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
import { BASE_OBJECT_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { import {
RelationMetadataEntity, RelationMetadataEntity,
@ -54,6 +56,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService, private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService, private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService,
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService, private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
private readonly indexMetadataService: IndexMetadataService,
) { ) {
super(relationMetadataRepository); super(relationMetadataRepository);
} }
@ -91,21 +94,22 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
const fromId = uuidV4(); const fromId = uuidV4();
const toId = uuidV4(); const toId = uuidV4();
await this.fieldMetadataService.createMany([ const createdRelationFieldsMetadata =
this.createFieldMetadataForRelationMetadata( await this.fieldMetadataService.createMany([
relationMetadataInput, this.createFieldMetadataForRelationMetadata(
'from', relationMetadataInput,
isCustom, 'from',
fromId, isCustom,
), fromId,
this.createFieldMetadataForRelationMetadata( ),
relationMetadataInput, this.createFieldMetadataForRelationMetadata(
'to', relationMetadataInput,
isCustom, 'to',
toId, isCustom,
), toId,
this.createForeignKeyFieldMetadata(relationMetadataInput, columnName), ),
]); this.createForeignKeyFieldMetadata(relationMetadataInput, columnName),
]);
const createdRelationMetadata = await super.createOne({ const createdRelationMetadata = await super.createOne({
...relationMetadataInput, ...relationMetadataInput,
@ -119,6 +123,38 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
columnName, columnName,
); );
const toObjectMetadata =
objectMetadataMap[relationMetadataInput.toObjectMetadataId];
const foreignKeyFieldMetadata = createdRelationFieldsMetadata.find(
(fieldMetadata) => fieldMetadata.type === FieldMetadataType.UUID,
);
if (!foreignKeyFieldMetadata) {
throw new RelationMetadataException(
`ForeignKey field metadata not found`,
RelationMetadataExceptionCode.RELATION_METADATA_NOT_FOUND,
);
}
const deletedFieldMetadata = toObjectMetadata.fields.find(
(fieldMetadata) =>
fieldMetadata.standardId === BASE_OBJECT_STANDARD_FIELD_IDS.deletedAt,
);
if (!deletedFieldMetadata) {
throw new RelationMetadataException(
`Deleted field metadata not found`,
RelationMetadataExceptionCode.RELATION_METADATA_NOT_FOUND,
);
}
await this.indexMetadataService.createIndex(
relationMetadataInput.workspaceId,
toObjectMetadata,
[foreignKeyFieldMetadata, deletedFieldMetadata],
);
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations( await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
relationMetadataInput.workspaceId, relationMetadataInput.workspaceId,
); );