From d4457d756cf847cd3615bd0f33fb708b481f6d6c Mon Sep 17 00:00:00 2001 From: Weiko Date: Fri, 18 Oct 2024 18:59:50 +0200 Subject: [PATCH] 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 --- .../index-metadata/index-metadata.service.ts | 84 +++++++++++++------ .../relation-metadata.service.ts | 2 +- .../metadata-modules/search/search.service.ts | 4 +- 3 files changed, 62 insertions(+), 28 deletions(-) diff --git a/packages/twenty-server/src/engine/metadata-modules/index-metadata/index-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/index-metadata/index-metadata.service.ts index 1d75cbd7a..6b8ff8fcb 100644 --- a/packages/twenty-server/src/engine/metadata-modules/index-metadata/index-metadata.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/index-metadata/index-metadata.service.ts @@ -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[], @@ -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[], + 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, diff --git a/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.service.ts index 90291ad65..d9a1850a5 100644 --- a/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.service.ts @@ -149,7 +149,7 @@ export class RelationMetadataService extends TypeOrmQueryService