Index v2 side effects (#14567)

# Introduction
Honestly this implem is a mess, discussing a potential side effect
handler with @weiko before the build and run that would handle each side
effect per entity and operation

Handling:
- [ ] unique index is generated when a field is updated with the
`isUnique`
- [x] an index is generated when a relation is created
- [x] search vector index creation on custom object creation
- [x] renaming a field metadata or an object should re-create all
related indexes which are composed by their namings
- [x] delete object should remove any related indexes
- [x] delete field should update related indexes ( if index ends up
empty it should be removed )
- [ ] on object renaming that contains morph fields -> triggers update
field -> trigger index recompute
- [x] on update name renaming should recompute all related indexes

## TODO
- [x] Integration testing
- [ ] Refactor the index maps cache to be storing a
`idsByObjectMetadataId`
- [x] Refactor deterministic name to use order sorting
- [x] Remove flat index from flat object

## What's next

Will handle morph indexes in a new dedicated PR for the moment will
stick to this
Same for the cache improvement and uniqueness
This commit is contained in:
Paul Rastoin
2025-09-23 10:57:11 +02:00
committed by GitHub
parent d0b9c7f076
commit fbd3286792
63 changed files with 2591 additions and 1323 deletions
@@ -208,14 +208,14 @@ export class WorkspaceMigrationBuildOrchestratorService {
relatedFlatEntityMapsKeys,
actions: [
// Object and fields
...orchestratorActionsReport.fieldMetadata.deleted,
...orchestratorActionsReport.index.deleted,
...orchestratorActionsReport.fieldMetadata.deleted,
...orchestratorActionsReport.objectMetadata.deleted,
...orchestratorActionsReport.objectMetadata.created,
...orchestratorActionsReport.index.created,
...orchestratorActionsReport.objectMetadata.updated,
...orchestratorActionsReport.fieldMetadata.created,
...orchestratorActionsReport.fieldMetadata.updated,
...orchestratorActionsReport.index.created,
...orchestratorActionsReport.index.updated,
///
@@ -2408,653 +2408,6 @@ exports[`Workspace migration builder object actions test suite It should build a
}
`;
exports[`Workspace migration builder object actions test suite It should build a create_object and create_index actions for each of this fieldMetadata 1`] = `
{
"created": [],
"deleted": [],
"updated": [],
}
`;
exports[`Workspace migration builder object actions test suite It should build a create_object and create_index actions for each of this fieldMetadata 2`] = `
{
"created": [
{
"createFieldActions": [
{
"flatFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": "uuid",
"description": "Id",
"flatRelationTargetFieldMetadata": null,
"flatRelationTargetObjectMetadata": null,
"icon": "Icon123",
"id": Any<String>,
"isActive": true,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": false,
"isSystem": true,
"isUIReadOnly": false,
"isUnique": false,
"label": "Id",
"morphId": null,
"name": "id",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": null,
"relationTargetObjectMetadataId": null,
"settings": null,
"standardId": Any<String>,
"standardOverrides": null,
"type": "UUID",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"type": "create_field",
},
{
"flatFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": "'Untitled'",
"description": "Name",
"flatRelationTargetFieldMetadata": null,
"flatRelationTargetObjectMetadata": null,
"icon": "IconAbc",
"id": Any<String>,
"isActive": true,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": false,
"isSystem": false,
"isUIReadOnly": false,
"isUnique": false,
"label": "Name",
"morphId": null,
"name": "name",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": null,
"relationTargetObjectMetadataId": null,
"settings": null,
"standardId": Any<String>,
"standardOverrides": null,
"type": "TEXT",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"type": "create_field",
},
{
"flatFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": "now",
"description": "Creation date",
"flatRelationTargetFieldMetadata": null,
"flatRelationTargetObjectMetadata": null,
"icon": "IconCalendar",
"id": Any<String>,
"isActive": true,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": false,
"isSystem": false,
"isUIReadOnly": false,
"isUnique": false,
"label": "Creation date",
"morphId": null,
"name": "createdAt",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": null,
"relationTargetObjectMetadataId": null,
"settings": null,
"standardId": Any<String>,
"standardOverrides": null,
"type": "DATE_TIME",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"type": "create_field",
},
{
"flatFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": "now",
"description": "Last time the record was changed",
"flatRelationTargetFieldMetadata": null,
"flatRelationTargetObjectMetadata": null,
"icon": "IconCalendarClock",
"id": Any<String>,
"isActive": true,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": false,
"isSystem": false,
"isUIReadOnly": false,
"isUnique": false,
"label": "Last update",
"morphId": null,
"name": "updatedAt",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": null,
"relationTargetObjectMetadataId": null,
"settings": null,
"standardId": Any<String>,
"standardOverrides": null,
"type": "DATE_TIME",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"type": "create_field",
},
{
"flatFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": null,
"description": "Deletion date",
"flatRelationTargetFieldMetadata": null,
"flatRelationTargetObjectMetadata": null,
"icon": "IconCalendarClock",
"id": Any<String>,
"isActive": true,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": true,
"isSystem": false,
"isUIReadOnly": false,
"isUnique": false,
"label": "Deleted at",
"morphId": null,
"name": "deletedAt",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": null,
"relationTargetObjectMetadataId": null,
"settings": null,
"standardId": Any<String>,
"standardOverrides": null,
"type": "DATE_TIME",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"type": "create_field",
},
{
"flatFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": {
"name": "''",
"source": "'MANUAL'",
},
"description": "The creator of the record",
"flatRelationTargetFieldMetadata": null,
"flatRelationTargetObjectMetadata": null,
"icon": "IconCreativeCommonsSa",
"id": Any<String>,
"isActive": true,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": false,
"isSystem": false,
"isUIReadOnly": false,
"isUnique": false,
"label": "Created by",
"morphId": null,
"name": "createdBy",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": null,
"relationTargetObjectMetadataId": null,
"settings": null,
"standardId": Any<String>,
"standardOverrides": null,
"type": "ACTOR",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"type": "create_field",
},
{
"flatFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": 0,
"description": "Position",
"flatRelationTargetFieldMetadata": null,
"flatRelationTargetObjectMetadata": null,
"icon": "IconHierarchy2",
"id": Any<String>,
"isActive": true,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": false,
"isSystem": true,
"isUIReadOnly": false,
"isUnique": false,
"label": "Position",
"morphId": null,
"name": "position",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": null,
"relationTargetObjectMetadataId": null,
"settings": null,
"standardId": Any<String>,
"standardOverrides": null,
"type": "POSITION",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"type": "create_field",
},
{
"flatFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": null,
"description": "TimelineActivities tied to the Rocket",
"flatRelationTargetFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": null,
"description": "TimelineActivities Rocket",
"icon": "IconBuildingSkyscraper",
"id": Any<String>,
"isActive": true,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": true,
"isSystem": true,
"isUIReadOnly": false,
"isUnique": false,
"label": "Rocket",
"morphId": null,
"name": "rocket",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": Any<String>,
"relationTargetObjectMetadataId": Any<String>,
"settings": {
"joinColumnName": "rocketId",
"onDelete": "CASCADE",
"relationType": "MANY_TO_ONE",
},
"standardId": Any<String>,
"standardOverrides": null,
"type": "RELATION",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"flatRelationTargetObjectMetadata": null,
"icon": "IconTimelineEvent",
"id": Any<String>,
"isActive": true,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": true,
"isSystem": true,
"isUIReadOnly": false,
"isUnique": false,
"label": "TimelineActivities",
"morphId": null,
"name": "timelineActivities",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": Any<String>,
"relationTargetObjectMetadataId": Any<String>,
"settings": {
"relationType": "ONE_TO_MANY",
},
"standardId": Any<String>,
"standardOverrides": null,
"type": "RELATION",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"type": "create_field",
},
{
"flatFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": null,
"description": "Favorites tied to the Rocket",
"flatRelationTargetFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": null,
"description": "Favorites Rocket",
"icon": "IconBuildingSkyscraper",
"id": Any<String>,
"isActive": true,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": true,
"isSystem": true,
"isUIReadOnly": false,
"isUnique": false,
"label": "Rocket",
"morphId": null,
"name": "rocket",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": Any<String>,
"relationTargetObjectMetadataId": Any<String>,
"settings": {
"joinColumnName": "rocketId",
"onDelete": "CASCADE",
"relationType": "MANY_TO_ONE",
},
"standardId": Any<String>,
"standardOverrides": null,
"type": "RELATION",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"flatRelationTargetObjectMetadata": null,
"icon": "IconHeart",
"id": Any<String>,
"isActive": true,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": true,
"isSystem": true,
"isUIReadOnly": false,
"isUnique": false,
"label": "Favorites",
"morphId": null,
"name": "favorites",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": Any<String>,
"relationTargetObjectMetadataId": Any<String>,
"settings": {
"relationType": "ONE_TO_MANY",
},
"standardId": Any<String>,
"standardOverrides": null,
"type": "RELATION",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"type": "create_field",
},
{
"flatFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": null,
"description": "Attachments tied to the Rocket",
"flatRelationTargetFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": null,
"description": "Attachments Rocket",
"icon": "IconBuildingSkyscraper",
"id": Any<String>,
"isActive": true,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": true,
"isSystem": true,
"isUIReadOnly": false,
"isUnique": false,
"label": "Rocket",
"morphId": null,
"name": "rocket",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": Any<String>,
"relationTargetObjectMetadataId": Any<String>,
"settings": {
"joinColumnName": "rocketId",
"onDelete": "CASCADE",
"relationType": "MANY_TO_ONE",
},
"standardId": Any<String>,
"standardOverrides": null,
"type": "RELATION",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"flatRelationTargetObjectMetadata": null,
"icon": "IconFileImport",
"id": Any<String>,
"isActive": true,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": true,
"isSystem": true,
"isUIReadOnly": false,
"isUnique": false,
"label": "Attachments",
"morphId": null,
"name": "attachments",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": Any<String>,
"relationTargetObjectMetadataId": Any<String>,
"settings": {
"relationType": "ONE_TO_MANY",
},
"standardId": Any<String>,
"standardOverrides": null,
"type": "RELATION",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"type": "create_field",
},
{
"flatFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": null,
"description": "NoteTargets tied to the Rocket",
"flatRelationTargetFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": null,
"description": "NoteTargets Rocket",
"icon": "IconBuildingSkyscraper",
"id": Any<String>,
"isActive": true,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": true,
"isSystem": true,
"isUIReadOnly": false,
"isUnique": false,
"label": "Rocket",
"morphId": null,
"name": "rocket",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": Any<String>,
"relationTargetObjectMetadataId": Any<String>,
"settings": {
"joinColumnName": "rocketId",
"onDelete": "CASCADE",
"relationType": "MANY_TO_ONE",
},
"standardId": Any<String>,
"standardOverrides": null,
"type": "RELATION",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"flatRelationTargetObjectMetadata": null,
"icon": "IconCheckbox",
"id": Any<String>,
"isActive": true,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": true,
"isSystem": true,
"isUIReadOnly": false,
"isUnique": false,
"label": "NoteTargets",
"morphId": null,
"name": "noteTargets",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": Any<String>,
"relationTargetObjectMetadataId": Any<String>,
"settings": {
"relationType": "ONE_TO_MANY",
},
"standardId": Any<String>,
"standardOverrides": null,
"type": "RELATION",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"type": "create_field",
},
{
"flatFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": null,
"description": "TaskTargets tied to the Rocket",
"flatRelationTargetFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": null,
"description": "TaskTargets Rocket",
"icon": "IconBuildingSkyscraper",
"id": Any<String>,
"isActive": true,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": true,
"isSystem": true,
"isUIReadOnly": false,
"isUnique": false,
"label": "Rocket",
"morphId": null,
"name": "rocket",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": Any<String>,
"relationTargetObjectMetadataId": Any<String>,
"settings": {
"joinColumnName": "rocketId",
"onDelete": "CASCADE",
"relationType": "MANY_TO_ONE",
},
"standardId": Any<String>,
"standardOverrides": null,
"type": "RELATION",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"flatRelationTargetObjectMetadata": null,
"icon": "IconCheckbox",
"id": Any<String>,
"isActive": true,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": true,
"isSystem": true,
"isUIReadOnly": false,
"isUnique": false,
"label": "TaskTargets",
"morphId": null,
"name": "taskTargets",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": Any<String>,
"relationTargetObjectMetadataId": Any<String>,
"settings": {
"relationType": "ONE_TO_MANY",
},
"standardId": Any<String>,
"standardOverrides": null,
"type": "RELATION",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"type": "create_field",
},
{
"flatFieldMetadata": {
"createdAt": Any<String>,
"defaultValue": null,
"description": "Field used for full-text search",
"flatRelationTargetFieldMetadata": null,
"flatRelationTargetObjectMetadata": null,
"icon": null,
"id": Any<String>,
"isActive": false,
"isCustom": false,
"isLabelSyncedWithName": false,
"isNullable": true,
"isSystem": true,
"isUIReadOnly": false,
"isUnique": false,
"label": "Search vector",
"morphId": null,
"name": "searchVector",
"objectMetadataId": Any<String>,
"options": null,
"relationTargetFieldMetadataId": null,
"relationTargetObjectMetadataId": null,
"settings": null,
"standardId": Any<String>,
"standardOverrides": null,
"type": "TS_VECTOR",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"type": "create_field",
},
],
"flatObjectMetadataWithoutFields": {
"createdAt": Any<String>,
"description": "A rocket",
"duplicateCriteria": null,
"icon": "IconRocket",
"id": Any<String>,
"imageIdentifierFieldMetadataId": null,
"isActive": true,
"isAuditLogged": true,
"isCustom": true,
"isLabelSyncedWithName": false,
"isRemote": false,
"isSearchable": true,
"isSystem": false,
"isUIReadOnly": false,
"labelIdentifierFieldMetadataId": Any<String>,
"labelPlural": "Rockets",
"labelSingular": "Rocket",
"namePlural": "rockets",
"nameSingular": "rocket",
"shortcut": null,
"standardId": null,
"standardOverrides": null,
"targetTableName": "DEPRECATED",
"universalIdentifier": Any<String>,
"updatedAt": Any<String>,
"workspaceId": Any<String>,
},
"type": "create_object",
},
],
"deleted": [],
"updated": [],
}
`;
exports[`Workspace migration builder object actions test suite It should build a delete_object action with custom deactivated object 1`] = `
{
"created": [],
@@ -1,4 +1,3 @@
import { getFlatIndexMetadataMock } from 'src/engine/metadata-modules/flat-index-metadata/__mocks__/get-flat-index-metadata.mock';
import { FLAT_OBJECT_METADATA_MAPS_MOCKS } from 'src/engine/metadata-modules/flat-object-metadata-maps/mocks/flat-object-metadata-maps.mock';
import { deleteObjectFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-object-from-flat-object-metadata-maps-or-throw.util';
import { replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
@@ -93,31 +92,6 @@ const CREATE_OBJECT_TEST_CASES: WorkspaceMigrationBuilderTestCase[] = [
},
},
},
{
title:
'It should build a create_object and create_index actions for each of this fieldMetadata',
context: {
input: {
fromFlatObjectMetadataMaps:
fromFlatObjectMetadatasToFlatObjectMetadataMaps([
...STANDARD_RELATION_TARGET_FLAT_OBJECT_METADATA_MOCKS,
]),
toFlatObjectMetadataMaps:
fromFlatObjectMetadatasToFlatObjectMetadataMaps([
...STANDARD_RELATION_TARGET_FLAT_OBJECT_METADATA_MOCKS,
{
...ROCKET_FLAT_OBJECT_MOCK,
flatIndexMetadatas: [
getFlatIndexMetadataMock({
objectMetadataId: ROCKET_FLAT_OBJECT_MOCK.id,
universalIdentifier: 'field-metadata-unique-identifier-1',
}),
],
},
]),
},
},
},
];
const UPDATE_OBJECT_TEST_CASES: WorkspaceMigrationBuilderTestCase[] = [
@@ -1,6 +1,7 @@
import { Injectable } from '@nestjs/common';
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
import { FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
import { compareTwoFlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/utils/compare-two-flat-index-metadata.util';
import {
@@ -139,7 +140,10 @@ export class WorkspaceMigrationV2IndexActionsBuilderService extends WorkspaceEnt
this.flatIndexValidatorService.validateFlatIndexCreation({
dependencyOptimisticFlatEntityMaps,
flatIndexToValidate: toFlatIndex,
optimisticFlatIndexMaps,
optimisticFlatIndexMaps: deleteFlatEntityFromFlatEntityMapsOrThrow({
entityToDeleteId: fromFlatIndex.id,
flatEntityMaps: optimisticFlatIndexMaps,
}),
});
if (creationValidationResult.errors.length > 0) {
@@ -1,38 +0,0 @@
import { type FromTo } from 'twenty-shared/types';
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
import {
type FlatObjectMetadata,
type FlatObjectMetadataWithoutFields,
} from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import {
type CustomDeletedCreatedUpdatedMatrix,
deletedCreatedUpdatedMatrixDispatcher,
} from 'src/engine/workspace-manager/workspace-migration-v2/utils/deleted-created-updated-matrix-dispatcher.util';
export type UpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix = {
flatObjectMetadata: FlatObjectMetadataWithoutFields;
} & CustomDeletedCreatedUpdatedMatrix<'indexMetadata', FlatIndexMetadata>;
export const computeUpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix = (
updatedObjectMetadata: FromTo<FlatObjectMetadata>[],
): UpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix[] => {
const matrixAccumulator: UpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix[] =
[];
for (const { from, to } of updatedObjectMetadata) {
const indexMetadataMatrix = deletedCreatedUpdatedMatrixDispatcher({
from: from.flatIndexMetadatas,
to: to.flatIndexMetadatas,
});
matrixAccumulator.push({
flatObjectMetadata: to,
createdIndexMetadata: indexMetadataMatrix.created,
deletedIndexMetadata: indexMetadataMatrix.deleted,
updatedIndexMetadata: indexMetadataMatrix.updated,
});
}
return matrixAccumulator;
};
@@ -5,6 +5,5 @@ import {
export const fromFlatObjectMetadataToFlatObjectMetadataWithoutFields = ({
flatFieldMetadatas: _flatFieldMetadatas,
flatIndexMetadatas: _flatIndexMetadatas,
...rest
}: FlatObjectMetadata): FlatObjectMetadataWithoutFields => rest;
@@ -94,7 +94,7 @@ export class FlatIndexValidatorService {
optimisticFlatIndexMaps.byId,
).filter(isDefined);
const existingFlatIndexOnName = allExistingFlatIndex.some(
const existingFlatIndexOnName = allExistingFlatIndex.find(
(flatIndexMetadata) =>
flatIndexMetadata.name.toLocaleUpperCase() ===
flatIndexToValidate.name.toLocaleUpperCase(),
@@ -102,7 +102,7 @@ export class FlatIndexValidatorService {
if (isDefined(existingFlatIndexOnName)) {
validationResult.errors.push({
code: IndexExceptionCode.INDEX_EMPTY_FIELDS,
code: IndexExceptionCode.INDEX_ALREADY_EXISTS,
message: t`Index with same name already exists`,
userFriendlyMessage: t`Index with same name already exists`,
});
@@ -1,13 +1,26 @@
import { Injectable } from '@nestjs/common';
import { isDefined } from 'twenty-shared/utils';
import {
OptimisticallyApplyActionOnAllFlatEntityMapsArgs,
WorkspaceMigrationRunnerActionHandler,
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
import {
WorkspaceQueryRunnerException,
WorkspaceQueryRunnerExceptionCode,
} from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.exception';
import {
FlatEntityMapsException,
FlatEntityMapsExceptionCode,
} from 'src/engine/core-modules/common/exceptions/flat-entity-maps.exception';
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
import { findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-with-field-id-only.util';
import { findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service';
import { type CreateIndexAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-index-action-v2';
@@ -44,15 +57,34 @@ export class CreateIndexActionHandlerService extends WorkspaceMigrationRunnerAct
queryRunner.manager.getRepository<IndexMetadataEntity>(
IndexMetadataEntity,
);
const indexFieldMetadataRepository =
queryRunner.manager.getRepository<IndexFieldMetadataEntity>(
IndexFieldMetadataEntity,
);
const {
flatIndexMetadata: { flatIndexFieldMetadatas, ...rest },
flatIndexMetadata: { flatIndexFieldMetadatas, ...flatIndexMetadata },
} = action;
await indexMetadataRepository.insert({
indexFieldMetadatas: flatIndexFieldMetadatas,
...rest,
});
const indexInsertResult =
await indexMetadataRepository.insert(flatIndexMetadata);
if (indexInsertResult.identifiers.length !== 1) {
throw new WorkspaceQueryRunnerException(
'Failed to create index metadata',
WorkspaceQueryRunnerExceptionCode.INTERNAL_SERVER_ERROR,
);
}
const indexMetadataId = indexInsertResult.identifiers[0].id;
const indexFieldMetadataToInsert = flatIndexFieldMetadatas.map(
(flatIndexFieldMetadata) => ({
...flatIndexFieldMetadata,
indexMetadataId,
}),
);
await indexFieldMetadataRepository.insert(indexFieldMetadataToInsert);
}
async executeForWorkspaceSchema(
@@ -76,7 +108,33 @@ export class CreateIndexActionHandlerService extends WorkspaceMigrationRunnerAct
});
const quotedColumns = flatIndexMetadata.flatIndexFieldMetadatas.map(
(column) => `"${column}"`,
({ fieldMetadataId }) => {
const flatFieldMetadata =
findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId({
fieldMetadataId,
flatObjectMetadataMaps,
});
if (!isDefined(flatFieldMetadata)) {
throw new FlatEntityMapsException(
'Index field related field metadata not found',
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
);
}
if (isMorphOrRelationFlatFieldMetadata(flatFieldMetadata)) {
if (!isDefined(flatFieldMetadata.settings?.joinColumnName)) {
throw new FlatEntityMapsException(
'Join column name is not defined for relation field',
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
);
}
return `"${flatFieldMetadata.settings.joinColumnName}"`;
}
return `"${flatFieldMetadata.name}"`;
},
);
await this.workspaceSchemaManagerService.indexManager.createIndex({
@@ -8,16 +8,22 @@ import {
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
import { findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service';
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
import { type DeleteIndexAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-index-action-v2';
import { type WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
import { getWorkspaceSchemaContextForMigration } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/utils/get-workspace-schema-context-for-migration.util';
@Injectable()
export class DeleteIndexActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
'delete_index',
) {
constructor(
private readonly workspaceSchemaManagerService: WorkspaceSchemaManagerService,
) {
super();
}
optimisticallyApplyActionOnAllFlatEntityMaps({
action,
allFlatEntityMaps: { flatIndexMaps },
@@ -51,7 +57,7 @@ export class DeleteIndexActionHandlerService extends WorkspaceMigrationRunnerAct
): Promise<void> {
const {
action,
allFlatEntityMaps: { flatObjectMetadataMaps, flatIndexMaps },
allFlatEntityMaps: { flatIndexMaps },
queryRunner,
workspaceId,
} = context;
@@ -63,19 +69,12 @@ export class DeleteIndexActionHandlerService extends WorkspaceMigrationRunnerAct
},
);
const flatObjectMetadata =
findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
flatObjectMetadataMaps,
objectMetadataId: flatIndexMetadataToDelete.objectMetadataId,
});
const { schemaName, tableName } = getWorkspaceSchemaContextForMigration({
workspaceId,
flatObjectMetadata,
});
const schemaName = getWorkspaceSchemaName(workspaceId);
await queryRunner.dropIndex(
`${schemaName}.${tableName}`,
flatIndexMetadataToDelete.name,
);
await this.workspaceSchemaManagerService.indexManager.dropIndex({
indexName: flatIndexMetadataToDelete.name,
queryRunner,
schemaName,
});
}
}
@@ -48,7 +48,6 @@ export class CreateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow({
flatObjectMetadata: {
...flatObjectMetadataWithoutFields,
flatIndexMetadatas: [],
flatFieldMetadatas,
},
flatObjectMetadataMaps,