Deprecate asExpression from field metadata search_vector (#22287)

## Summary

Fully deprecates the cached `asExpression` / `generatedType` settings on
`TS_VECTOR` (searchVector) fields. Previously the generated-column
expression was stored in `FieldMetadataSettings` and kept in sync via
imperative recompute side-effects. It is now **derived at DDL time**
from the `searchFieldMetadata` rows that describe which fields feed the
search vector, making `searchFieldMetadata` the single source of truth
and removing a whole class of cache-drift bugs.

This is delivered across the milestones tracked in #2587 and coordinates
with the frontend migration (#1428).

## Why

- The searchVector expression lived in two places (stored
`settings.asExpression` + the actual generated column), kept consistent
by bespoke side-effects (`recompute-search-vector-on-field-rename`,
label-identifier recompute, etc.).
- The frontend reconstructed the searchable-fields list by
**regex-parsing** the stored `asExpression`.
- Both are brittle. Deriving the expression from `searchFieldMetadata`
rows at build/run time removes the cache and the parsing.

## What changed

### Server - data model & derivation
- Introduce the `tsVectorFieldMetadata` relation on
`searchFieldMetadata` (`tsVectorFieldMetadataId` / universal identifier)
linking each searchable-field row to its target `TS_VECTOR` field.
- New runtime derivation
`deriveSearchVectorAsExpressionForTsVectorField`
(`flat-search-field-metadata/utils/...`) used by the create-object and
update-field handlers to generate the column expression from
`searchFieldMetadata` rows.
- Remove `asExpression` / `generatedType` from stored settings:
`FieldMetadataSettings.TS_VECTOR` is now `null`; the column builder
(`generate-column-definitions.util.ts`) hardcodes `generatedType:
'STORED'` and requires the derived expression.
- Delete the imperative recompute side-effects and the
`compute-search-vector-universal-settings-from-object-manifest` path;
drop the `settings` block from all 28 standard
`compute-*-standard-flat-field-metadata` utils.

### Server - migration runner
- New `rebuildSearchVector` marker on `update-field` actions: the
orchestrator synthesizes targeted column rebuilds
(`compute-search-vector-rebuild-target-universal-identifiers.util.ts` +
the deprioritize aggregator) only when a searchFieldMetadata change or
indexed-field rename actually requires it - instead of rebuilding on
every settings touch.
- Deferrable FKs + in-flight ID resolution so a `searchFieldMetadata`
row and its `TS_VECTOR` field can be created in the same transaction
(deterministic UUIDs).

### Frontend (contract change, #1428)
- New `SearchFieldMetadataDTO` + dataloader exposing
`searchFieldMetadataList` on object metadata.
- `SettingsObjectSearchSection` now reads
`objectMetadataItem.searchFieldMetadatas` instead of parsing
`asExpression`; new `SearchFieldMetadataItem` type, fragment, and
mapping updates.

### Upgrade commands (2.18)
-
`2-18-instance-command-fast-...-add-ts-vector-field-metadata-id-to-search-field-metadata`
-
`2-18-instance-command-fast-...-make-search-field-metadata-fks-deferrable`
-
`2-18-instance-command-slow-...-backfill-ts-vector-field-metadata-id-on-search-field-metadata`

(These were relocated from 2.16 to 2.18 and re-timestamped into an
ordered block - add column -> make FK deferrable -> backfill data -
since 2.16/2.17 are released.)

### Tests
- Updated search-vector side-effect integration specs to assert behavior
(search works) rather than the now-removed `asExpression`; removed the
obsolete expression-validation specs; refreshed the application-sync
snapshot (`universalSettings: null`).

## Upgrade / compatibility notes
- Existing workspaces keep their stored `settings` until a later
cleanup; nothing reads it anymore. The new derivation drives all DDL
going forward.
- Schema changes are gated behind the 2.18 instance commands above.

## Known follow-up (separate PR)
https://github.com/twentyhq/core-team-issues/issues/2620
- The column rebuild (`DROP`/`ADD` of the `searchVector` STORED column)
cascade-drops its GIN index and does not recreate it - a pre-existing
regression on `main` inherited here. A follow-up PR will fix the rebuild
handler to recreate the GIN index and add a 2.18 workspace command to
recompute every search vector + strip the deprecated settings.
(Planned.)

## Test plan
- [ ] `npx nx typecheck twenty-server` / `twenty-front`
- [ ] `npx nx lint:diff-with-main twenty-server` / `twenty-front`
- [ ] Server integration: create/update/delete field, rename indexed
field, update object - search returns expected records
- [ ] Run the 2.18 instance commands on a seeded DB; verify
`tsVectorFieldMetadataId` backfilled and FKs deferrable
- [ ] Frontend: object Search settings tab lists the correct searchable
fields (no `asExpression` parsing)

close https://github.com/twentyhq/core-team-issues/issues/2587

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22287?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Paul Rastoin
2026-06-30 11:16:01 +02:00
committed by GitHub
parent 68c33a37ad
commit b0d7516951
144 changed files with 2030 additions and 1759 deletions
@@ -15,9 +15,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildAttachmentStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -327,12 +324,6 @@ export const buildAttachmentStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -14,9 +14,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildBlocklistStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -190,12 +187,6 @@ export const buildBlocklistStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -14,9 +14,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildCalendarChannelEventAssociationStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -188,12 +185,6 @@ export const buildCalendarChannelEventAssociationStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -14,9 +14,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildCalendarEventParticipantStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -186,12 +183,6 @@ export const buildCalendarEventParticipantStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -13,9 +13,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildCalendarEventStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -182,12 +179,6 @@ export const buildCalendarEventStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -14,10 +14,8 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { CallRecordingRequestStatus } from 'src/modules/call-recording/common/enums/call-recording-request-status.enum';
import { CallRecordingStatus } from 'src/modules/call-recording/common/enums/call-recording-status.enum';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildCallRecordingStandardFlatFieldMetadatas = ({
now,
@@ -486,12 +484,6 @@ export const buildCallRecordingStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -15,9 +15,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildCompanyStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -279,12 +276,6 @@ export const buildCompanyStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -14,9 +14,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildDashboardStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -219,12 +216,6 @@ export const buildDashboardStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -14,9 +14,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildMessageCampaignStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -160,12 +157,6 @@ export const buildMessageCampaignStandardFlatFieldMetadatas = ({
icon: 'IconSend',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
}),
subject: createStandardFieldFlatMetadata({
@@ -14,9 +14,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildMessageChannelMessageAssociationMessageFolderStandardFlatFieldMetadatas =
({
now,
@@ -192,12 +189,6 @@ export const buildMessageChannelMessageAssociationMessageFolderStandardFlatField
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -14,8 +14,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
import { MessageDirection } from 'src/modules/messaging/common/enums/message-direction.enum';
export const buildMessageChannelMessageAssociationStandardFlatFieldMetadatas =
@@ -193,12 +191,6 @@ export const buildMessageChannelMessageAssociationStandardFlatFieldMetadatas =
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -14,9 +14,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildMessageListMemberStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -234,12 +231,6 @@ export const buildMessageListMemberStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -15,9 +15,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildMessageListStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -269,12 +266,6 @@ export const buildMessageListStandardFlatFieldMetadatas = ({
icon: 'IconUsersGroup',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -15,9 +15,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildMessageParticipantStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -187,12 +184,6 @@ export const buildMessageParticipantStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -14,9 +14,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildMessageStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -183,12 +180,6 @@ export const buildMessageStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -13,9 +13,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildMessageThreadStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -182,12 +179,6 @@ export const buildMessageThreadStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -14,9 +14,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildNoteStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -224,12 +221,6 @@ export const buildNoteStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -15,9 +15,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildNoteTargetStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -191,12 +188,6 @@ export const buildNoteTargetStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -15,9 +15,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildOpportunityStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -295,12 +292,6 @@ export const buildOpportunityStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -17,9 +17,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildPersonStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -538,12 +535,6 @@ export const buildPersonStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -15,9 +15,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildTaskStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -281,12 +278,6 @@ export const buildTaskStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -15,9 +15,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildTaskTargetStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -191,12 +188,6 @@ export const buildTaskTargetStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -15,9 +15,6 @@ import {
type CreateStandardFieldArgs,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildTimelineActivityStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -597,12 +594,6 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -14,9 +14,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildWorkflowAutomatedTriggerStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -186,12 +183,6 @@ export const buildWorkflowAutomatedTriggerStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -15,9 +15,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildWorkflowRunStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -360,12 +357,6 @@ export const buildWorkflowRunStandardFlatFieldMetadatas = ({
isSystem: true,
isNullable: true,
isUIEditable: false,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -14,9 +14,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildWorkflowStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -258,12 +255,6 @@ export const buildWorkflowStandardFlatFieldMetadatas = ({
icon: 'IconUser',
isSystem: true,
isNullable: true,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -15,9 +15,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
export const buildWorkflowVersionStandardFlatFieldMetadatas = ({
now,
objectName,
@@ -288,12 +285,6 @@ export const buildWorkflowVersionStandardFlatFieldMetadatas = ({
isSystem: true,
isNullable: true,
isUIEditable: false,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -14,8 +14,6 @@ import {
createStandardFieldFlatMetadata,
} from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME } from 'src/engine/workspace-manager/twenty-standard-application/constants/search-fields-by-standard-object-name.constant';
import { WorkspaceMemberNumberFormatEnum } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
export const buildWorkspaceMemberStandardFlatFieldMetadatas = ({
@@ -457,12 +455,6 @@ export const buildWorkspaceMemberStandardFlatFieldMetadatas = ({
isSystem: true,
isNullable: true,
isUIEditable: false,
settings: {
generatedType: 'STORED',
asExpression: getTsVectorColumnExpressionFromFields(
SEARCH_FIELDS_BY_STANDARD_OBJECT_NAME[objectName],
),
},
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
@@ -117,6 +117,8 @@ export const createStandardFieldFlatMetadata = <
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
viewSortIds: [],
viewSortUniversalIdentifiers: [],
searchFieldMetadataIds: [],
searchFieldMetadataUniversalIdentifiers: [],
universalSettings: settings ?? null,
};
};
@@ -138,6 +138,8 @@ export const createStandardRelationFieldFlatMetadata = <
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
viewSortIds: [],
viewSortUniversalIdentifiers: [],
searchFieldMetadataIds: [],
searchFieldMetadataUniversalIdentifiers: [],
universalSettings: {
...settings,
...(junctionTargetFieldUniversalIdentifier && {
@@ -3,6 +3,7 @@ import { v4 } from 'uuid';
import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util';
import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type';
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants';
import { type AllStandardObjectFieldName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-field-name.type';
import { type AllStandardObjectName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-name.type';
import { type StandardBuilderArgs } from 'src/engine/workspace-manager/twenty-standard-application/types/metadata-standard-buillder-args.type';
@@ -56,6 +57,14 @@ export const createStandardSearchFieldFlatMetadata = <
flatEntityMaps: flatFieldMetadataMaps,
});
const tsVectorFieldMetadataId =
standardObjectMetadataRelatedEntityIds[objectName].fields[
SEARCH_VECTOR_FIELD.name as AllStandardObjectFieldName<O>
].id;
const tsVectorFieldMetadataUniversalIdentifier =
objectFields[SEARCH_VECTOR_FIELD.name as keyof typeof objectFields]
.universalIdentifier;
return {
id: v4(),
universalIdentifier: v4(),
@@ -66,6 +75,8 @@ export const createStandardSearchFieldFlatMetadata = <
objectMetadataUniversalIdentifier: flatObjectMetadata.universalIdentifier,
fieldMetadataId,
fieldMetadataUniversalIdentifier: flatFieldMetadata.universalIdentifier,
tsVectorFieldMetadataId,
tsVectorFieldMetadataUniversalIdentifier,
position,
workspaceId,
createdAt: now,
@@ -23,15 +23,14 @@ const buildFieldIdByUniversalIdentifierForObjectAction = ({
};
for (const universalFlatFieldMetadata of action.universalFlatFieldMetadatas) {
const { universalIdentifier } = universalFlatFieldMetadata;
const providedFieldId =
fieldMetadataIdByUniversalIdentifier[
universalFlatFieldMetadata.universalIdentifier
];
fieldMetadataIdByUniversalIdentifier[universalIdentifier];
if (isDefined(providedFieldId)) {
fieldIdByUniversalIdentifier[
universalFlatFieldMetadata.universalIdentifier
] = providedFieldId;
fieldIdByUniversalIdentifier[universalIdentifier] = providedFieldId;
} else if (!isDefined(fieldIdByUniversalIdentifier[universalIdentifier])) {
fieldIdByUniversalIdentifier[universalIdentifier] = v4();
}
}
@@ -200,6 +199,7 @@ export const enrichCreateWorkspaceMigrationActionsWithIds = ({
if (
action.metadataName !== 'fieldMetadata' &&
action.metadataName !== 'objectMetadata' &&
!isDefined(idByUniversalIdentifier) &&
!isDefined(fieldMetadataIdByUniversalIdentifier) &&
!isDefined(pageLayoutTabIdByUniversalIdentifier)
@@ -209,17 +209,18 @@ export const enrichCreateWorkspaceMigrationActionsWithIds = ({
switch (action.metadataName) {
case 'objectMetadata': {
const id = isDefined(idByUniversalIdentifier)
? idByUniversalIdentifier[action.flatEntity.universalIdentifier]
: undefined;
const objectFieldIdByUniversalIdentifier = isDefined(
fieldMetadataIdByUniversalIdentifier,
)
? buildFieldIdByUniversalIdentifierForObjectAction({
action,
fieldMetadataIdByUniversalIdentifier,
})
: undefined;
const id =
(isDefined(idByUniversalIdentifier)
? idByUniversalIdentifier[action.flatEntity.universalIdentifier]
: undefined) ??
action.id ??
v4();
const objectFieldIdByUniversalIdentifier =
buildFieldIdByUniversalIdentifierForObjectAction({
action,
fieldMetadataIdByUniversalIdentifier:
fieldMetadataIdByUniversalIdentifier ?? {},
});
return {
...action,
@@ -21,6 +21,7 @@ import { AllUniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspa
import { type MetadataUniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-maps.type';
import { aggregateOrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration/utils/aggregate-orchestrator-actions-report.util';
import { computeOrderedMigrationActions } from 'src/engine/workspace-manager/workspace-migration/utils/compute-ordered-migration-actions.util';
import { computeSearchVectorRebuildTargetUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/utils/compute-search-vector-rebuild-target-universal-identifiers.util';
import { crossEntityTransversalValidation } from 'src/engine/workspace-manager/workspace-migration/utils/cross-entity-transversal-validation.util';
import { mergeOrchestratorFailureReports } from 'src/engine/workspace-manager/workspace-migration/utils/merge-orchestrator-failure-reports.util';
import { WorkspaceMigrationAgentActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/agent/workspace-migration-agent-actions-builder.service';
@@ -390,11 +391,23 @@ export class WorkspaceMigrationBuildOrchestratorService {
};
}
const searchVectorUniversalIdentifiersToRebuild =
computeSearchVectorRebuildTargetUniversalIdentifiers({
orchestratorActionsReport,
fromFlatSearchFieldMetadataMaps:
fromToAllFlatEntityMaps.flatSearchFieldMetadataMaps?.from,
toFlatSearchFieldMetadataMaps:
optimisticAllFlatEntityMaps.flatSearchFieldMetadataMaps,
toFlatFieldMetadataMaps:
optimisticAllFlatEntityMaps.flatFieldMetadataMaps,
});
const { aggregatedOrchestratorActionsReport } =
aggregateOrchestratorActionsReport({
orchestratorActionsReport,
flatFieldMetadataMaps:
optimisticAllFlatEntityMaps.flatFieldMetadataMaps,
searchVectorUniversalIdentifiersToRebuild,
});
return {
@@ -4,4 +4,5 @@ import { type MetadataUniversalFlatEntityMaps } from 'src/engine/workspace-manag
export type AggregateOrchestratorActionsReportArgs = {
orchestratorActionsReport: OrchestratorActionsReport;
flatFieldMetadataMaps?: MetadataUniversalFlatEntityMaps<'fieldMetadata'>;
searchVectorUniversalIdentifiersToRebuild?: Set<string>;
};
@@ -14,6 +14,7 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_FOREIGN_KEY_AGGREGATOR_PROPERTIES should matc
"kanbanAggregateOperationViewUniversalIdentifiers",
"calendarViewUniversalIdentifiers",
"mainGroupByFieldMetadataViewUniversalIdentifiers",
"searchFieldMetadataUniversalIdentifiers",
],
"fieldPermission": [],
"frontComponent": [],
@@ -37,6 +37,8 @@ exports[`flatEntityDeletedCreatedUpdatedMatrixDispatcher It should detect a crea
"relationTargetFieldMetadataUniversalIdentifier": null,
"relationTargetObjectMetadataId": null,
"relationTargetObjectMetadataUniversalIdentifier": null,
"searchFieldMetadataIds": [],
"searchFieldMetadataUniversalIdentifiers": [],
"settings": null,
"standardOverrides": null,
"type": "TEXT",
@@ -102,6 +104,8 @@ exports[`flatEntityDeletedCreatedUpdatedMatrixDispatcher It should detect a dele
"relationTargetFieldMetadataUniversalIdentifier": null,
"relationTargetObjectMetadataId": null,
"relationTargetObjectMetadataUniversalIdentifier": null,
"searchFieldMetadataIds": [],
"searchFieldMetadataUniversalIdentifiers": [],
"settings": null,
"standardOverrides": null,
"type": "TEXT",
@@ -181,6 +185,8 @@ exports[`flatEntityDeletedCreatedUpdatedMatrixDispatcher It should detect create
"relationTargetFieldMetadataUniversalIdentifier": null,
"relationTargetObjectMetadataId": null,
"relationTargetObjectMetadataUniversalIdentifier": null,
"searchFieldMetadataIds": [],
"searchFieldMetadataUniversalIdentifiers": [],
"settings": null,
"standardOverrides": null,
"type": "TEXT",
@@ -232,6 +238,8 @@ exports[`flatEntityDeletedCreatedUpdatedMatrixDispatcher It should detect create
"relationTargetFieldMetadataUniversalIdentifier": null,
"relationTargetObjectMetadataId": null,
"relationTargetObjectMetadataUniversalIdentifier": null,
"searchFieldMetadataIds": [],
"searchFieldMetadataUniversalIdentifiers": [],
"settings": null,
"standardOverrides": null,
"type": "TEXT",
@@ -11,6 +11,7 @@ import { type MetadataRelatedFlatEntityMapsKeys } from 'src/engine/metadata-modu
import { type MetadataUniversalFlatEntityAndRelatedUniversalFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-types.type';
import { type MetadataUniversalFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-universal-flat-entity.type';
import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util';
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util';
import { type UniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-maps.type';
import { addUniversalFlatEntityToUniversalFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/add-universal-flat-entity-to-universal-flat-entity-maps-through-mutation-or-throw.util';
@@ -22,6 +23,7 @@ type AddUniversalFlatEntityToUniversalFlatEntityAndRelatedEntityMapsThroughMutat
metadataName: T;
universalFlatEntity: MetadataUniversalFlatEntity<T>;
universalFlatEntityAndRelatedMapsToMutate: MetadataUniversalFlatEntityAndRelatedUniversalFlatEntityMaps<T>;
skipMissingRelatedEntities?: boolean;
};
export const addUniversalFlatEntityToUniversalFlatEntityAndRelatedEntityMapsThroughMutationOrThrow =
@@ -29,6 +31,7 @@ export const addUniversalFlatEntityToUniversalFlatEntityAndRelatedEntityMapsThro
metadataName,
universalFlatEntity,
universalFlatEntityAndRelatedMapsToMutate,
skipMissingRelatedEntities = false,
}: AddUniversalFlatEntityToUniversalFlatEntityAndRelatedEntityMapsThroughMutationOrThrowArgs<T>) => {
const flatEntityMapsKey = getMetadataFlatEntityMapsKey(metadataName);
@@ -96,6 +99,18 @@ export const addUniversalFlatEntityToUniversalFlatEntityAndRelatedEntityMapsThro
continue;
}
if (skipMissingRelatedEntities) {
const maybeRelatedUniversalFlatEntity =
findFlatEntityByUniversalIdentifier({
universalIdentifier: universalForeignKeyValue,
flatEntityMaps: relatedUniversalFlatEntityMaps,
});
if (!isDefined(maybeRelatedUniversalFlatEntity)) {
continue;
}
}
const relatedUniversalFlatEntity =
findFlatEntityByUniversalIdentifierOrThrow({
universalIdentifier: universalForeignKeyValue,
@@ -13,6 +13,10 @@ import { type MetadataToFlatEntityMapsKey } from 'src/engine/metadata-modules/fl
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util';
export type PreallocatedIdByUniversalIdentifierByMetadataName = Partial<
Record<AllMetadataName, Record<string, string>>
>;
type ManyToOneRelationsConfig<T extends AllMetadataName> =
(typeof ALL_MANY_TO_ONE_METADATA_RELATIONS)[T];
@@ -74,6 +78,7 @@ export const resolveUniversalRelationIdentifiersToIds = <
metadataName,
universalForeignKeyValues,
flatEntityMaps,
preallocatedIdByUniversalIdentifierByMetadataName,
}: {
metadataName: T;
universalForeignKeyValues: Record<TProvidedKeys, string | null | undefined>;
@@ -81,6 +86,7 @@ export const resolveUniversalRelationIdentifiersToIds = <
T,
TProvidedKeys
>;
preallocatedIdByUniversalIdentifierByMetadataName?: PreallocatedIdByUniversalIdentifierByMetadataName;
}): ResolvedForeignKeyIds<T, TProvidedKeys> => {
const relationEntries = ALL_MANY_TO_ONE_METADATA_RELATIONS[metadataName];
const result: Record<string, string | null> = {};
@@ -131,6 +137,18 @@ export const resolveUniversalRelationIdentifiersToIds = <
continue;
}
const preallocatedId = isDefined(universalIdentifierValue)
? preallocatedIdByUniversalIdentifierByMetadataName?.[
targetMetadataName
]?.[universalIdentifierValue]
: undefined;
if (isDefined(preallocatedId)) {
result[foreignKey] = preallocatedId;
continue;
}
const targetEntity = findFlatEntityByUniversalIdentifier<
MetadataFlatEntity<typeof targetMetadataName>
>({
@@ -4,15 +4,22 @@ import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants';
import { type AggregateOrchestratorActionsReportArgs } from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-aggregate-orchestrator-actions-report-args.type';
import { type OrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-orchestrator.type';
import { WORKSPACE_MIGRATION_ACTION_TYPE } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/constants/workspace-migration-action-type.constant';
import { type UniversalUpdateFieldAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/field/types/workspace-migration-field-action';
export const aggregateOrchestratorActionsReportDeprioritizeSearchVectorUpdateFieldActions =
({
orchestratorActionsReport,
flatFieldMetadataMaps,
searchVectorUniversalIdentifiersToRebuild,
}: AggregateOrchestratorActionsReportArgs): OrchestratorActionsReport => {
assertIsDefinedOrThrow(flatFieldMetadataMaps);
const rebuildTargetUniversalIdentifiers =
searchVectorUniversalIdentifiersToRebuild ?? new Set<string>();
const coveredVectorUniversalIdentifiers = new Set<string>();
const updateFieldActions = orchestratorActionsReport.fieldMetadata.update;
const { searchVectorUpdateFieldActions, otherUpdateFieldActions } =
@@ -30,11 +37,22 @@ export const aggregateOrchestratorActionsReportDeprioritizeSearchVectorUpdateFie
flatFieldMetadata.name === SEARCH_VECTOR_FIELD.name;
if (isSearchVectorUpdateFieldAction) {
coveredVectorUniversalIdentifiers.add(
updateFieldAction.universalIdentifier,
);
const searchVectorUpdateFieldAction =
rebuildTargetUniversalIdentifiers.has(
updateFieldAction.universalIdentifier,
)
? { ...updateFieldAction, rebuildSearchVector: true }
: updateFieldAction;
return {
...acc,
searchVectorUpdateFieldActions: [
...acc.searchVectorUpdateFieldActions,
updateFieldAction,
searchVectorUpdateFieldAction,
],
};
}
@@ -53,11 +71,31 @@ export const aggregateOrchestratorActionsReportDeprioritizeSearchVectorUpdateFie
},
);
const synthesizedSearchVectorUpdateFieldActions: UniversalUpdateFieldAction[] =
[...rebuildTargetUniversalIdentifiers].flatMap(
(vectorUniversalIdentifier) =>
coveredVectorUniversalIdentifiers.has(vectorUniversalIdentifier)
? []
: [
{
type: WORKSPACE_MIGRATION_ACTION_TYPE.update,
metadataName: 'fieldMetadata',
universalIdentifier: vectorUniversalIdentifier,
update: {},
rebuildSearchVector: true,
},
],
);
return {
...orchestratorActionsReport,
fieldMetadata: {
...orchestratorActionsReport.fieldMetadata,
update: [...otherUpdateFieldActions, ...searchVectorUpdateFieldActions],
update: [
...otherUpdateFieldActions,
...searchVectorUpdateFieldActions,
...synthesizedSearchVectorUpdateFieldActions,
],
},
};
};
@@ -6,6 +6,7 @@ import { aggregateRelationFieldPairs } from 'src/engine/workspace-manager/worksp
export const aggregateOrchestratorActionsReport = ({
orchestratorActionsReport,
flatFieldMetadataMaps,
searchVectorUniversalIdentifiersToRebuild,
}: AggregateOrchestratorActionsReportArgs) => {
const aggregatedOrchestratorActionsReport = [
aggregateNonRelationFieldsIntoObjectActions,
@@ -16,6 +17,7 @@ export const aggregateOrchestratorActionsReport = ({
aggregator({
orchestratorActionsReport: currentOrchestratorActionsReport,
flatFieldMetadataMaps,
searchVectorUniversalIdentifiersToRebuild,
}),
orchestratorActionsReport,
);
@@ -5,8 +5,12 @@ export const computeOrderedMigrationActions = (
aggregatedOrchestratorActionsReport: OrchestratorActionsReport,
): AllUniversalWorkspaceMigrationAction[] => {
return [
// Object and fields and indexes
...aggregatedOrchestratorActionsReport.searchFieldMetadata.delete,
...aggregatedOrchestratorActionsReport.searchFieldMetadata.create,
...aggregatedOrchestratorActionsReport.searchFieldMetadata.update,
///
// Object and fields and indexes
...aggregatedOrchestratorActionsReport.index.delete,
...aggregatedOrchestratorActionsReport.fieldMetadata.delete,
...aggregatedOrchestratorActionsReport.objectMetadata.delete,
@@ -16,8 +20,6 @@ export const computeOrderedMigrationActions = (
...aggregatedOrchestratorActionsReport.fieldMetadata.update,
...aggregatedOrchestratorActionsReport.index.create,
...aggregatedOrchestratorActionsReport.index.update.flat(),
...aggregatedOrchestratorActionsReport.searchFieldMetadata.create,
...aggregatedOrchestratorActionsReport.searchFieldMetadata.update,
///
// Views
@@ -0,0 +1,91 @@
import { isDefined } from 'twenty-shared/utils';
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
import { type OrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-orchestrator.type';
import { type MetadataUniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-maps.type';
export const computeSearchVectorRebuildTargetUniversalIdentifiers = ({
orchestratorActionsReport,
fromFlatSearchFieldMetadataMaps,
toFlatSearchFieldMetadataMaps,
toFlatFieldMetadataMaps,
}: {
orchestratorActionsReport: OrchestratorActionsReport;
fromFlatSearchFieldMetadataMaps?: MetadataUniversalFlatEntityMaps<'searchFieldMetadata'>;
toFlatSearchFieldMetadataMaps: MetadataUniversalFlatEntityMaps<'searchFieldMetadata'>;
toFlatFieldMetadataMaps: MetadataUniversalFlatEntityMaps<'fieldMetadata'>;
}): Set<string> => {
const searchFieldMetadataActions =
orchestratorActionsReport.searchFieldMetadata;
const emptyFlatSearchFieldMetadataMaps: MetadataUniversalFlatEntityMaps<'searchFieldMetadata'> =
{ byUniversalIdentifier: {} };
const renamedFieldSearchFieldMetadataUniversalIdentifiers =
orchestratorActionsReport.fieldMetadata.update
.filter((updateAction) => isDefined(updateAction.update.name))
.flatMap(
(updateAction) =>
findFlatEntityByUniversalIdentifier({
flatEntityMaps: toFlatFieldMetadataMaps,
universalIdentifier: updateAction.universalIdentifier,
})?.searchFieldMetadataUniversalIdentifiers ?? [],
);
const candidateFieldMetadataVectorUniversalIdentifiers = new Set<string>(
[
...searchFieldMetadataActions.create.map(
(createAction) =>
createAction.flatEntity.tsVectorFieldMetadataUniversalIdentifier,
),
...searchFieldMetadataActions.update.map(
(updateAction) =>
findFlatEntityByUniversalIdentifier({
flatEntityMaps: toFlatSearchFieldMetadataMaps,
universalIdentifier: updateAction.universalIdentifier,
})?.tsVectorFieldMetadataUniversalIdentifier,
),
...searchFieldMetadataActions.delete.map(
(deleteAction) =>
findFlatEntityByUniversalIdentifier({
flatEntityMaps:
fromFlatSearchFieldMetadataMaps ??
emptyFlatSearchFieldMetadataMaps,
universalIdentifier: deleteAction.universalIdentifier,
})?.tsVectorFieldMetadataUniversalIdentifier,
),
...renamedFieldSearchFieldMetadataUniversalIdentifiers.map(
(searchFieldMetadataUniversalIdentifier) =>
findFlatEntityByUniversalIdentifier({
flatEntityMaps: toFlatSearchFieldMetadataMaps,
universalIdentifier: searchFieldMetadataUniversalIdentifier,
})?.tsVectorFieldMetadataUniversalIdentifier,
),
].filter(isDefined),
);
const fieldUniversalIdentifiersBeingCreatedOrDeleted = new Set<string>([
...orchestratorActionsReport.fieldMetadata.create.map(
(createFieldAction) => createFieldAction.flatEntity.universalIdentifier,
),
...orchestratorActionsReport.objectMetadata.create.flatMap(
(createObjectAction) =>
createObjectAction.universalFlatFieldMetadatas.map(
(universalFlatFieldMetadata) =>
universalFlatFieldMetadata.universalIdentifier,
),
),
...orchestratorActionsReport.fieldMetadata.delete.map(
(deleteFieldAction) => deleteFieldAction.universalIdentifier,
),
]);
return new Set(
[...candidateFieldMetadataVectorUniversalIdentifiers].filter(
(vectorUniversalIdentifier) =>
!fieldUniversalIdentifiersBeingCreatedOrDeleted.has(
vectorUniversalIdentifier,
),
),
);
};
@@ -23,7 +23,9 @@ export type UniversalCreateFieldAction =
};
export type UniversalUpdateFieldAction =
BaseUniversalUpdateWorkspaceMigrationAction<'fieldMetadata'>;
BaseUniversalUpdateWorkspaceMigrationAction<'fieldMetadata'> & {
rebuildSearchVector?: boolean;
};
export type UniversalDeleteFieldAction =
BaseUniversalDeleteWorkspaceMigrationAction<'fieldMetadata'>;
@@ -35,7 +37,9 @@ export type FlatCreateFieldAction =
};
export type FlatUpdateFieldAction =
BaseFlatUpdateWorkspaceMigrationAction<'fieldMetadata'>;
BaseFlatUpdateWorkspaceMigrationAction<'fieldMetadata'> & {
rebuildSearchVector?: boolean;
};
export type FlatDeleteFieldAction =
BaseFlatDeleteWorkspaceMigrationAction<'fieldMetadata'>;
@@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
import { msg, t } from '@lingui/core/macro';
import { ALL_METADATA_NAME } from 'twenty-shared/metadata';
import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
@@ -70,6 +71,39 @@ export class FlatSearchFieldMetadataValidatorService {
});
}
const flatTsVectorFieldMetadata = findFlatEntityByUniversalIdentifier({
universalIdentifier:
flatSearchFieldMetadataToValidate.tsVectorFieldMetadataUniversalIdentifier,
flatEntityMaps: flatFieldMetadataMaps,
});
if (!isDefined(flatTsVectorFieldMetadata)) {
validationResult.errors.push({
code: SearchFieldMetadataExceptionCode.TS_VECTOR_FIELD_METADATA_NOT_FOUND,
message: t`TS_VECTOR field metadata not found`,
userFriendlyMessage: msg`Search vector field not found`,
});
} else {
if (flatTsVectorFieldMetadata.type !== FieldMetadataType.TS_VECTOR) {
validationResult.errors.push({
code: SearchFieldMetadataExceptionCode.INVALID_TS_VECTOR_FIELD_METADATA,
message: t`TS_VECTOR field metadata must be of type TS_VECTOR`,
userFriendlyMessage: msg`Search vector field must be a search vector`,
});
}
if (
flatTsVectorFieldMetadata.objectMetadataUniversalIdentifier !==
flatSearchFieldMetadataToValidate.objectMetadataUniversalIdentifier
) {
validationResult.errors.push({
code: SearchFieldMetadataExceptionCode.INVALID_TS_VECTOR_FIELD_METADATA,
message: t`TS_VECTOR field metadata must belong to the same object`,
userFriendlyMessage: msg`Search vector field must belong to the same object`,
});
}
}
const flatObjectMetadata = findFlatEntityByUniversalIdentifier({
universalIdentifier:
flatSearchFieldMetadataToValidate.objectMetadataUniversalIdentifier,
@@ -15,12 +15,14 @@ import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/
import { computeCompositeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
import { getCompositeTypeOrThrow } from 'src/engine/metadata-modules/field-metadata/utils/get-composite-type-or-throw.util';
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
import { findManyFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps.util';
import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util';
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { isCompositeFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-composite-flat-field-metadata.util';
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 { 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 { 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';
@@ -124,6 +126,7 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
metadataName: 'fieldMetadata',
entityId: flatFieldMetadata.id,
update,
rebuildSearchVector: action.rebuildSearchVector,
};
}
@@ -161,7 +164,11 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
const {
flatAction,
queryRunner,
allFlatEntityMaps: { flatObjectMetadataMaps, flatFieldMetadataMaps },
allFlatEntityMaps: {
flatObjectMetadataMaps,
flatFieldMetadataMaps,
flatSearchFieldMetadataMaps,
},
workspaceId,
} = context;
const { entityId, update } = flatAction;
@@ -239,49 +246,6 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
}
if (isDefined(update.settings)) {
// Handle asExpression/generatedType change (for TS_VECTOR fields)
if (
isFlatFieldMetadataOfType(
optimisticFlatFieldMetadata,
FieldMetadataType.TS_VECTOR,
)
) {
const fromSettings =
optimisticFlatFieldMetadata.settings as FieldMetadataSettingsMapping['TS_VECTOR'];
const toSettings =
update.settings as FieldMetadataSettingsMapping['TS_VECTOR'];
if (
isDefined(toSettings?.asExpression) &&
(toSettings.asExpression !== fromSettings?.asExpression ||
toSettings.generatedType !== fromSettings?.generatedType)
) {
await this.workspaceSchemaManagerService.columnManager.dropColumns({
queryRunner,
schemaName,
tableName,
columnNames: [optimisticFlatFieldMetadata.name],
});
await this.workspaceSchemaManagerService.columnManager.addColumns({
queryRunner,
schemaName,
tableName,
columnDefinitions: [
{
name: optimisticFlatFieldMetadata.name,
type: 'tsvector',
...toSettings,
},
],
});
optimisticFlatFieldMetadata = {
...optimisticFlatFieldMetadata,
settings: toSettings,
};
}
}
// Handle onDelete change (for morph/relation fields) order matters
if (isMorphOrRelationFlatFieldMetadata(optimisticFlatFieldMetadata)) {
const fromSettings = optimisticFlatFieldMetadata.settings;
@@ -357,6 +321,54 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
}
}
}
if (
flatAction.rebuildSearchVector === true &&
isFlatFieldMetadataOfType(
optimisticFlatFieldMetadata,
FieldMetadataType.TS_VECTOR,
)
) {
const indexedFieldById = new Map(
findManyFlatEntityByIdInFlatEntityMaps({
flatEntityMaps: flatFieldMetadataMaps,
flatEntityIds: flatObjectMetadata.fieldIds,
}).map((indexedFlatFieldMetadata) => [
indexedFlatFieldMetadata.id,
{
name: indexedFlatFieldMetadata.name,
type: indexedFlatFieldMetadata.type,
},
]),
);
const searchVectorAsExpression =
deriveSearchVectorAsExpressionForTsVectorField({
tsVectorFieldMetadataId: optimisticFlatFieldMetadata.id,
flatSearchFieldMetadataMaps,
indexedFieldById,
});
const columnDefinitions = generateColumnDefinitions({
flatFieldMetadata: optimisticFlatFieldMetadata,
flatObjectMetadata,
workspaceId,
searchVectorAsExpression,
});
await this.workspaceSchemaManagerService.columnManager.dropColumns({
queryRunner,
schemaName,
tableName,
columnNames: [optimisticFlatFieldMetadata.name],
});
await this.workspaceSchemaManagerService.columnManager.addColumns({
queryRunner,
schemaName,
tableName,
columnDefinitions,
});
}
}
private async handleFieldNameUpdate({
@@ -135,6 +135,7 @@ export const fromUniversalFlatFieldMetadataToFlatFieldMetadata = ({
mainGroupByFieldMetadataViewIds: [],
kanbanAggregateOperationViewIds: [],
viewSortIds: [],
searchFieldMetadataIds: [],
...emptyUniversalForeignKeyAggregators,
};
};
@@ -1,5 +1,6 @@
import { Injectable } from '@nestjs/common';
import { FieldMetadataType } from 'twenty-shared/types';
import { v4 } from 'uuid';
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface';
@@ -7,6 +8,8 @@ import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-mana
import { ALL_METADATA_ENTITY_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-entity-by-metadata-name.constant';
import { isCompositeFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-composite-flat-field-metadata.util';
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 { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service';
import {
FlatCreateObjectAction,
@@ -117,7 +120,7 @@ export class CreateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
async executeForWorkspaceSchema(
context: WorkspaceMigrationActionRunnerContext<FlatCreateObjectAction>,
): Promise<void> {
const { flatAction, queryRunner, workspaceId } = context;
const { flatAction, queryRunner, workspaceId, allFlatEntityMaps } = context;
const { flatEntity: flatObjectMetadata, flatFieldMetadatas } = flatAction;
const { schemaName, tableName } = getWorkspaceSchemaContextForMigration({
@@ -125,11 +128,29 @@ export class CreateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
objectMetadata: flatObjectMetadata,
});
const indexedFieldById = new Map(
flatFieldMetadatas.map((flatFieldMetadata) => [
flatFieldMetadata.id,
{ name: flatFieldMetadata.name, type: flatFieldMetadata.type },
]),
);
const columnDefinitions = flatFieldMetadatas.flatMap((flatFieldMetadata) =>
generateColumnDefinitions({
flatFieldMetadata,
flatObjectMetadata,
workspaceId,
searchVectorAsExpression: isFlatFieldMetadataOfType(
flatFieldMetadata,
FieldMetadataType.TS_VECTOR,
)
? deriveSearchVectorAsExpressionForTsVectorField({
tsVectorFieldMetadataId: flatFieldMetadata.id,
flatSearchFieldMetadataMaps:
allFlatEntityMaps.flatSearchFieldMetadataMaps,
indexedFieldById,
})
: undefined,
}),
);
@@ -4,11 +4,11 @@ import { v4 } from 'uuid';
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface';
import { resolveUniversalRelationIdentifiersToIds } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util';
import {
FlatCreateSearchFieldMetadataAction,
UniversalCreateSearchFieldMetadataAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/search-field-metadata/types/workspace-migration-search-field-metadata-action.type';
import { resolveUniversalRelationIdentifiersToIds } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util';
import {
WorkspaceMigrationActionRunnerArgs,
WorkspaceMigrationActionRunnerContext,
@@ -24,12 +24,14 @@ export class CreateSearchFieldMetadataActionHandlerService extends WorkspaceMigr
allFlatEntityMaps,
flatApplication,
workspaceId,
preallocatedIdByUniversalIdentifierByMetadataName,
}: WorkspaceMigrationActionRunnerArgs<UniversalCreateSearchFieldMetadataAction>): Promise<FlatCreateSearchFieldMetadataAction> {
const { objectMetadataId, fieldMetadataId } =
const { objectMetadataId, fieldMetadataId, tsVectorFieldMetadataId } =
resolveUniversalRelationIdentifiersToIds({
flatEntityMaps: allFlatEntityMaps,
metadataName: action.metadataName,
universalForeignKeyValues: action.flatEntity,
preallocatedIdByUniversalIdentifierByMetadataName,
});
return {
@@ -38,6 +40,7 @@ export class CreateSearchFieldMetadataActionHandlerService extends WorkspaceMigr
...action.flatEntity,
objectMetadataId,
fieldMetadataId,
tsVectorFieldMetadataId,
id: action.id ?? v4(),
applicationId: flatApplication.id,
workspaceId,
@@ -23,6 +23,7 @@ import {
WorkspaceMigrationRunnerExceptionCode,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/exceptions/workspace-migration-runner.exception';
import { WorkspaceMigrationRunnerActionHandlerRegistryService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/registry/workspace-migration-runner-action-handler-registry.service';
import { buildPreallocatedIdByUniversalIdentifierFromActions } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/build-preallocated-id-by-universal-identifier-from-actions.util';
import { type AfterCommitSideEffect } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/types/after-commit-side-effect.type';
import { type MetadataEvent } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/types/metadata-event';
@@ -281,6 +282,9 @@ export class WorkspaceMigrationRunnerService {
});
}
const preallocatedIdByUniversalIdentifierByMetadataName =
buildPreallocatedIdByUniversalIdentifierFromActions(actions);
this.logger.perfTime('Runner', 'Transaction execution');
await queryRunner.connect();
@@ -313,6 +317,7 @@ export class WorkspaceMigrationRunnerService {
allFlatEntityMaps,
queryRunner,
workspaceId,
preallocatedIdByUniversalIdentifierByMetadataName,
},
},
);
@@ -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 PreallocatedIdByUniversalIdentifierByMetadataName } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util';
import {
type AllFlatWorkspaceMigrationAction,
type AllUniversalWorkspaceMigrationAction,
@@ -15,6 +16,7 @@ export type WorkspaceMigrationActionRunnerArgs<
allFlatEntityMaps: AllFlatEntityMaps;
workspaceId: string;
flatApplication: FlatApplication;
preallocatedIdByUniversalIdentifierByMetadataName?: PreallocatedIdByUniversalIdentifierByMetadataName;
};
export type WorkspaceMigrationActionRunnerContext<
@@ -0,0 +1,57 @@
import { isDefined } from 'twenty-shared/utils';
import { type PreallocatedIdByUniversalIdentifierByMetadataName } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util';
import { type AllUniversalWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/workspace-migration-action-common';
export const buildPreallocatedIdByUniversalIdentifierFromActions = (
actions: AllUniversalWorkspaceMigrationAction[],
): PreallocatedIdByUniversalIdentifierByMetadataName => {
const objectMetadata: Record<string, string> = {};
const fieldMetadata: Record<string, string> = {};
for (const action of actions) {
if (action.type !== 'create') {
continue;
}
if (action.metadataName === 'objectMetadata') {
if (isDefined(action.id)) {
objectMetadata[action.flatEntity.universalIdentifier] = action.id;
}
const fieldIdByUniversalIdentifier =
action.fieldIdByUniversalIdentifier ?? {};
for (const universalFlatFieldMetadata of action.universalFlatFieldMetadatas) {
const fieldId =
fieldIdByUniversalIdentifier[
universalFlatFieldMetadata.universalIdentifier
];
if (isDefined(fieldId)) {
fieldMetadata[universalFlatFieldMetadata.universalIdentifier] =
fieldId;
}
}
continue;
}
if (action.metadataName === 'fieldMetadata') {
if (isDefined(action.id)) {
fieldMetadata[action.flatEntity.universalIdentifier] = action.id;
}
if (
isDefined(action.relatedUniversalFlatFieldMetadata) &&
isDefined(action.relatedFieldId)
) {
fieldMetadata[
action.relatedUniversalFlatFieldMetadata.universalIdentifier
] = action.relatedFieldId;
}
}
}
return { objectMetadata, fieldMetadata };
};
@@ -99,6 +99,7 @@ export const generateCompositeColumnDefinition = ({
const generateTsVectorColumnDefinition = (
flatFieldMetadata: FlatFieldMetadata<FieldMetadataType.TS_VECTOR>,
searchVectorAsExpression?: string,
): WorkspaceSchemaColumnDefinition => {
const columnName = computeColumnName(flatFieldMetadata.name);
@@ -108,8 +109,8 @@ const generateTsVectorColumnDefinition = (
isNullable: true,
isArray: false,
default: null,
asExpression: flatFieldMetadata.settings?.asExpression ?? undefined,
generatedType: flatFieldMetadata.settings?.generatedType ?? undefined,
asExpression: searchVectorAsExpression ?? undefined,
generatedType: 'STORED',
isPrimary: false,
};
};
@@ -177,10 +178,12 @@ export const generateColumnDefinitions = ({
flatFieldMetadata,
flatObjectMetadata,
workspaceId,
searchVectorAsExpression,
}: {
flatFieldMetadata: FlatFieldMetadata;
flatObjectMetadata: FlatObjectMetadata;
workspaceId: string;
searchVectorAsExpression?: string;
}): WorkspaceSchemaColumnDefinition[] => {
const { tableName, schemaName } = getWorkspaceSchemaContextForMigration({
workspaceId,
@@ -203,7 +206,12 @@ export const generateColumnDefinitions = ({
if (
isFlatFieldMetadataOfType(flatFieldMetadata, FieldMetadataType.TS_VECTOR)
) {
return [generateTsVectorColumnDefinition(flatFieldMetadata)];
return [
generateTsVectorColumnDefinition(
flatFieldMetadata,
searchVectorAsExpression,
),
];
}
if (isMorphOrRelationFlatFieldMetadata(flatFieldMetadata)) {
@@ -47,6 +47,18 @@ export const optimisticallyApplyCreateActionOnAllFlatEntityMaps = ({
return allFlatEntityMaps;
}
case 'searchFieldMetadata': {
addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow({
flatEntity: flatAction.flatEntity,
flatEntityAndRelatedMapsToMutate: allFlatEntityMaps,
metadataName: flatAction.metadataName,
// searchFieldMetadata is created before its parent object/fields, which
// therefore aren't in the maps yet to back-link to.
skipMissingRelatedEntities: true,
});
return allFlatEntityMaps;
}
case 'view':
case 'viewField':
case 'viewGroup':
@@ -74,8 +86,7 @@ export const optimisticallyApplyCreateActionOnAllFlatEntityMaps = ({
case 'fieldPermission':
case 'webhook':
case 'applicationVariable':
case 'connectionProvider':
case 'searchFieldMetadata': {
case 'connectionProvider': {
addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow({
flatEntity: flatAction.flatEntity,
flatEntityAndRelatedMapsToMutate: allFlatEntityMaps,