diff --git a/packages/twenty-server/src/database/commands/workspace-export/utils/generate-workspace-schema-ddl.util.ts b/packages/twenty-server/src/database/commands/workspace-export/utils/generate-workspace-schema-ddl.util.ts index 5d6eb18b3d..9e137463fd 100644 --- a/packages/twenty-server/src/database/commands/workspace-export/utils/generate-workspace-schema-ddl.util.ts +++ b/packages/twenty-server/src/database/commands/workspace-export/utils/generate-workspace-schema-ddl.util.ts @@ -7,6 +7,7 @@ import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-fiel import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type'; import { deriveSearchVectorAsExpressionForTsVectorField } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/derive-search-vector-as-expression-for-ts-vector-field.util'; +import { getTargetSearchFieldMetadatasForTsVectorField } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/get-target-search-field-metadatas-for-ts-vector-field.util'; import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants'; import { type SearchFieldMetadataEntity } from 'src/engine/metadata-modules/search-field-metadata/search-field-metadata.entity'; @@ -125,8 +126,11 @@ export const generateWorkspaceSchemaDdl = ( FieldMetadataType.TS_VECTOR, ) ? deriveSearchVectorAsExpressionForTsVectorField({ - tsVectorFieldMetadataId: flatFieldMetadata.id, - flatSearchFieldMetadataMaps, + targetSearchFieldMetadatas: + getTargetSearchFieldMetadatasForTsVectorField({ + tsVectorFieldMetadataId: flatFieldMetadata.id, + flatSearchFieldMetadataMaps, + }), indexedFieldById, }) : undefined, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/__tests__/build-search-field-metadatas-by-ts-vector-field-id.util.spec.ts b/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/__tests__/build-search-field-metadatas-by-ts-vector-field-id.util.spec.ts new file mode 100644 index 0000000000..34604d2291 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/__tests__/build-search-field-metadatas-by-ts-vector-field-id.util.spec.ts @@ -0,0 +1,92 @@ +import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; +import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type'; +import { buildSearchFieldMetadatasByTsVectorFieldId } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/build-search-field-metadatas-by-ts-vector-field-id.util'; +import { getTargetSearchFieldMetadatasForTsVectorField } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/get-target-search-field-metadatas-for-ts-vector-field.util'; + +const buildFlatSearchFieldMetadata = ( + overrides: Partial & + Pick, +): FlatSearchFieldMetadata => + ({ + id: overrides.universalIdentifier, + tsVectorFieldMetadataId: 'ts-vector-1', + fieldMetadataId: 'field-1', + position: 0, + ...overrides, + }) as unknown as FlatSearchFieldMetadata; + +const buildMaps = ( + flatSearchFieldMetadatas: FlatSearchFieldMetadata[], +): FlatEntityMaps => ({ + byUniversalIdentifier: Object.fromEntries( + flatSearchFieldMetadatas.map((flatSearchFieldMetadata) => [ + flatSearchFieldMetadata.universalIdentifier, + flatSearchFieldMetadata, + ]), + ), + universalIdentifierById: {}, + universalIdentifiersByApplicationId: {}, +}); + +describe('buildSearchFieldMetadatasByTsVectorFieldId', () => { + it('groups search fields by their tsVector field id', () => { + const a1 = buildFlatSearchFieldMetadata({ + universalIdentifier: 'a1', + tsVectorFieldMetadataId: 'ts-vector-a', + }); + const a2 = buildFlatSearchFieldMetadata({ + universalIdentifier: 'a2', + tsVectorFieldMetadataId: 'ts-vector-a', + }); + const b1 = buildFlatSearchFieldMetadata({ + universalIdentifier: 'b1', + tsVectorFieldMetadataId: 'ts-vector-b', + }); + + const grouped = buildSearchFieldMetadatasByTsVectorFieldId( + buildMaps([a1, a2, b1]), + ); + + expect(grouped.get('ts-vector-a')).toEqual([a1, a2]); + expect(grouped.get('ts-vector-b')).toEqual([b1]); + expect(grouped.get('ts-vector-unknown')).toBeUndefined(); + }); + + it('matches the one-off filter helper for a given tsVector field', () => { + const maps = buildMaps([ + buildFlatSearchFieldMetadata({ + universalIdentifier: 'a1', + tsVectorFieldMetadataId: 'ts-vector-a', + }), + buildFlatSearchFieldMetadata({ + universalIdentifier: 'b1', + tsVectorFieldMetadataId: 'ts-vector-b', + }), + ]); + + const grouped = buildSearchFieldMetadatasByTsVectorFieldId(maps); + + expect(grouped.get('ts-vector-a')).toEqual( + getTargetSearchFieldMetadatasForTsVectorField({ + tsVectorFieldMetadataId: 'ts-vector-a', + flatSearchFieldMetadataMaps: maps, + }), + ); + }); + + it('ignores undefined map entries', () => { + const maps = buildMaps([ + buildFlatSearchFieldMetadata({ + universalIdentifier: 'a1', + tsVectorFieldMetadataId: 'ts-vector-a', + }), + ]); + + maps.byUniversalIdentifier['ghost'] = undefined; + + const grouped = buildSearchFieldMetadatasByTsVectorFieldId(maps); + + expect(grouped.get('ts-vector-a')).toHaveLength(1); + expect(grouped.size).toBe(1); + }); +}); diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/__tests__/create-search-field-metadatas-by-ts-vector-field-id-accessor.util.spec.ts b/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/__tests__/create-search-field-metadatas-by-ts-vector-field-id-accessor.util.spec.ts new file mode 100644 index 0000000000..b60bcb78db --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/__tests__/create-search-field-metadatas-by-ts-vector-field-id-accessor.util.spec.ts @@ -0,0 +1,86 @@ +import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; +import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type'; +import { createSearchFieldMetadatasByTsVectorFieldIdAccessor } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/create-search-field-metadatas-by-ts-vector-field-id-accessor.util'; + +const buildFlatSearchFieldMetadata = ( + universalIdentifier: string, + tsVectorFieldMetadataId: string, +): FlatSearchFieldMetadata => + ({ + id: universalIdentifier, + universalIdentifier, + tsVectorFieldMetadataId, + fieldMetadataId: `field-${universalIdentifier}`, + position: 0, + }) as unknown as FlatSearchFieldMetadata; + +const buildMaps = ( + flatSearchFieldMetadatas: FlatSearchFieldMetadata[], +): FlatEntityMaps => ({ + byUniversalIdentifier: Object.fromEntries( + flatSearchFieldMetadatas.map((flatSearchFieldMetadata) => [ + flatSearchFieldMetadata.universalIdentifier, + flatSearchFieldMetadata, + ]), + ), + universalIdentifierById: {}, + universalIdentifiersByApplicationId: {}, +}); + +describe('createSearchFieldMetadatasByTsVectorFieldIdAccessor', () => { + it('resolves the search fields for a tsVector field', () => { + const maps = buildMaps([ + buildFlatSearchFieldMetadata('a1', 'ts-vector-a'), + buildFlatSearchFieldMetadata('a2', 'ts-vector-a'), + buildFlatSearchFieldMetadata('b1', 'ts-vector-b'), + ]); + + const accessor = createSearchFieldMetadatasByTsVectorFieldIdAccessor( + () => maps, + ); + + expect(accessor.get('ts-vector-a')).toHaveLength(2); + expect(accessor.get('ts-vector-b')).toHaveLength(1); + expect(accessor.get('ts-vector-unknown')).toEqual([]); + }); + + it('serves a stale result after the map changes until invalidate() is called', () => { + const maps = buildMaps([buildFlatSearchFieldMetadata('a1', 'ts-vector-a')]); + + const accessor = createSearchFieldMetadatasByTsVectorFieldIdAccessor( + () => maps, + ); + + expect(accessor.get('ts-vector-a')).toHaveLength(1); + + maps.byUniversalIdentifier['a2'] = buildFlatSearchFieldMetadata( + 'a2', + 'ts-vector-a', + ); + + expect(accessor.get('ts-vector-a')).toHaveLength(1); + + accessor.invalidate(); + + expect(accessor.get('ts-vector-a')).toHaveLength(2); + }); + + it('rebuilds against the latest maps reference returned by the getter', () => { + let maps = buildMaps([buildFlatSearchFieldMetadata('a1', 'ts-vector-a')]); + + const accessor = createSearchFieldMetadatasByTsVectorFieldIdAccessor( + () => maps, + ); + + expect(accessor.get('ts-vector-a')).toHaveLength(1); + + maps = buildMaps([ + buildFlatSearchFieldMetadata('a1', 'ts-vector-a'), + buildFlatSearchFieldMetadata('a2', 'ts-vector-a'), + ]); + + accessor.invalidate(); + + expect(accessor.get('ts-vector-a')).toHaveLength(2); + }); +}); diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/build-search-field-metadatas-by-ts-vector-field-id.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/build-search-field-metadatas-by-ts-vector-field-id.util.ts new file mode 100644 index 0000000000..b59927efe2 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/build-search-field-metadatas-by-ts-vector-field-id.util.ts @@ -0,0 +1,35 @@ +import { isDefined } from 'twenty-shared/utils'; + +import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; +import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type'; + +export const buildSearchFieldMetadatasByTsVectorFieldId = ( + flatSearchFieldMetadataMaps: FlatEntityMaps, +): Map => { + const searchFieldMetadatasByTsVectorFieldId = new Map< + string, + FlatSearchFieldMetadata[] + >(); + + for (const flatSearchFieldMetadata of Object.values( + flatSearchFieldMetadataMaps.byUniversalIdentifier, + )) { + if (!isDefined(flatSearchFieldMetadata)) { + continue; + } + + const { tsVectorFieldMetadataId } = flatSearchFieldMetadata; + + const existingSearchFieldMetadatas = + searchFieldMetadatasByTsVectorFieldId.get(tsVectorFieldMetadataId) ?? []; + + existingSearchFieldMetadatas.push(flatSearchFieldMetadata); + + searchFieldMetadatasByTsVectorFieldId.set( + tsVectorFieldMetadataId, + existingSearchFieldMetadatas, + ); + } + + return searchFieldMetadatasByTsVectorFieldId; +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/create-search-field-metadatas-by-ts-vector-field-id-accessor.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/create-search-field-metadatas-by-ts-vector-field-id-accessor.util.ts new file mode 100644 index 0000000000..9ee732d550 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/create-search-field-metadatas-by-ts-vector-field-id-accessor.util.ts @@ -0,0 +1,36 @@ +import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; +import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type'; +import { buildSearchFieldMetadatasByTsVectorFieldId } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/build-search-field-metadatas-by-ts-vector-field-id.util'; + +// Scoped alternative to the warmed-up optimistic aggregator design in +// https://github.com/twentyhq/core-team-issues/issues/2622. Prefer implementing +// that centralized cache if a second create-before-parent consumer re-occurs. + +export type SearchFieldMetadatasByTsVectorFieldIdAccessor = { + get: (tsVectorFieldMetadataId: string) => FlatSearchFieldMetadata[]; + invalidate: () => void; +}; + +export const createSearchFieldMetadatasByTsVectorFieldIdAccessor = ( + getFlatSearchFieldMetadataMaps: () => FlatEntityMaps, +): SearchFieldMetadatasByTsVectorFieldIdAccessor => { + let searchFieldMetadatasByTsVectorFieldId: + | Map + | undefined; + + return { + get: (tsVectorFieldMetadataId) => { + searchFieldMetadatasByTsVectorFieldId ??= + buildSearchFieldMetadatasByTsVectorFieldId( + getFlatSearchFieldMetadataMaps(), + ); + + return ( + searchFieldMetadatasByTsVectorFieldId.get(tsVectorFieldMetadataId) ?? [] + ); + }, + invalidate: () => { + searchFieldMetadatasByTsVectorFieldId = undefined; + }, + }; +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/derive-search-vector-as-expression-for-ts-vector-field.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/derive-search-vector-as-expression-for-ts-vector-field.util.ts index 0208cb40d5..56258f95db 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/derive-search-vector-as-expression-for-ts-vector-field.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/derive-search-vector-as-expression-for-ts-vector-field.util.ts @@ -1,7 +1,6 @@ import { type FieldMetadataType } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; -import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; import { buildSearchVectorTargetField, computeSearchVectorAsExpressionFromSearchFieldMetadatas, @@ -9,27 +8,17 @@ import { import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type'; export const deriveSearchVectorAsExpressionForTsVectorField = ({ - tsVectorFieldMetadataId, - flatSearchFieldMetadataMaps, + targetSearchFieldMetadatas, indexedFieldById, }: { - tsVectorFieldMetadataId: string; - flatSearchFieldMetadataMaps: FlatEntityMaps; + targetSearchFieldMetadatas: FlatSearchFieldMetadata[]; indexedFieldById: ReadonlyMap< string, { name: string; type: FieldMetadataType } >; }): string => { - const targetSearchableFields = Object.values( - flatSearchFieldMetadataMaps.byUniversalIdentifier, - ) - .filter(isDefined) - .filter( - (flatSearchFieldMetadata) => - flatSearchFieldMetadata.tsVectorFieldMetadataId === - tsVectorFieldMetadataId, - ) - .flatMap((flatSearchFieldMetadata) => { + const targetSearchableFields = targetSearchFieldMetadatas.flatMap( + (flatSearchFieldMetadata) => { const indexedField = indexedFieldById.get( flatSearchFieldMetadata.fieldMetadataId, ); @@ -45,7 +34,8 @@ export const deriveSearchVectorAsExpressionForTsVectorField = ({ sortKey: flatSearchFieldMetadata.universalIdentifier, }), ]; - }); + }, + ); return computeSearchVectorAsExpressionFromSearchFieldMetadatas( targetSearchableFields, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/get-target-search-field-metadatas-for-ts-vector-field.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/get-target-search-field-metadatas-for-ts-vector-field.util.ts new file mode 100644 index 0000000000..2e171cc632 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-search-field-metadata/utils/get-target-search-field-metadatas-for-ts-vector-field.util.ts @@ -0,0 +1,19 @@ +import { isDefined } from 'twenty-shared/utils'; + +import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; +import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type'; + +export const getTargetSearchFieldMetadatasForTsVectorField = ({ + tsVectorFieldMetadataId, + flatSearchFieldMetadataMaps, +}: { + tsVectorFieldMetadataId: string; + flatSearchFieldMetadataMaps: FlatEntityMaps; +}): FlatSearchFieldMetadata[] => + Object.values(flatSearchFieldMetadataMaps.byUniversalIdentifier) + .filter(isDefined) + .filter( + (flatSearchFieldMetadata) => + flatSearchFieldMetadata.tsVectorFieldMetadataId === + tsVectorFieldMetadataId, + ); diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/field/services/update-field-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/field/services/update-field-action-handler.service.ts index 48c42e4206..f9ff3c6657 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/field/services/update-field-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/field/services/update-field-action-handler.service.ts @@ -25,6 +25,7 @@ import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field- import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util'; import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util'; import { deriveSearchVectorAsExpressionForTsVectorField } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/derive-search-vector-as-expression-for-ts-vector-field.util'; +import { getTargetSearchFieldMetadatasForTsVectorField } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/get-target-search-field-metadatas-for-ts-vector-field.util'; import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service'; import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util'; @@ -180,6 +181,7 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct flatIndexMaps, }, workspaceId, + getSearchFieldMetadatasByTsVectorFieldId, } = context; const { entityId, update } = flatAction; @@ -367,8 +369,14 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct const searchVectorAsExpression = deriveSearchVectorAsExpressionForTsVectorField({ - tsVectorFieldMetadataId: optimisticFlatFieldMetadata.id, - flatSearchFieldMetadataMaps, + targetSearchFieldMetadatas: + getSearchFieldMetadatasByTsVectorFieldId?.( + optimisticFlatFieldMetadata.id, + ) ?? + getTargetSearchFieldMetadatasForTsVectorField({ + tsVectorFieldMetadataId: optimisticFlatFieldMetadata.id, + flatSearchFieldMetadataMaps, + }), indexedFieldById, }); diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/object/services/create-object-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/object/services/create-object-action-handler.service.ts index 7531193b76..d0d85fddcb 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/object/services/create-object-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/object/services/create-object-action-handler.service.ts @@ -10,6 +10,7 @@ import { isCompositeFlatFieldMetadata } from 'src/engine/metadata-modules/flat-f import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-flat-field-metadata.util'; import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util'; import { deriveSearchVectorAsExpressionForTsVectorField } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/derive-search-vector-as-expression-for-ts-vector-field.util'; +import { getTargetSearchFieldMetadatasForTsVectorField } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/get-target-search-field-metadatas-for-ts-vector-field.util'; import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service'; import { FlatCreateObjectAction, @@ -120,7 +121,13 @@ export class CreateObjectActionHandlerService extends WorkspaceMigrationRunnerAc async executeForWorkspaceSchema( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId, allFlatEntityMaps } = context; + const { + flatAction, + queryRunner, + workspaceId, + allFlatEntityMaps, + getSearchFieldMetadatasByTsVectorFieldId, + } = context; const { flatEntity: flatObjectMetadata, flatFieldMetadatas } = flatAction; const { schemaName, tableName } = getWorkspaceSchemaContextForMigration({ @@ -145,9 +152,15 @@ export class CreateObjectActionHandlerService extends WorkspaceMigrationRunnerAc FieldMetadataType.TS_VECTOR, ) ? deriveSearchVectorAsExpressionForTsVectorField({ - tsVectorFieldMetadataId: flatFieldMetadata.id, - flatSearchFieldMetadataMaps: - allFlatEntityMaps.flatSearchFieldMetadataMaps, + targetSearchFieldMetadatas: + getSearchFieldMetadatasByTsVectorFieldId?.( + flatFieldMetadata.id, + ) ?? + getTargetSearchFieldMetadatasForTsVectorField({ + tsVectorFieldMetadataId: flatFieldMetadata.id, + flatSearchFieldMetadataMaps: + allFlatEntityMaps.flatSearchFieldMetadataMaps, + }), indexedFieldById, }) : undefined, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/services/workspace-migration-runner.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/services/workspace-migration-runner.service.ts index c61caac76d..58fefd7c2f 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/services/workspace-migration-runner.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/services/workspace-migration-runner.service.ts @@ -13,6 +13,7 @@ import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-e import { getMetadataRelatedMetadataNamesForValidation } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-related-metadata-names-for-validation.util'; import { getMetadataRelatedMetadataNames } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-related-metadata-names.util'; import { getMetadataSerializedRelationNames } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-serialized-relation-names.util'; +import { createSearchFieldMetadatasByTsVectorFieldIdAccessor } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/create-search-field-metadatas-by-ts-vector-field-id-accessor.util'; import { FIND_ALL_VIEWS_GRAPHQL_OPERATION } from 'src/engine/metadata-modules/view/constants/find-all-views-graphql-operation.constant'; import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service'; import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service'; @@ -310,6 +311,11 @@ export class WorkspaceMigrationRunnerService { let slowestActionLabel = 'n/a'; let actionCount = 0; + const searchFieldMetadatasByTsVectorFieldIdAccessor = + createSearchFieldMetadatasByTsVectorFieldIdAccessor( + () => allFlatEntityMaps.flatSearchFieldMetadataMaps, + ); + try { await queryRunner.query(`SET LOCAL lock_timeout = '8s'`); @@ -330,6 +336,8 @@ export class WorkspaceMigrationRunnerService { queryRunner, workspaceId, preallocatedIdByUniversalIdentifierByMetadataName, + getSearchFieldMetadatasByTsVectorFieldId: + searchFieldMetadatasByTsVectorFieldIdAccessor.get, }, }, ); @@ -355,6 +363,10 @@ export class WorkspaceMigrationRunnerService { ...partialOptimisticCache, } as typeof allFlatEntityMaps; + if (action.metadataName === 'searchFieldMetadata') { + searchFieldMetadatasByTsVectorFieldIdAccessor.invalidate(); + } + allMetadataEvents.push(...metadataEvents); allAfterCommitSideEffects.push(...afterCommitSideEffects); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/types/workspace-migration-action-runner-args.type.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/types/workspace-migration-action-runner-args.type.ts index 80c12593d8..524a3c1da8 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/types/workspace-migration-action-runner-args.type.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/types/workspace-migration-action-runner-args.type.ts @@ -2,6 +2,7 @@ import { type QueryRunner } from 'typeorm'; import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type'; import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type'; +import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type'; import { type PreallocatedIdByUniversalIdentifierByMetadataName } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util'; import { type AllFlatWorkspaceMigrationAction, @@ -17,6 +18,9 @@ export type WorkspaceMigrationActionRunnerArgs< workspaceId: string; flatApplication: FlatApplication; preallocatedIdByUniversalIdentifierByMetadataName?: PreallocatedIdByUniversalIdentifierByMetadataName; + getSearchFieldMetadatasByTsVectorFieldId?: ( + tsVectorFieldMetadataId: string, + ) => FlatSearchFieldMetadata[]; }; export type WorkspaceMigrationActionRunnerContext<