Fix custom index creation missing indexFieldMetadatas (#7832)

## Context
Regression on custom index creation where indexFieldMetadatas were not
saved properly in the DB. This is because we recently changed save() to
upsert() in the indexMetadataService and upsert does not handle nesting
insert properly.
I'm suggesting another fix where we separate indexMetadata creation and
index migration creation in 2 different functions. Since the goal was to
be able to recreate the index after being deleted when we changed the
tsvector expression and indexMetadata was actually not deleted, we
didn't need to recreate that part (hence the upsert) and only needed to
run a migration to create the actual index in the workspace schema.
I've updated the different services and now only call
createIndexMigration when we update a search vector expression.

Note: this is also fixing the sync-metadata command when running on a
workspace with a custom object (including the seeded workspace which has
the 'rocket' custom object), failing due to the missing 'searchVector'
indexFieldMetadata
This commit is contained in:
Weiko 2024-10-18 18:59:50 +02:00 committed by GitHub
parent 17b934e22b
commit d4457d756c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 62 additions and 28 deletions

View File

@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { isDefined } from 'class-validator';
import { InsertResult, Repository } from 'typeorm';
import { Repository } from 'typeorm';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import {
@ -28,7 +28,7 @@ export class IndexMetadataService {
private readonly workspaceMigrationService: WorkspaceMigrationService,
) {}
async createIndex(
async createIndexMetadata(
workspaceId: string,
objectMetadata: ObjectMetadataEntity,
fieldMetadataToIndex: Partial<FieldMetadataEntity>[],
@ -45,42 +45,76 @@ export class IndexMetadataService {
const indexName = `IDX_${generateDeterministicIndexName([tableName, ...columnNames])}`;
let result: InsertResult;
let result: IndexMetadataEntity;
const existingIndex = await this.indexMetadataRepository.findOne({
where: {
name: indexName,
workspaceId,
objectMetadataId: objectMetadata.id,
},
});
if (existingIndex) {
throw new Error(
`Index ${indexName} on object metadata ${objectMetadata.nameSingular} already exists`,
);
}
try {
result = await this.indexMetadataRepository.upsert(
{
name: indexName,
indexFieldMetadatas: fieldMetadataToIndex.map(
(fieldMetadata, index) => {
return {
fieldMetadataId: fieldMetadata.id,
order: index,
};
},
),
workspaceId,
objectMetadataId: objectMetadata.id,
...(isDefined(indexType) ? { indexType: indexType } : {}),
isCustom: isCustom,
},
{
conflictPaths: ['workspaceId', 'name', 'objectMetadataId'],
skipUpdateIfNoValuesChanged: true,
},
);
result = await this.indexMetadataRepository.save({
name: indexName,
indexFieldMetadatas: fieldMetadataToIndex.map(
(fieldMetadata, index) => ({
fieldMetadataId: fieldMetadata.id,
order: index,
}),
),
workspaceId,
objectMetadataId: objectMetadata.id,
...(isDefined(indexType) ? { indexType } : {}),
isCustom,
});
} catch (error) {
throw new Error(
`Failed to create index ${indexName} on object metadata ${objectMetadata.nameSingular}`,
);
}
if (!result.identifiers.length) {
if (!result) {
throw new Error(
`Failed to return saved index ${indexName} on object metadata ${objectMetadata.nameSingular}`,
);
}
await this.createIndexCreationMigration(
workspaceId,
objectMetadata,
fieldMetadataToIndex,
isUnique,
isCustom,
indexType,
indexWhereClause,
);
}
async createIndexCreationMigration(
workspaceId: string,
objectMetadata: ObjectMetadataEntity,
fieldMetadataToIndex: Partial<FieldMetadataEntity>[],
isUnique: boolean,
isCustom: boolean,
indexType?: IndexType,
indexWhereClause?: string,
) {
const tableName = computeObjectTargetTable(objectMetadata);
const columnNames: string[] = fieldMetadataToIndex.map(
(fieldMetadata) => fieldMetadata.name as string,
);
const indexName = `IDX_${generateDeterministicIndexName([tableName, ...columnNames])}`;
const migration = {
name: tableName,
action: WorkspaceMigrationTableActionType.ALTER_INDEXES,

View File

@ -149,7 +149,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
);
}
await this.indexMetadataService.createIndex(
await this.indexMetadataService.createIndexMetadata(
relationMetadataInput.workspaceId,
toObjectMetadata,
[foreignKeyFieldMetadata, deletedFieldMetadata],

View File

@ -105,7 +105,7 @@ export class SearchService {
],
);
await this.indexMetadataService.createIndex(
await this.indexMetadataService.createIndexMetadata(
objectMetadataInput.workspaceId,
createdObjectMetadata,
[searchVectorFieldMetadata],
@ -157,7 +157,7 @@ export class SearchService {
);
// index needs to be recreated as typeorm deletes then recreates searchVector column at alter
await this.indexMetadataService.createIndex(
await this.indexMetadataService.createIndexCreationMigration(
workspaceId,
objectMetadata,
[existingSearchVectorFieldMetadata],