[Apps] Fix - app-synced object should be searchable (#19206)

## Summary

- **Make app-synced objects searchable**: `isSearchable` was hardcoded
to `false` and the `searchVector` field was missing the `GENERATED
ALWAYS AS (...)` expression, causing all records to have a `NULL` search
vector and be excluded from search results. Fixed by defaulting
`isSearchable` to `true` (configurable via the object manifest),
computing the `asExpression` from the label identifier field, and
allowing the update-field-action-handler to handle the `null` → defined
`asExpression` transition.
- **Make `isSearchable` updatable on an object**: The property had
`toCompare: false` in the entity properties configuration, so updates
via the API were silently ignored and never persisted. Fixed by setting
`toCompare: true`.
This commit is contained in:
Marie
2026-04-02 19:14:37 +02:00
committed by GitHub
parent 43baf7b91c
commit 2d6c8be7df
34 changed files with 639 additions and 51 deletions
@@ -1,5 +1,9 @@
import { type FieldMetadataType } from 'twenty-shared/types';
import { findOrThrow, isDefined } from 'twenty-shared/utils';
import {
findOrThrow,
isDefined,
type SearchableFieldType,
} from 'twenty-shared/utils';
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
@@ -12,7 +16,6 @@ import {
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
import { type SearchableFieldType } from 'src/engine/workspace-manager/utils/is-searchable-field.util';
type RecomputeSearchVectorFieldAfterLabelIdentifierUpdateArgs = {
existingFlatObjectMetadata: FlatObjectMetadata;
@@ -2,6 +2,7 @@ import { msg } from '@lingui/core/macro';
import {
isDefined,
isLabelIdentifierFieldMetadataTypes,
isSearchableFieldType,
} from 'twenty-shared/utils';
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
@@ -50,7 +51,14 @@ export const validateFlatObjectMetadataIdentifiers = ({
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
message:
'labelIdentifierFieldMetadataUniversalIdentifier validation failed: field type not compatible',
userFriendlyMessage: msg`Field cannot be used as label identifier`,
userFriendlyMessage: msg`Field cannot be used as label identifier due to its type: should be of type UUID, text or full name`,
});
} else if (!isSearchableFieldType(universalFlatFieldMetadata.type)) {
errors.push({
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
message:
'labelIdentifierFieldMetadataUniversalIdentifier validation failed: field type not compatible',
userFriendlyMessage: msg`Field cannot be used as label identifier due to its type`,
});
}
}