Add indexes to custom relations (#7156)
TODO: command to retro-actively create indexes to existing custom objects
This commit is contained in:
@ -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 {}
|
||||||
|
|||||||
@ -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],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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,
|
||||||
|
|||||||
@ -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,
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user