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:
@@ -666,6 +666,7 @@ type Object {
|
||||
duplicateCriteria: [[String!]!]
|
||||
fieldsList: [Field!]!
|
||||
indexMetadataList: [Index!]!
|
||||
searchFieldMetadataList: [SearchField!]!
|
||||
fields(
|
||||
"""Limit or page results."""
|
||||
paging: CursorPaging! = {first: 10}
|
||||
@@ -2085,6 +2086,15 @@ type ObjectRecordCount {
|
||||
totalCount: Int!
|
||||
}
|
||||
|
||||
type SearchField {
|
||||
id: UUID!
|
||||
fieldMetadataId: UUID!
|
||||
tsVectorFieldMetadataId: UUID!
|
||||
position: Float!
|
||||
createdAt: DateTime!
|
||||
updatedAt: DateTime!
|
||||
}
|
||||
|
||||
type ObjectConnection {
|
||||
"""Paging information"""
|
||||
pageInfo: PageInfo!
|
||||
|
||||
@@ -455,6 +455,7 @@ export interface Object {
|
||||
duplicateCriteria?: Scalars['String'][][]
|
||||
fieldsList: Field[]
|
||||
indexMetadataList: Index[]
|
||||
searchFieldMetadataList: SearchField[]
|
||||
fields: ObjectFieldsConnection
|
||||
indexMetadatas: ObjectIndexMetadatasConnection
|
||||
__typename: 'Object'
|
||||
@@ -1719,6 +1720,16 @@ export interface ObjectRecordCount {
|
||||
__typename: 'ObjectRecordCount'
|
||||
}
|
||||
|
||||
export interface SearchField {
|
||||
id: Scalars['UUID']
|
||||
fieldMetadataId: Scalars['UUID']
|
||||
tsVectorFieldMetadataId: Scalars['UUID']
|
||||
position: Scalars['Float']
|
||||
createdAt: Scalars['DateTime']
|
||||
updatedAt: Scalars['DateTime']
|
||||
__typename: 'SearchField'
|
||||
}
|
||||
|
||||
export interface ObjectConnection {
|
||||
/** Paging information */
|
||||
pageInfo: PageInfo
|
||||
@@ -3487,6 +3498,7 @@ export interface ObjectGenqlSelection{
|
||||
duplicateCriteria?: boolean | number
|
||||
fieldsList?: FieldGenqlSelection
|
||||
indexMetadataList?: IndexGenqlSelection
|
||||
searchFieldMetadataList?: SearchFieldGenqlSelection
|
||||
fields?: (ObjectFieldsConnectionGenqlSelection & { __args: {
|
||||
/** Limit or page results. */
|
||||
paging: CursorPaging,
|
||||
@@ -4802,6 +4814,17 @@ export interface ObjectRecordCountGenqlSelection{
|
||||
__scalar?: boolean | number
|
||||
}
|
||||
|
||||
export interface SearchFieldGenqlSelection{
|
||||
id?: boolean | number
|
||||
fieldMetadataId?: boolean | number
|
||||
tsVectorFieldMetadataId?: boolean | number
|
||||
position?: boolean | number
|
||||
createdAt?: boolean | number
|
||||
updatedAt?: boolean | number
|
||||
__typename?: boolean | number
|
||||
__scalar?: boolean | number
|
||||
}
|
||||
|
||||
export interface ObjectConnectionGenqlSelection{
|
||||
/** Paging information */
|
||||
pageInfo?: PageInfoGenqlSelection
|
||||
@@ -7771,6 +7794,14 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null
|
||||
|
||||
|
||||
|
||||
const SearchField_possibleTypes: string[] = ['SearchField']
|
||||
export const isSearchField = (obj?: { __typename?: any } | null): obj is SearchField => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isSearchField"')
|
||||
return SearchField_possibleTypes.includes(obj.__typename)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const ObjectConnection_possibleTypes: string[] = ['ObjectConnection']
|
||||
export const isObjectConnection = (obj?: { __typename?: any } | null): obj is ObjectConnection => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isObjectConnection"')
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -33,6 +33,15 @@ const METADATA_QUERY = `
|
||||
isLabelSyncedWithName
|
||||
isSearchable
|
||||
duplicateCriteria
|
||||
searchFieldMetadataList {
|
||||
__typename
|
||||
id
|
||||
fieldMetadataId
|
||||
tsVectorFieldMetadataId
|
||||
position
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
indexMetadataList {
|
||||
__typename
|
||||
id
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -25,6 +25,14 @@ export const OBJECT_METADATA_FRAGMENT = gql`
|
||||
isLabelSyncedWithName
|
||||
isSearchable
|
||||
duplicateCriteria
|
||||
searchFieldMetadataList {
|
||||
id
|
||||
fieldMetadataId
|
||||
tsVectorFieldMetadataId
|
||||
position
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
indexMetadataList {
|
||||
id
|
||||
createdAt
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { type Object as GeneratedObject } from '~/generated-metadata/graphql';
|
||||
|
||||
import { type IndexMetadataItem } from '@/object-metadata/types/IndexMetadataItem';
|
||||
import { type SearchFieldMetadataItem } from '@/object-metadata/types/SearchFieldMetadataItem';
|
||||
import { type FieldMetadataItem } from './FieldMetadataItem';
|
||||
|
||||
export type EnrichedObjectMetadataItem = Omit<
|
||||
@@ -12,6 +13,7 @@ export type EnrichedObjectMetadataItem = Omit<
|
||||
| 'labelIdentifierFieldMetadataId'
|
||||
| 'fieldsList'
|
||||
| 'indexMetadataList'
|
||||
| 'searchFieldMetadataList'
|
||||
// Deprecated GraphQL field kept server-side for one release; no longer queried
|
||||
| 'isUIReadOnly'
|
||||
> & {
|
||||
@@ -22,4 +24,5 @@ export type EnrichedObjectMetadataItem = Omit<
|
||||
updatableFields: FieldMetadataItem[];
|
||||
labelIdentifierFieldMetadataId: string;
|
||||
indexMetadatas: IndexMetadataItem[];
|
||||
searchFieldMetadatas: SearchFieldMetadataItem[];
|
||||
};
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { type SearchField as GeneratedSearchField } from '~/generated-metadata/graphql';
|
||||
|
||||
export type SearchFieldMetadataItem = Omit<
|
||||
GeneratedSearchField,
|
||||
'__typename'
|
||||
> & {
|
||||
__typename?: string;
|
||||
};
|
||||
+13
-2
@@ -1,5 +1,6 @@
|
||||
import { type IndexFieldMetadataItem } from '@/object-metadata/types/IndexFieldMetadataItem';
|
||||
import { type IndexMetadataItem } from '@/object-metadata/types/IndexMetadataItem';
|
||||
import { type SearchFieldMetadataItem } from '@/object-metadata/types/SearchFieldMetadataItem';
|
||||
import { objectMetadataItemSchema } from '@/object-metadata/validation-schemas/objectMetadataItemSchema';
|
||||
import { type ObjectMetadataItemsQuery } from '~/generated-metadata/graphql';
|
||||
import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem';
|
||||
@@ -21,8 +22,12 @@ export const mapPaginatedObjectMetadataItemsToObjectMetadataItems = ({
|
||||
object.node.labelIdentifierFieldMetadataId,
|
||||
);
|
||||
|
||||
const { fieldsList, indexMetadataList, ...objectWithoutFieldsList } =
|
||||
object.node;
|
||||
const {
|
||||
fieldsList,
|
||||
indexMetadataList,
|
||||
searchFieldMetadataList,
|
||||
...objectWithoutFieldsList
|
||||
} = object.node;
|
||||
|
||||
return {
|
||||
...objectWithoutFieldsList,
|
||||
@@ -30,6 +35,12 @@ export const mapPaginatedObjectMetadataItemsToObjectMetadataItems = ({
|
||||
...field,
|
||||
})),
|
||||
labelIdentifierFieldMetadataId,
|
||||
searchFieldMetadatas: searchFieldMetadataList.map(
|
||||
(searchFieldMetadata) =>
|
||||
({
|
||||
...searchFieldMetadata,
|
||||
}) satisfies SearchFieldMetadataItem,
|
||||
),
|
||||
indexMetadatas: indexMetadataList.map(
|
||||
({ indexFieldMetadataList: indexFields, ...indexRest }) =>
|
||||
({
|
||||
|
||||
+2
@@ -3,6 +3,7 @@ import { z } from 'zod';
|
||||
import { fieldMetadataItemSchema } from '@/object-metadata/validation-schemas/fieldMetadataItemSchema';
|
||||
import { indexMetadataItemSchema } from '@/object-metadata/validation-schemas/indexMetadataItemSchema';
|
||||
import { metadataLabelSchema } from '@/object-metadata/validation-schemas/metadataLabelSchema';
|
||||
import { searchFieldMetadataItemSchema } from '@/object-metadata/validation-schemas/searchFieldMetadataItemSchema';
|
||||
import { camelCaseStringSchema } from '~/utils/validation-schemas/camelCaseStringSchema';
|
||||
|
||||
export const objectMetadataItemSchema = z.object({
|
||||
@@ -13,6 +14,7 @@ export const objectMetadataItemSchema = z.object({
|
||||
readableFields: z.array(fieldMetadataItemSchema()),
|
||||
updatableFields: z.array(fieldMetadataItemSchema()),
|
||||
indexMetadatas: z.array(indexMetadataItemSchema),
|
||||
searchFieldMetadatas: z.array(searchFieldMetadataItemSchema),
|
||||
icon: z.string().startsWith('Icon').trim(),
|
||||
applicationId: z.uuid(),
|
||||
id: z.uuid(),
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { type SearchFieldMetadataItem } from '@/object-metadata/types/SearchFieldMetadataItem';
|
||||
|
||||
export const searchFieldMetadataItemSchema = z.object({
|
||||
__typename: z.literal('SearchField').optional(),
|
||||
id: z.uuid(),
|
||||
fieldMetadataId: z.uuid(),
|
||||
tsVectorFieldMetadataId: z.uuid(),
|
||||
position: z.number(),
|
||||
createdAt: z.string(),
|
||||
updatedAt: z.string(),
|
||||
}) satisfies z.ZodType<SearchFieldMetadataItem>;
|
||||
+1
@@ -50,6 +50,7 @@ const mockObjectMetadataItem: EnrichedObjectMetadataItem = {
|
||||
readableFields: fields,
|
||||
updatableFields: fields,
|
||||
indexMetadatas: [],
|
||||
searchFieldMetadatas: [],
|
||||
isLabelSyncedWithName: true,
|
||||
isRemote: false,
|
||||
isSystem: false,
|
||||
|
||||
+3
@@ -29,6 +29,7 @@ const objectMetadataItemWithPositionField: EnrichedObjectMetadataItem = {
|
||||
readableFields: fields,
|
||||
updatableFields: fields,
|
||||
indexMetadatas: [],
|
||||
searchFieldMetadatas: [],
|
||||
createdAt: '2021-01-01',
|
||||
updatedAt: '2021-01-01',
|
||||
nameSingular: 'object1',
|
||||
@@ -191,6 +192,7 @@ describe('turnSortsIntoOrderBy', () => {
|
||||
readableFields: [],
|
||||
updatableFields: [],
|
||||
indexMetadatas: [],
|
||||
searchFieldMetadatas: [],
|
||||
createdAt: '2021-01-01',
|
||||
updatedAt: '2021-01-01',
|
||||
nameSingular: 'company',
|
||||
@@ -241,6 +243,7 @@ describe('turnSortsIntoOrderBy', () => {
|
||||
readableFields: [],
|
||||
updatableFields: [],
|
||||
indexMetadatas: [],
|
||||
searchFieldMetadatas: [],
|
||||
createdAt: '2021-01-01',
|
||||
updatedAt: '2021-01-01',
|
||||
nameSingular: 'person',
|
||||
|
||||
+1
@@ -49,6 +49,7 @@ describe('buildRecordGqlFieldsAggregateForView', () => {
|
||||
readableFields: fields,
|
||||
updatableFields: fields,
|
||||
indexMetadatas: [],
|
||||
searchFieldMetadatas: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
+2
@@ -19,6 +19,7 @@ describe('generateAggregateQuery', () => {
|
||||
readableFields: [],
|
||||
updatableFields: [],
|
||||
indexMetadatas: [],
|
||||
searchFieldMetadatas: [],
|
||||
isLabelSyncedWithName: true,
|
||||
isRemote: false,
|
||||
isSystem: false,
|
||||
@@ -62,6 +63,7 @@ describe('generateAggregateQuery', () => {
|
||||
readableFields: [],
|
||||
updatableFields: [],
|
||||
indexMetadatas: [],
|
||||
searchFieldMetadatas: [],
|
||||
isLabelSyncedWithName: true,
|
||||
isRemote: false,
|
||||
isSystem: false,
|
||||
|
||||
+17
-38
@@ -12,6 +12,7 @@ import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useContext, useMemo, useState } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { IconEye, IconSearch, useIcons } from 'twenty-ui/icon';
|
||||
import { Card } from 'twenty-ui/surfaces';
|
||||
@@ -44,56 +45,34 @@ const StyledNameLabel = styled.div`
|
||||
|
||||
const INDEXED_FIELDS_GRID_TEMPLATE_COLUMNS = 'minmax(0, 1fr) 100px 148px';
|
||||
|
||||
// TODO: This is very DIRTY ; let's migrate searchVector to be proper tables
|
||||
// Already tracked here: https://github.com/twentyhq/core-team-issues/issues/1428
|
||||
const extractIndexedFields = (
|
||||
objectMetadataItem: EnrichedObjectMetadataItem,
|
||||
): IndexedFieldEntry[] => {
|
||||
const searchVectorField = objectMetadataItem.fields.find(
|
||||
(field) => field.name === SEARCH_VECTOR_FIELD_NAME,
|
||||
const fieldById = new Map(
|
||||
objectMetadataItem.fields.map((field) => [field.id, field]),
|
||||
);
|
||||
|
||||
const asExpression = (
|
||||
searchVectorField?.settings as { asExpression?: string } | null
|
||||
)?.asExpression;
|
||||
return [...objectMetadataItem.searchFieldMetadatas]
|
||||
.sort(
|
||||
(searchFieldMetadataA, searchFieldMetadataB) =>
|
||||
searchFieldMetadataA.position - searchFieldMetadataB.position,
|
||||
)
|
||||
.map((searchFieldMetadata) => {
|
||||
const field = fieldById.get(searchFieldMetadata.fieldMetadataId);
|
||||
|
||||
if (!asExpression) {
|
||||
return [];
|
||||
}
|
||||
if (!isDefined(field) || field.name === SEARCH_VECTOR_FIELD_NAME) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const columnNames = [
|
||||
...new Set(
|
||||
Array.from(asExpression.matchAll(/"([^"]+)"/g), (match) => match[1]),
|
||||
),
|
||||
];
|
||||
|
||||
const seenFieldIds = new Set<string>();
|
||||
const entries: IndexedFieldEntry[] = [];
|
||||
|
||||
for (const columnName of columnNames) {
|
||||
const field = objectMetadataItem.fields.find(
|
||||
(fieldItem) =>
|
||||
fieldItem.name === columnName || columnName.startsWith(fieldItem.name),
|
||||
);
|
||||
|
||||
if (
|
||||
field &&
|
||||
field.name !== SEARCH_VECTOR_FIELD_NAME &&
|
||||
!seenFieldIds.has(field.id)
|
||||
) {
|
||||
seenFieldIds.add(field.id);
|
||||
|
||||
entries.push({
|
||||
return {
|
||||
id: field.id,
|
||||
label: field.label,
|
||||
icon: field.icon,
|
||||
weight: 1,
|
||||
fieldType: field.type,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
} satisfies IndexedFieldEntry;
|
||||
})
|
||||
.filter(isDefined);
|
||||
};
|
||||
|
||||
export const SettingsObjectSearchSection = ({
|
||||
|
||||
+1
@@ -80,6 +80,7 @@ const mockObjectMetadataItem: EnrichedObjectMetadataItem = {
|
||||
isSearchable: true,
|
||||
labelIdentifierFieldMetadataId: '1',
|
||||
indexMetadatas: [],
|
||||
searchFieldMetadatas: [],
|
||||
fields: fields,
|
||||
readableFields: fields,
|
||||
updatableFields: fields,
|
||||
|
||||
+1
@@ -254,6 +254,7 @@ const buildObjectMetadataItemsFromMarketplaceApp = (
|
||||
(field) => !nonUpdatableFieldIds.has(field.id),
|
||||
),
|
||||
indexMetadatas: [],
|
||||
searchFieldMetadatas: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
+29
@@ -43,6 +43,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"labelPlural": "Calendar events",
|
||||
"description": "Calendar events",
|
||||
"icon": "IconCalendar",
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [],
|
||||
"fieldsList": [
|
||||
{
|
||||
@@ -1043,6 +1044,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -1488,6 +1490,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -2264,6 +2267,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -2378,6 +2382,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"labelPlural": "Message Threads",
|
||||
"description": "Message Thread",
|
||||
"icon": "IconMessage",
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [],
|
||||
"fieldsList": [
|
||||
{
|
||||
@@ -3572,6 +3577,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
]
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -4388,6 +4394,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -5184,6 +5191,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -5767,6 +5775,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
]
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -6366,6 +6375,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -7179,6 +7189,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
]
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -7694,6 +7705,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -8255,6 +8267,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -8842,6 +8855,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -9493,6 +9507,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -10144,6 +10159,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -11184,6 +11200,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -12432,6 +12449,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
]
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -13080,6 +13098,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
]
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -13492,6 +13511,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -14144,6 +14164,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -14722,6 +14743,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -15937,6 +15959,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -16541,6 +16564,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
]
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -17677,6 +17701,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -18195,6 +18220,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -18723,6 +18749,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -19072,6 +19099,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
@@ -19650,6 +19678,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"morphRelations": null
|
||||
}
|
||||
],
|
||||
"searchFieldMetadataList": [],
|
||||
"indexMetadataList": [
|
||||
{
|
||||
"__typename": "Index",
|
||||
|
||||
+2
@@ -178,6 +178,8 @@ const buildLegacyCalendarEventRecordingPreferenceFieldMetadata = ({
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
viewSortIds: [],
|
||||
viewSortUniversalIdentifiers: [],
|
||||
searchFieldMetadataIds: [],
|
||||
searchFieldMetadataUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
});
|
||||
|
||||
|
||||
+88
-4
@@ -5,6 +5,7 @@ import { buildSearchFieldMetadataBackfillOperations } from 'src/database/command
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { getFlatFieldMetadataMock } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/get-flat-field-metadata.mock';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants';
|
||||
import {
|
||||
getFlatObjectMetadataMock,
|
||||
getStandardFlatObjectMetadataMock,
|
||||
@@ -100,6 +101,8 @@ const buildSearchFieldMetadata = ({
|
||||
fieldMetadataUniversalIdentifier,
|
||||
applicationUniversalIdentifier,
|
||||
position = 0,
|
||||
tsVectorFieldMetadataId = 'ts-vector-field-metadata-id',
|
||||
tsVectorFieldMetadataUniversalIdentifier = 'ts-vector-field-metadata-universal-identifier',
|
||||
}: {
|
||||
id: string;
|
||||
universalIdentifier: string;
|
||||
@@ -109,6 +112,8 @@ const buildSearchFieldMetadata = ({
|
||||
fieldMetadataUniversalIdentifier: string;
|
||||
applicationUniversalIdentifier: string;
|
||||
position?: number;
|
||||
tsVectorFieldMetadataId?: string;
|
||||
tsVectorFieldMetadataUniversalIdentifier?: string;
|
||||
}): FlatSearchFieldMetadata => {
|
||||
const createdAt = '2024-01-01T00:00:00.000Z';
|
||||
|
||||
@@ -119,6 +124,8 @@ const buildSearchFieldMetadata = ({
|
||||
fieldMetadataId,
|
||||
objectMetadataUniversalIdentifier,
|
||||
fieldMetadataUniversalIdentifier,
|
||||
tsVectorFieldMetadataId,
|
||||
tsVectorFieldMetadataUniversalIdentifier,
|
||||
applicationId: 'unused-application-id',
|
||||
applicationUniversalIdentifier,
|
||||
position,
|
||||
@@ -128,6 +135,25 @@ const buildSearchFieldMetadata = ({
|
||||
};
|
||||
};
|
||||
|
||||
const buildSearchVectorField = ({
|
||||
objectMetadataId,
|
||||
objectMetadataUniversalIdentifier,
|
||||
applicationUniversalIdentifier,
|
||||
}: {
|
||||
objectMetadataId: string;
|
||||
objectMetadataUniversalIdentifier: string;
|
||||
applicationUniversalIdentifier: string;
|
||||
}): FlatFieldMetadata =>
|
||||
getFlatFieldMetadataMock({
|
||||
id: `${objectMetadataId}-search-vector-field-id`,
|
||||
universalIdentifier: `${objectMetadataUniversalIdentifier}-search-vector-field-uid`,
|
||||
objectMetadataId,
|
||||
objectMetadataUniversalIdentifier,
|
||||
type: FieldMetadataType.TS_VECTOR,
|
||||
name: SEARCH_VECTOR_FIELD.name,
|
||||
applicationUniversalIdentifier,
|
||||
});
|
||||
|
||||
// Custom object with a searchable `name` field plus a TEXT field whose name
|
||||
// overlaps it by prefix (name / nameDescription). Only the `name` field should
|
||||
// produce a row; the exact-name match makes the prefix overlap irrelevant. This
|
||||
@@ -158,6 +184,12 @@ const buildCustomObjectFixture = () => {
|
||||
applicationUniversalIdentifier: CUSTOM_APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
|
||||
const searchVectorField = buildSearchVectorField({
|
||||
objectMetadataId: customObjectId,
|
||||
objectMetadataUniversalIdentifier: customObjectUniversalIdentifier,
|
||||
applicationUniversalIdentifier: CUSTOM_APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
|
||||
const customObject = getFlatObjectMetadataMock({
|
||||
id: customObjectId,
|
||||
universalIdentifier: customObjectUniversalIdentifier,
|
||||
@@ -168,15 +200,16 @@ const buildCustomObjectFixture = () => {
|
||||
fieldUniversalIdentifiers: [
|
||||
nameField.universalIdentifier,
|
||||
nameDescriptionField.universalIdentifier,
|
||||
searchVectorField.universalIdentifier,
|
||||
],
|
||||
});
|
||||
|
||||
return { customObject, nameField, nameDescriptionField };
|
||||
return { customObject, nameField, nameDescriptionField, searchVectorField };
|
||||
};
|
||||
|
||||
describe('buildSearchFieldMetadataBackfillOperations', () => {
|
||||
it('selects only the name field for a custom object, ignoring a prefix-overlapping field name', () => {
|
||||
const { customObject, nameField, nameDescriptionField } =
|
||||
const { customObject, nameField, nameDescriptionField, searchVectorField } =
|
||||
buildCustomObjectFixture();
|
||||
|
||||
const { flatSearchFieldMetadatasToCreateByApplicationUniversalIdentifier } =
|
||||
@@ -185,6 +218,7 @@ describe('buildSearchFieldMetadataBackfillOperations', () => {
|
||||
flatFieldMetadataMaps: buildFlatFieldMetadataMaps([
|
||||
nameField,
|
||||
nameDescriptionField,
|
||||
searchVectorField,
|
||||
]),
|
||||
flatSearchFieldMetadataMaps: buildFlatSearchFieldMetadataMaps([]),
|
||||
standardFlatSearchFieldMetadataMaps:
|
||||
@@ -204,6 +238,10 @@ describe('buildSearchFieldMetadataBackfillOperations', () => {
|
||||
expect(customApplicationRows?.[0].objectMetadataUniversalIdentifier).toBe(
|
||||
customObject.universalIdentifier,
|
||||
);
|
||||
// The row points at the object's searchVector field via the new FK.
|
||||
expect(
|
||||
customApplicationRows?.[0].tsVectorFieldMetadataUniversalIdentifier,
|
||||
).toBe(searchVectorField.universalIdentifier);
|
||||
// Custom object name field is seeded at position 0.
|
||||
expect(customApplicationRows?.[0].position).toBe(0);
|
||||
// No spurious row for the prefix-overlapping `nameDescription` field.
|
||||
@@ -263,7 +301,7 @@ describe('buildSearchFieldMetadataBackfillOperations', () => {
|
||||
});
|
||||
|
||||
it('groups rows per application: standard-object rows under the standard application, custom-object rows under the custom application', () => {
|
||||
const { customObject, nameField, nameDescriptionField } =
|
||||
const { customObject, nameField, nameDescriptionField, searchVectorField } =
|
||||
buildCustomObjectFixture();
|
||||
|
||||
const standardObjectId = 'standard-object-id';
|
||||
@@ -282,12 +320,23 @@ describe('buildSearchFieldMetadataBackfillOperations', () => {
|
||||
TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
|
||||
const standardSearchVectorField = buildSearchVectorField({
|
||||
objectMetadataId: standardObjectId,
|
||||
objectMetadataUniversalIdentifier: standardObjectUniversalIdentifier,
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
|
||||
const standardObject = getStandardFlatObjectMetadataMock({
|
||||
id: standardObjectId,
|
||||
universalIdentifier: standardObjectUniversalIdentifier,
|
||||
isSearchable: true,
|
||||
labelIdentifierFieldMetadataId: standardFieldId,
|
||||
fieldIds: [standardFieldId],
|
||||
fieldUniversalIdentifiers: [
|
||||
standardFieldUniversalIdentifier,
|
||||
standardSearchVectorField.universalIdentifier,
|
||||
],
|
||||
});
|
||||
|
||||
const standardSearchFieldMetadata = buildSearchFieldMetadata({
|
||||
@@ -310,7 +359,9 @@ describe('buildSearchFieldMetadataBackfillOperations', () => {
|
||||
flatFieldMetadataMaps: buildFlatFieldMetadataMaps([
|
||||
nameField,
|
||||
nameDescriptionField,
|
||||
searchVectorField,
|
||||
standardField,
|
||||
standardSearchVectorField,
|
||||
]),
|
||||
flatSearchFieldMetadataMaps: buildFlatSearchFieldMetadataMaps([]),
|
||||
standardFlatSearchFieldMetadataMaps: buildFlatSearchFieldMetadataMaps([
|
||||
@@ -339,6 +390,9 @@ describe('buildSearchFieldMetadataBackfillOperations', () => {
|
||||
expect(standardApplicationRows?.[0].fieldMetadataUniversalIdentifier).toBe(
|
||||
standardFieldUniversalIdentifier,
|
||||
);
|
||||
expect(
|
||||
standardApplicationRows?.[0].tsVectorFieldMetadataUniversalIdentifier,
|
||||
).toBe(standardSearchVectorField.universalIdentifier);
|
||||
|
||||
const customApplicationRows =
|
||||
flatSearchFieldMetadatasToCreateByApplicationUniversalIdentifier[
|
||||
@@ -349,6 +403,9 @@ describe('buildSearchFieldMetadataBackfillOperations', () => {
|
||||
expect(customApplicationRows?.[0].fieldMetadataUniversalIdentifier).toBe(
|
||||
nameField.universalIdentifier,
|
||||
);
|
||||
expect(
|
||||
customApplicationRows?.[0].tsVectorFieldMetadataUniversalIdentifier,
|
||||
).toBe(searchVectorField.universalIdentifier);
|
||||
});
|
||||
|
||||
it('selects only the standard-set field on a standard object, never a prefix-overlapping sibling (phone vs phoneNumber)', () => {
|
||||
@@ -386,12 +443,24 @@ describe('buildSearchFieldMetadataBackfillOperations', () => {
|
||||
TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
|
||||
const standardSearchVectorField = buildSearchVectorField({
|
||||
objectMetadataId: standardObjectId,
|
||||
objectMetadataUniversalIdentifier: standardObjectUniversalIdentifier,
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
|
||||
const standardObject = getStandardFlatObjectMetadataMock({
|
||||
id: standardObjectId,
|
||||
universalIdentifier: standardObjectUniversalIdentifier,
|
||||
isSearchable: true,
|
||||
labelIdentifierFieldMetadataId: phoneFieldId,
|
||||
fieldIds: [phoneFieldId, phoneNumberFieldId],
|
||||
fieldUniversalIdentifiers: [
|
||||
phoneFieldUniversalIdentifier,
|
||||
phoneNumberFieldUniversalIdentifier,
|
||||
standardSearchVectorField.universalIdentifier,
|
||||
],
|
||||
});
|
||||
|
||||
const phoneSearchFieldMetadata = buildSearchFieldMetadata({
|
||||
@@ -411,6 +480,7 @@ describe('buildSearchFieldMetadataBackfillOperations', () => {
|
||||
flatFieldMetadataMaps: buildFlatFieldMetadataMaps([
|
||||
phoneField,
|
||||
phoneNumberField,
|
||||
standardSearchVectorField,
|
||||
]),
|
||||
flatSearchFieldMetadataMaps: buildFlatSearchFieldMetadataMaps([]),
|
||||
standardFlatSearchFieldMetadataMaps: buildFlatSearchFieldMetadataMaps([
|
||||
@@ -455,12 +525,23 @@ describe('buildSearchFieldMetadataBackfillOperations', () => {
|
||||
TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
|
||||
const standardSearchVectorField = buildSearchVectorField({
|
||||
objectMetadataId: standardObjectId,
|
||||
objectMetadataUniversalIdentifier: standardObjectUniversalIdentifier,
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
|
||||
const standardObject = getStandardFlatObjectMetadataMock({
|
||||
id: standardObjectId,
|
||||
universalIdentifier: standardObjectUniversalIdentifier,
|
||||
isSearchable: true,
|
||||
labelIdentifierFieldMetadataId: emailsFieldId,
|
||||
fieldIds: [emailsFieldId],
|
||||
fieldUniversalIdentifiers: [
|
||||
emailsFieldUniversalIdentifier,
|
||||
standardSearchVectorField.universalIdentifier,
|
||||
],
|
||||
});
|
||||
|
||||
// The standard maps row sits at position 3 (e.g. SEARCH_FIELDS_FOR_PERSON order);
|
||||
@@ -480,7 +561,10 @@ describe('buildSearchFieldMetadataBackfillOperations', () => {
|
||||
const { flatSearchFieldMetadatasToCreateByApplicationUniversalIdentifier } =
|
||||
buildSearchFieldMetadataBackfillOperations({
|
||||
flatObjectMetadataMaps: buildFlatObjectMetadataMaps([standardObject]),
|
||||
flatFieldMetadataMaps: buildFlatFieldMetadataMaps([emailsField]),
|
||||
flatFieldMetadataMaps: buildFlatFieldMetadataMaps([
|
||||
emailsField,
|
||||
standardSearchVectorField,
|
||||
]),
|
||||
flatSearchFieldMetadataMaps: buildFlatSearchFieldMetadataMaps([]),
|
||||
standardFlatSearchFieldMetadataMaps: buildFlatSearchFieldMetadataMaps([
|
||||
standardSearchFieldMetadata,
|
||||
|
||||
+11
@@ -10,6 +10,7 @@ import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-m
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type';
|
||||
import { buildFlatSearchFieldMetadataForField } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/build-flat-search-field-metadata-for-field.util';
|
||||
import { findTsVectorFlatFieldMetadataForObject } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/find-ts-vector-flat-field-metadata-for-object.util';
|
||||
import { DEFAULT_LABEL_IDENTIFIER_FIELD_NAME } from 'src/engine/metadata-modules/object-metadata/constants/object-metadata.constants';
|
||||
import { type UniversalFlatSearchFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-search-field-metadata.type';
|
||||
|
||||
@@ -84,12 +85,22 @@ export const buildSearchFieldMetadataBackfillOperations = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const tsVectorFlatFieldMetadata = findTsVectorFlatFieldMetadataForObject({
|
||||
fieldUniversalIdentifiers: flatObjectMetadata.fieldUniversalIdentifiers,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(tsVectorFlatFieldMetadata)) {
|
||||
return;
|
||||
}
|
||||
|
||||
candidateSearchFieldMetadataKeys.add(searchFieldMetadataKey);
|
||||
|
||||
flatSearchFieldMetadatasToCreate.push(
|
||||
buildFlatSearchFieldMetadataForField({
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadata,
|
||||
tsVectorFlatFieldMetadata,
|
||||
position,
|
||||
}),
|
||||
);
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import { QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
|
||||
|
||||
@RegisteredInstanceCommand('2.18.0', 1810000001000)
|
||||
export class AddTsVectorFieldMetadataIdToSearchFieldMetadataFastInstanceCommand implements FastInstanceCommand {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."searchFieldMetadata" ADD "tsVectorFieldMetadataId" uuid',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."searchFieldMetadata" ADD CONSTRAINT "FK_31102b31f7338b6a298fc22996a" FOREIGN KEY ("tsVectorFieldMetadataId") REFERENCES "core"."fieldMetadata"("id") ON DELETE CASCADE ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED',
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."searchFieldMetadata" DROP CONSTRAINT IF EXISTS "FK_1b78544eb06f82059a2a01013a3"',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."searchFieldMetadata" ADD CONSTRAINT "FK_1b78544eb06f82059a2a01013a3" FOREIGN KEY ("objectMetadataId") REFERENCES "core"."objectMetadata"("id") ON DELETE CASCADE ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED',
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."searchFieldMetadata" DROP CONSTRAINT IF EXISTS "FK_6d5c6922bfd1578b1eff2abb9d6"',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."searchFieldMetadata" ADD CONSTRAINT "FK_6d5c6922bfd1578b1eff2abb9d6" FOREIGN KEY ("fieldMetadataId") REFERENCES "core"."fieldMetadata"("id") ON DELETE CASCADE ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED',
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."searchFieldMetadata" DROP CONSTRAINT IF EXISTS "FK_6d5c6922bfd1578b1eff2abb9d6"',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."searchFieldMetadata" ADD CONSTRAINT "FK_6d5c6922bfd1578b1eff2abb9d6" FOREIGN KEY ("fieldMetadataId") REFERENCES "core"."fieldMetadata"("id") ON DELETE CASCADE ON UPDATE NO ACTION',
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."searchFieldMetadata" DROP CONSTRAINT IF EXISTS "FK_1b78544eb06f82059a2a01013a3"',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."searchFieldMetadata" ADD CONSTRAINT "FK_1b78544eb06f82059a2a01013a3" FOREIGN KEY ("objectMetadataId") REFERENCES "core"."objectMetadata"("id") ON DELETE CASCADE ON UPDATE NO ACTION',
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."searchFieldMetadata" DROP CONSTRAINT "FK_31102b31f7338b6a298fc22996a"',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."searchFieldMetadata" DROP COLUMN "tsVectorFieldMetadataId"',
|
||||
);
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
import { DataSource, QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { SlowInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/slow-instance-command.interface';
|
||||
|
||||
// Backfills the tsVectorFieldMetadataId FK by pointing every existing
|
||||
// searchFieldMetadata row at its object's system searchVector (TS_VECTOR) field,
|
||||
// then enforces NOT NULL. Runs after the fast command that adds the column (and makes
|
||||
// the FKs deferrable). Any row left without a resolvable searchVector
|
||||
// aborts the upgrade rather than silently relaxing the NOT NULL guarantee.
|
||||
@RegisteredInstanceCommand('2.18.0', 1810000003000, { type: 'slow' })
|
||||
export class BackfillTsVectorFieldMetadataIdOnSearchFieldMetadataSlowInstanceCommand
|
||||
implements SlowInstanceCommand
|
||||
{
|
||||
async runDataMigration(dataSource: DataSource): Promise<void> {
|
||||
await dataSource.query(
|
||||
`UPDATE "core"."searchFieldMetadata" "searchFieldMetadata"
|
||||
SET "tsVectorFieldMetadataId" = "fieldMetadata"."id"
|
||||
FROM "core"."fieldMetadata" "fieldMetadata"
|
||||
WHERE "fieldMetadata"."objectMetadataId" = "searchFieldMetadata"."objectMetadataId"
|
||||
AND "fieldMetadata"."type" = 'TS_VECTOR'
|
||||
AND "fieldMetadata"."name" = 'searchVector'
|
||||
AND "searchFieldMetadata"."tsVectorFieldMetadataId" IS NULL`,
|
||||
);
|
||||
|
||||
const unresolvedRows: { count: string }[] = await dataSource.query(
|
||||
`SELECT COUNT(*) AS "count" FROM "core"."searchFieldMetadata"
|
||||
WHERE "tsVectorFieldMetadataId" IS NULL`,
|
||||
);
|
||||
|
||||
const unresolvedCount = Number(unresolvedRows[0]?.count ?? 0);
|
||||
|
||||
if (unresolvedCount > 0) {
|
||||
throw new Error(
|
||||
`Cannot enforce searchFieldMetadata.tsVectorFieldMetadataId NOT NULL: ${unresolvedCount} row(s) reference an object without a searchVector (TS_VECTOR) field`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."searchFieldMetadata" ALTER COLUMN "tsVectorFieldMetadataId" SET NOT NULL',
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."searchFieldMetadata" ALTER COLUMN "tsVectorFieldMetadataId" DROP NOT NULL',
|
||||
);
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export const ADD_TS_VECTOR_FIELD_METADATA_ID_TO_SEARCH_FIELD_METADATA_UPGRADE_COMMAND_NAME =
|
||||
'2.18.0_AddTsVectorFieldMetadataIdToSearchFieldMetadataFastInstanceCommand_1810000001000';
|
||||
+9
-5
@@ -34,6 +34,13 @@ import { AddIsSystemSideEffectFastInstanceCommand } from 'src/database/commands/
|
||||
import { BackfillConnectionSecuritySlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-15/2-15-instance-command-slow-1781461753981-backfill-connection-security';
|
||||
import { AddChannelWebhookSubscriptionFieldsFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-16/2-16-instance-command-fast-1782152096938-add-channel-webhook-subscription-fields';
|
||||
import { AddUniversalIdentifierAndApplicationIdToSearchFieldMetadataFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-16/2-16-instance-command-fast-1782200000000-add-universal-identifier-and-application-id-to-search-field-metadata';
|
||||
import { AddServerTriggerSettingsToLogicFunctionFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-16/2-16-instance-command-fast-1782211913427-add-server-trigger-settings-to-logic-function';
|
||||
import { AddPrimaryPublicDomainToApplicationFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-16/2-16-instance-command-fast-1782281874768-add-primary-public-domain-to-application';
|
||||
import { MakePublicDomainApplicationIdNotNullSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-16/2-16-instance-command-slow-1782281874769-make-public-domain-application-id-not-null';
|
||||
import { CreateDpaAgreementCoreTableFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-17/2-17-instance-command-fast-1801000020000-create-dpa-agreement-core-table';
|
||||
import { CreateApplicationTranslationCoreTableFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-17/2-17-instance-command-fast-1801000100000-create-application-translation-core-table';
|
||||
import { AddTsVectorFieldMetadataIdToSearchFieldMetadataFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-18/2-18-instance-command-fast-1810000001000-add-ts-vector-field-metadata-id-to-search-field-metadata';
|
||||
import { BackfillTsVectorFieldMetadataIdOnSearchFieldMetadataSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-18/2-18-instance-command-slow-1810000003000-backfill-ts-vector-field-metadata-id-on-search-field-metadata';
|
||||
import { AddCacheTokensToAgentChatThreadFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-2/2-2-instance-command-fast-1777455269302-add-cache-tokens-to-agent-chat-thread';
|
||||
import { AddLogoToApplicationFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-2/2-2-instance-command-fast-1777539664664-add-logo-to-application';
|
||||
import { AddSubFieldNameToViewSortEarlyFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-instance-command-fast-1747234200000-add-sub-field-name-to-view-sort';
|
||||
@@ -77,11 +84,6 @@ import { EncryptNonSecretApplicationVariableSlowInstanceCommand } from 'src/data
|
||||
import { MigrateAiModelPreferencesSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-9/2-9-instance-command-slow-1799000010000-migrate-ai-model-preferences';
|
||||
import { AddFolderImportToMessageFolderPendingSyncActionFastInstanceCommand } from './2-15/2-15-instance-command-fast-1781714499016-add-folder-import-to-message-folder-pending-sync-action';
|
||||
import { AddViewKanbanColumnWidthFastInstanceCommand } from './2-15/2-15-instance-command-fast-1781900000000-add-view-kanban-column-width';
|
||||
import { AddPrimaryPublicDomainToApplicationFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-16/2-16-instance-command-fast-1782281874768-add-primary-public-domain-to-application';
|
||||
import { MakePublicDomainApplicationIdNotNullSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-16/2-16-instance-command-slow-1782281874769-make-public-domain-application-id-not-null';
|
||||
import { AddServerTriggerSettingsToLogicFunctionFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-16/2-16-instance-command-fast-1782211913427-add-server-trigger-settings-to-logic-function';
|
||||
import { CreateDpaAgreementCoreTableFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-17/2-17-instance-command-fast-1801000020000-create-dpa-agreement-core-table';
|
||||
import { CreateApplicationTranslationCoreTableFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-17/2-17-instance-command-fast-1801000100000-create-application-translation-core-table';
|
||||
|
||||
export const INSTANCE_COMMANDS = [
|
||||
AddViewFieldGroupIdIndexOnViewFieldFastInstanceCommand,
|
||||
@@ -166,4 +168,6 @@ export const INSTANCE_COMMANDS = [
|
||||
MakePublicDomainApplicationIdNotNullSlowInstanceCommand,
|
||||
CreateDpaAgreementCoreTableFastInstanceCommand,
|
||||
CreateApplicationTranslationCoreTableFastInstanceCommand,
|
||||
AddTsVectorFieldMetadataIdToSearchFieldMetadataFastInstanceCommand,
|
||||
BackfillTsVectorFieldMetadataIdOnSearchFieldMetadataSlowInstanceCommand,
|
||||
];
|
||||
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { getFlatFieldMetadataMock } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/get-flat-field-metadata.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type SearchFieldMetadataEntity } from 'src/engine/metadata-modules/search-field-metadata/search-field-metadata.entity';
|
||||
import { generateWorkspaceSchemaDdl } from 'src/database/commands/workspace-export/utils/generate-workspace-schema-ddl.util';
|
||||
|
||||
describe('generateWorkspaceSchemaDdl', () => {
|
||||
const workspaceId = '20202020-1c25-4d02-bf25-6aeccf7ea419';
|
||||
const objectMetadataId = '20202020-object-id';
|
||||
const nameFieldId = '20202020-name-field-id';
|
||||
const searchVectorFieldId = '20202020-search-vector-field-id';
|
||||
|
||||
const objectMetadata = {
|
||||
...getFlatObjectMetadataMock({
|
||||
universalIdentifier: 'person',
|
||||
id: objectMetadataId,
|
||||
nameSingular: 'person',
|
||||
namePlural: 'persons',
|
||||
}),
|
||||
isActive: true,
|
||||
} as unknown as ObjectMetadataEntity;
|
||||
|
||||
const nameField = getFlatFieldMetadataMock({
|
||||
universalIdentifier: 'name',
|
||||
id: nameFieldId,
|
||||
objectMetadataId,
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'name',
|
||||
}) as unknown as FieldMetadataEntity;
|
||||
|
||||
const searchVectorField = getFlatFieldMetadataMock({
|
||||
universalIdentifier: 'searchVector',
|
||||
id: searchVectorFieldId,
|
||||
objectMetadataId,
|
||||
type: FieldMetadataType.TS_VECTOR,
|
||||
name: 'searchVector',
|
||||
}) as unknown as FieldMetadataEntity;
|
||||
|
||||
const fieldsByObjectId = new Map<string, FieldMetadataEntity[]>([
|
||||
[objectMetadataId, [nameField, searchVectorField]],
|
||||
]);
|
||||
|
||||
const buildSearchFieldMetadata = (
|
||||
overrides: Partial<SearchFieldMetadataEntity> = {},
|
||||
): SearchFieldMetadataEntity =>
|
||||
({
|
||||
universalIdentifier: 'search-name',
|
||||
objectMetadataId,
|
||||
fieldMetadataId: nameFieldId,
|
||||
tsVectorFieldMetadataId: searchVectorFieldId,
|
||||
position: 0,
|
||||
...overrides,
|
||||
}) as SearchFieldMetadataEntity;
|
||||
|
||||
const generateSearchVectorColumnSql = (
|
||||
searchFieldMetadatasByObjectId: Map<string, SearchFieldMetadataEntity[]>,
|
||||
): string => {
|
||||
const statements = generateWorkspaceSchemaDdl(
|
||||
workspaceId,
|
||||
'workspace_schema',
|
||||
[objectMetadata],
|
||||
fieldsByObjectId,
|
||||
searchFieldMetadatasByObjectId,
|
||||
);
|
||||
|
||||
const createTableStatement = statements.find((statement) =>
|
||||
statement.includes('"searchVector"'),
|
||||
);
|
||||
|
||||
expect(createTableStatement).toBeDefined();
|
||||
|
||||
return createTableStatement as string;
|
||||
};
|
||||
|
||||
it('should emit the searchVector column as a STORED generated column derived from searchFieldMetadata', () => {
|
||||
const createTableStatement = generateSearchVectorColumnSql(
|
||||
new Map([[objectMetadataId, [buildSearchFieldMetadata()]]]),
|
||||
);
|
||||
|
||||
expect(createTableStatement).toContain(
|
||||
`"searchVector" tsvector GENERATED ALWAYS AS (to_tsvector('simple', COALESCE(public.unaccent_immutable("name"), ''))) STORED`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should tolerate a legacy NULL tsVectorFieldMetadataId row (pre-2.18 backfill)', () => {
|
||||
const createTableStatement = generateSearchVectorColumnSql(
|
||||
new Map([
|
||||
[
|
||||
objectMetadataId,
|
||||
[
|
||||
buildSearchFieldMetadata({
|
||||
tsVectorFieldMetadataId: null as never,
|
||||
}),
|
||||
],
|
||||
],
|
||||
]),
|
||||
);
|
||||
|
||||
expect(createTableStatement).toContain(
|
||||
`GENERATED ALWAYS AS (to_tsvector('simple', COALESCE(public.unaccent_immutable("name"), ''))) STORED`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should still emit a valid STORED generated column when no searchFieldMetadata rows exist', () => {
|
||||
const createTableStatement = generateSearchVectorColumnSql(new Map());
|
||||
|
||||
expect(createTableStatement).toContain(
|
||||
`"searchVector" tsvector GENERATED ALWAYS AS (to_tsvector('simple', NULL)) STORED`,
|
||||
);
|
||||
});
|
||||
});
|
||||
+74
-1
@@ -1,26 +1,82 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type';
|
||||
import { deriveSearchVectorAsExpressionForTsVectorField } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/derive-search-vector-as-expression-for-ts-vector-field.util';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants';
|
||||
import { type SearchFieldMetadataEntity } from 'src/engine/metadata-modules/search-field-metadata/search-field-metadata.entity';
|
||||
import { buildSqlColumnDefinition } from 'src/engine/twenty-orm/workspace-schema-manager/utils/build-sql-column-definition.util';
|
||||
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
import {
|
||||
escapeIdentifier,
|
||||
escapeLiteral,
|
||||
} from 'src/engine/workspace-manager/workspace-migration/utils/remove-sql-injection.util';
|
||||
import { generateColumnDefinitions } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/generate-column-definitions.util';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
import {
|
||||
type CreateEnumOperationSpec,
|
||||
EnumOperation,
|
||||
collectEnumOperationsForObject,
|
||||
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/workspace-schema-enum-operations.util';
|
||||
|
||||
const buildSearchFieldMetadataDerivationInputs = ({
|
||||
fieldMetadatas,
|
||||
objectSearchFieldMetadatas,
|
||||
}: {
|
||||
fieldMetadatas: FieldMetadataEntity[];
|
||||
objectSearchFieldMetadatas: SearchFieldMetadataEntity[];
|
||||
}): {
|
||||
indexedFieldById: ReadonlyMap<
|
||||
string,
|
||||
{ name: string; type: FieldMetadataType }
|
||||
>;
|
||||
flatSearchFieldMetadataMaps: FlatEntityMaps<FlatSearchFieldMetadata>;
|
||||
} => {
|
||||
const indexedFieldById = new Map(
|
||||
fieldMetadatas.map((fieldMetadata) => [
|
||||
fieldMetadata.id,
|
||||
{ name: fieldMetadata.name, type: fieldMetadata.type },
|
||||
]),
|
||||
);
|
||||
|
||||
const searchVectorFieldId = fieldMetadatas.find(
|
||||
(fieldMetadata) =>
|
||||
fieldMetadata.type === FieldMetadataType.TS_VECTOR &&
|
||||
fieldMetadata.name === SEARCH_VECTOR_FIELD.name,
|
||||
)?.id;
|
||||
|
||||
const byUniversalIdentifier = Object.fromEntries(
|
||||
objectSearchFieldMetadatas.map((searchFieldMetadata) => [
|
||||
searchFieldMetadata.universalIdentifier,
|
||||
{
|
||||
...searchFieldMetadata,
|
||||
tsVectorFieldMetadataId:
|
||||
searchFieldMetadata.tsVectorFieldMetadataId ?? searchVectorFieldId,
|
||||
} as unknown as FlatSearchFieldMetadata,
|
||||
]),
|
||||
);
|
||||
|
||||
return {
|
||||
indexedFieldById,
|
||||
flatSearchFieldMetadataMaps: {
|
||||
byUniversalIdentifier,
|
||||
universalIdentifierById: {},
|
||||
universalIdentifiersByApplicationId: {},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const generateWorkspaceSchemaDdl = (
|
||||
workspaceId: string,
|
||||
schemaName: string,
|
||||
objectMetadatas: ObjectMetadataEntity[],
|
||||
fieldsByObjectId: Map<string, FieldMetadataEntity[]>,
|
||||
searchFieldMetadatasByObjectId: Map<string, SearchFieldMetadataEntity[]>,
|
||||
): string[] => {
|
||||
const statements: string[] = [];
|
||||
|
||||
@@ -37,6 +93,13 @@ export const generateWorkspaceSchemaDdl = (
|
||||
const flatFieldMetadatas = fieldMetadatas as unknown as FlatFieldMetadata[];
|
||||
const flatObjectMetadata = objectMetadata as unknown as FlatObjectMetadata;
|
||||
|
||||
const { indexedFieldById, flatSearchFieldMetadataMaps } =
|
||||
buildSearchFieldMetadataDerivationInputs({
|
||||
fieldMetadatas,
|
||||
objectSearchFieldMetadatas:
|
||||
searchFieldMetadatasByObjectId.get(objectMetadata.id) ?? [],
|
||||
});
|
||||
|
||||
const enumOperations = collectEnumOperationsForObject({
|
||||
tableName,
|
||||
operation: EnumOperation.CREATE,
|
||||
@@ -57,6 +120,16 @@ export const generateWorkspaceSchemaDdl = (
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadata,
|
||||
workspaceId,
|
||||
searchVectorAsExpression: isFlatFieldMetadataOfType(
|
||||
flatFieldMetadata,
|
||||
FieldMetadataType.TS_VECTOR,
|
||||
)
|
||||
? deriveSearchVectorAsExpressionForTsVectorField({
|
||||
tsVectorFieldMetadataId: flatFieldMetadata.id,
|
||||
flatSearchFieldMetadataMaps,
|
||||
indexedFieldById,
|
||||
})
|
||||
: undefined,
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
+6
-1
@@ -5,10 +5,15 @@ import { WorkspaceExportCommand } from 'src/database/commands/workspace-export/w
|
||||
import { WorkspaceExportService } from 'src/database/commands/workspace-export/workspace-export.service';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { SearchFieldMetadataEntity } from 'src/engine/metadata-modules/search-field-metadata/search-field-metadata.entity';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([ObjectMetadataEntity, FieldMetadataEntity]),
|
||||
TypeOrmModule.forFeature([
|
||||
ObjectMetadataEntity,
|
||||
FieldMetadataEntity,
|
||||
SearchFieldMetadataEntity,
|
||||
]),
|
||||
],
|
||||
providers: [WorkspaceExportCommand, WorkspaceExportService],
|
||||
})
|
||||
|
||||
+39
-10
@@ -11,20 +11,21 @@ import {
|
||||
Repository,
|
||||
} from 'typeorm';
|
||||
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
|
||||
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
|
||||
import { escapeIdentifier } from 'src/engine/workspace-manager/workspace-migration/utils/remove-sql-injection.util';
|
||||
import { getCoreEntityMetadatasWithWorkspaceId } from 'src/database/commands/workspace-export/utils/get-core-entity-metadatas-with-workspace-id.util';
|
||||
import { generateWorkspaceSchemaDdl } from 'src/database/commands/workspace-export/utils/generate-workspace-schema-ddl.util';
|
||||
import { buildInsertPrefix } from 'src/database/commands/workspace-export/utils/build-insert-prefix.util';
|
||||
import { buildWorkspaceTableColumnSets } from 'src/database/commands/workspace-export/utils/build-workspace-table-column-sets.util';
|
||||
import { formatSqlValue } from 'src/database/commands/workspace-export/utils/format-sql-value.util';
|
||||
import { formatPgCopyField } from './utils/format-pg-copy-value.util';
|
||||
import { generateWorkspaceSchemaDdl } from 'src/database/commands/workspace-export/utils/generate-workspace-schema-ddl.util';
|
||||
import { getCoreEntityMetadatasWithWorkspaceId } from 'src/database/commands/workspace-export/utils/get-core-entity-metadatas-with-workspace-id.util';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { SearchFieldMetadataEntity } from 'src/engine/metadata-modules/search-field-metadata/search-field-metadata.entity';
|
||||
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
|
||||
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
import { escapeIdentifier } from 'src/engine/workspace-manager/workspace-migration/utils/remove-sql-injection.util';
|
||||
import { isNonEmptyArray } from 'twenty-shared/utils';
|
||||
import { formatPgCopyField } from './utils/format-pg-copy-value.util';
|
||||
|
||||
const BATCH_SIZE = 10_000;
|
||||
|
||||
@@ -57,6 +58,9 @@ export class WorkspaceExportService {
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
@InjectRepository(FieldMetadataEntity)
|
||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||
// eslint-disable-next-line twenty/prefer-workspace-scoped-repository -- Ignored
|
||||
@InjectRepository(SearchFieldMetadataEntity)
|
||||
private readonly searchFieldMetadataRepository: Repository<SearchFieldMetadataEntity>,
|
||||
) {}
|
||||
|
||||
async exportWorkspace({
|
||||
@@ -95,6 +99,28 @@ export class WorkspaceExportService {
|
||||
fieldsByObjectId.set(fieldMetadata.objectMetadataId, objectFields);
|
||||
}
|
||||
|
||||
const searchFieldMetadatas = await this.searchFieldMetadataRepository.find({
|
||||
where: { workspaceId },
|
||||
});
|
||||
|
||||
const searchFieldMetadatasByObjectId = new Map<
|
||||
string,
|
||||
SearchFieldMetadataEntity[]
|
||||
>();
|
||||
|
||||
for (const searchFieldMetadata of searchFieldMetadatas) {
|
||||
const objectSearchFieldMetadatas =
|
||||
searchFieldMetadatasByObjectId.get(
|
||||
searchFieldMetadata.objectMetadataId,
|
||||
) ?? [];
|
||||
|
||||
objectSearchFieldMetadatas.push(searchFieldMetadata);
|
||||
searchFieldMetadatasByObjectId.set(
|
||||
searchFieldMetadata.objectMetadataId,
|
||||
objectSearchFieldMetadatas,
|
||||
);
|
||||
}
|
||||
|
||||
mkdirSync(outputPath, { recursive: true });
|
||||
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
@@ -117,6 +143,7 @@ export class WorkspaceExportService {
|
||||
schemaName,
|
||||
objectMetadatas,
|
||||
fieldsByObjectId,
|
||||
searchFieldMetadatasByObjectId,
|
||||
stream,
|
||||
);
|
||||
|
||||
@@ -327,6 +354,7 @@ export class WorkspaceExportService {
|
||||
schemaName: string,
|
||||
objectMetadatas: ObjectMetadataEntity[],
|
||||
fieldsByObjectId: Map<string, FieldMetadataEntity[]>,
|
||||
searchFieldMetadatasByObjectId: Map<string, SearchFieldMetadataEntity[]>,
|
||||
stream: WriteStream,
|
||||
): void {
|
||||
this.logger.log('Generating workspace schema DDL from metadata...');
|
||||
@@ -336,6 +364,7 @@ export class WorkspaceExportService {
|
||||
schemaName,
|
||||
objectMetadatas,
|
||||
fieldsByObjectId,
|
||||
searchFieldMetadatasByObjectId,
|
||||
);
|
||||
|
||||
this.logger.log(` ${ddlStatements.length} DDL statements`);
|
||||
|
||||
+1
@@ -114,6 +114,7 @@ export const fromFieldManifestToUniversalFlatFieldMetadata = ({
|
||||
calendarViewUniversalIdentifiers: [],
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
viewSortUniversalIdentifiers: [],
|
||||
searchFieldMetadataUniversalIdentifiers: [],
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
+4
-17
@@ -2,7 +2,6 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type Manifest } from 'twenty-shared/application';
|
||||
import { MAX_CUSTOM_INDEXES_PER_OBJECT } from 'twenty-shared/constants';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { fromApplicationVariableManifestToUniversalFlatApplicationVariable } from 'src/engine/core-modules/application/application-manifest/converters/from-application-variable-manifest-to-universal-flat-application-variable.util';
|
||||
@@ -25,7 +24,6 @@ import { fromRoleManifestToUniversalFlatRole } from 'src/engine/core-modules/app
|
||||
import { fromRowLevelPermissionPredicateGroupManifestToUniversalFlatRowLevelPermissionPredicateGroup } from 'src/engine/core-modules/application/application-manifest/converters/from-row-level-permission-predicate-group-manifest-to-universal-flat-row-level-permission-predicate-group.util';
|
||||
import { fromRowLevelPermissionPredicateManifestToUniversalFlatRowLevelPermissionPredicate } from 'src/engine/core-modules/application/application-manifest/converters/from-row-level-permission-predicate-manifest-to-universal-flat-row-level-permission-predicate.util';
|
||||
import { fromSkillManifestToUniversalFlatSkill } from 'src/engine/core-modules/application/application-manifest/converters/from-skill-manifest-to-universal-flat-skill.util';
|
||||
import { computeSearchVectorUniversalSettingsFromObjectManifest } from 'src/engine/core-modules/application/application-manifest/utils/compute-search-vector-universal-settings-from-object-manifest.util';
|
||||
import { fromViewFieldGroupManifestToUniversalFlatViewFieldGroup } from 'src/engine/core-modules/application/application-manifest/converters/from-view-field-group-manifest-to-universal-flat-view-field-group.util';
|
||||
import { fromViewFieldManifestToUniversalFlatViewField } from 'src/engine/core-modules/application/application-manifest/converters/from-view-field-manifest-to-universal-flat-view-field.util';
|
||||
import { fromViewFilterGroupManifestToUniversalFlatViewFilterGroup } from 'src/engine/core-modules/application/application-manifest/converters/from-view-filter-group-manifest-to-universal-flat-view-filter-group.util';
|
||||
@@ -95,21 +93,10 @@ export class ComputeApplicationManifestAllUniversalFlatEntityMapsService {
|
||||
});
|
||||
|
||||
for (const fieldManifest of objectManifest.fields) {
|
||||
const enrichedFieldManifest =
|
||||
fieldManifest.type === FieldMetadataType.TS_VECTOR &&
|
||||
!isDefined(fieldManifest.universalSettings)
|
||||
? {
|
||||
...fieldManifest,
|
||||
objectUniversalIdentifier: objectManifest.universalIdentifier,
|
||||
universalSettings:
|
||||
computeSearchVectorUniversalSettingsFromObjectManifest({
|
||||
objectManifest,
|
||||
}),
|
||||
}
|
||||
: {
|
||||
...fieldManifest,
|
||||
objectUniversalIdentifier: objectManifest.universalIdentifier,
|
||||
};
|
||||
const enrichedFieldManifest = {
|
||||
...fieldManifest,
|
||||
objectUniversalIdentifier: objectManifest.universalIdentifier,
|
||||
};
|
||||
|
||||
const flatFieldMetadata = fromFieldManifestToUniversalFlatFieldMetadata(
|
||||
{
|
||||
|
||||
-149
@@ -1,149 +0,0 @@
|
||||
import { type ObjectManifest } from 'twenty-shared/application';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { computeSearchVectorUniversalSettingsFromObjectManifest } from 'src/engine/core-modules/application/application-manifest/utils/compute-search-vector-universal-settings-from-object-manifest.util';
|
||||
|
||||
const buildObjectManifest = (
|
||||
overrides: Partial<ObjectManifest> & {
|
||||
fields: ObjectManifest['fields'];
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: string;
|
||||
},
|
||||
): ObjectManifest => ({
|
||||
universalIdentifier: 'obj-uuid-1',
|
||||
nameSingular: 'testObject',
|
||||
namePlural: 'testObjects',
|
||||
labelSingular: 'Test Object',
|
||||
labelPlural: 'Test Objects',
|
||||
...overrides,
|
||||
});
|
||||
|
||||
describe('computeSearchVectorUniversalSettingsFromObjectManifest', () => {
|
||||
it('should return asExpression and generatedType for a TEXT label identifier field', () => {
|
||||
const result = computeSearchVectorUniversalSettingsFromObjectManifest({
|
||||
objectManifest: buildObjectManifest({
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: 'field-uuid-name',
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: 'field-uuid-name',
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
type: FieldMetadataType.TEXT,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
expect(result?.generatedType).toBe('STORED');
|
||||
expect(result?.asExpression).toContain("to_tsvector('simple'");
|
||||
expect(result?.asExpression).toContain('"name"');
|
||||
});
|
||||
|
||||
it('should return asExpression for a FULL_NAME label identifier field', () => {
|
||||
const result = computeSearchVectorUniversalSettingsFromObjectManifest({
|
||||
objectManifest: buildObjectManifest({
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: 'field-uuid-name',
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: 'field-uuid-name',
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
type: FieldMetadataType.FULL_NAME,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
expect(result?.generatedType).toBe('STORED');
|
||||
expect(result?.asExpression).toContain("to_tsvector('simple'");
|
||||
expect(result?.asExpression).toContain('"nameFirstName"');
|
||||
expect(result?.asExpression).toContain('"nameLastName"');
|
||||
});
|
||||
|
||||
it('should return asExpression for an EMAILS label identifier field', () => {
|
||||
const result = computeSearchVectorUniversalSettingsFromObjectManifest({
|
||||
objectManifest: buildObjectManifest({
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: 'field-uuid-email',
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: 'field-uuid-email',
|
||||
name: 'email',
|
||||
label: 'Email',
|
||||
type: FieldMetadataType.EMAILS,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
expect(result?.generatedType).toBe('STORED');
|
||||
expect(result?.asExpression).toContain("to_tsvector('simple'");
|
||||
expect(result?.asExpression).toContain('"emailPrimaryEmail"');
|
||||
});
|
||||
|
||||
it('should return asExpression for a UUID label identifier field', () => {
|
||||
const result = computeSearchVectorUniversalSettingsFromObjectManifest({
|
||||
objectManifest: buildObjectManifest({
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: 'field-uuid-id',
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: 'field-uuid-id',
|
||||
name: 'externalId',
|
||||
label: 'External ID',
|
||||
type: FieldMetadataType.UUID,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
expect(result?.generatedType).toBe('STORED');
|
||||
expect(result?.asExpression).toContain("to_tsvector('simple'");
|
||||
expect(result?.asExpression).toContain('"externalId"');
|
||||
});
|
||||
|
||||
it('should return null when label identifier field is not found in fields', () => {
|
||||
const result = computeSearchVectorUniversalSettingsFromObjectManifest({
|
||||
objectManifest: buildObjectManifest({
|
||||
labelIdentifierFieldMetadataUniversalIdentifier:
|
||||
'non-existent-field-uuid',
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: 'field-uuid-name',
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
type: FieldMetadataType.TEXT,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should return null when label identifier field has a non-searchable type', () => {
|
||||
const result = computeSearchVectorUniversalSettingsFromObjectManifest({
|
||||
objectManifest: buildObjectManifest({
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: 'field-uuid-number',
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: 'field-uuid-number',
|
||||
name: 'amount',
|
||||
label: 'Amount',
|
||||
type: FieldMetadataType.NUMBER,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should return null when fields array is empty', () => {
|
||||
const result = computeSearchVectorUniversalSettingsFromObjectManifest({
|
||||
objectManifest: buildObjectManifest({
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: 'field-uuid-name',
|
||||
fields: [],
|
||||
}),
|
||||
});
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
import { type ObjectManifest } from 'twenty-shared/application';
|
||||
import {
|
||||
type FieldMetadataUniversalSettings,
|
||||
FieldMetadataType,
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
|
||||
import { isDefined, isSearchableFieldType } from 'twenty-shared/utils';
|
||||
|
||||
export const computeSearchVectorUniversalSettingsFromObjectManifest = ({
|
||||
objectManifest,
|
||||
}: {
|
||||
objectManifest: ObjectManifest;
|
||||
}): FieldMetadataUniversalSettings<FieldMetadataType.TS_VECTOR> => {
|
||||
const labelIdentifierField = objectManifest.fields.find(
|
||||
(field) =>
|
||||
field.universalIdentifier ===
|
||||
objectManifest.labelIdentifierFieldMetadataUniversalIdentifier,
|
||||
);
|
||||
|
||||
if (
|
||||
!isDefined(labelIdentifierField) ||
|
||||
!isSearchableFieldType(labelIdentifierField.type)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
asExpression: getTsVectorColumnExpressionFromFields([
|
||||
{
|
||||
name: labelIdentifierField.name,
|
||||
type: labelIdentifierField.type,
|
||||
},
|
||||
]),
|
||||
generatedType: 'STORED',
|
||||
};
|
||||
};
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
type FieldMetadataLoaderPayload,
|
||||
type IndexFieldMetadataLoaderPayload,
|
||||
type IndexMetadataLoaderPayload,
|
||||
type SearchFieldMetadataLoaderPayload,
|
||||
type IsConfiguredLoaderPayload,
|
||||
type MorphRelationLoaderPayload,
|
||||
type StandardApplicationIdLoaderPayload,
|
||||
@@ -23,6 +24,7 @@ import { type RelationDTO } from 'src/engine/metadata-modules/field-metadata/dto
|
||||
import { type IndexFieldMetadataDTO } from 'src/engine/metadata-modules/index-metadata/dtos/index-field-metadata.dto';
|
||||
import { type IndexMetadataDTO } from 'src/engine/metadata-modules/index-metadata/dtos/index-metadata.dto';
|
||||
import { type ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
|
||||
import { type SearchFieldMetadataDTO } from 'src/engine/metadata-modules/search-field-metadata/dtos/search-field-metadata.dto';
|
||||
import { type ViewFieldGroupDTO } from 'src/engine/metadata-modules/view-field-group/dtos/view-field-group.dto';
|
||||
import { type ViewFieldDTO } from 'src/engine/metadata-modules/view-field/dtos/view-field.dto';
|
||||
import { type ViewFilterGroupDTO } from 'src/engine/metadata-modules/view-filter-group/dtos/view-filter-group.dto';
|
||||
@@ -53,6 +55,11 @@ export interface IDataloaders {
|
||||
IndexFieldMetadataDTO[]
|
||||
>;
|
||||
|
||||
searchFieldMetadataLoader: DataLoader<
|
||||
SearchFieldMetadataLoaderPayload,
|
||||
SearchFieldMetadataDTO[]
|
||||
>;
|
||||
|
||||
objectMetadataLoader: DataLoader<
|
||||
ObjectMetadataLoaderPayload,
|
||||
ObjectMetadataDTO | null
|
||||
|
||||
@@ -31,6 +31,7 @@ import { resolveMorphRelationsFromFlatFieldMetadata } from 'src/engine/metadata-
|
||||
import { resolveRelationFromFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/resolve-relation-from-flat-field-metadata.util';
|
||||
import { fromFlatObjectMetadataToObjectMetadataDto } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-to-object-metadata-dto.util';
|
||||
import { getMorphNameFromMorphFieldMetadataName } from 'src/engine/metadata-modules/flat-object-metadata/utils/get-morph-name-from-morph-field-metadata-name.util';
|
||||
import { fromFlatSearchFieldMetadataToSearchFieldMetadataDto } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/from-flat-search-field-metadata-to-search-field-metadata-dto.util';
|
||||
import { fromFlatViewFieldGroupToViewFieldGroupDto } from 'src/engine/metadata-modules/view-field-group/utils/from-flat-view-field-group-to-view-field-group-dto.util';
|
||||
import { fromFlatViewFieldToViewFieldDto } from 'src/engine/metadata-modules/view-field/utils/from-flat-view-field-to-view-field-dto.util';
|
||||
import { fromFlatViewFilterToViewFilterDto } from 'src/engine/metadata-modules/view-filter/utils/from-flat-view-filter-to-view-filter-dto.util';
|
||||
@@ -41,6 +42,7 @@ import { type IndexFieldMetadataDTO } from 'src/engine/metadata-modules/index-me
|
||||
import { type IndexMetadataDTO } from 'src/engine/metadata-modules/index-metadata/dtos/index-metadata.dto';
|
||||
import { ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type SearchFieldMetadataDTO } from 'src/engine/metadata-modules/search-field-metadata/dtos/search-field-metadata.dto';
|
||||
|
||||
export type RelationMetadataLoaderPayload = {
|
||||
workspaceId: string;
|
||||
@@ -76,6 +78,11 @@ export type IndexFieldMetadataLoaderPayload = {
|
||||
indexMetadata: Pick<IndexMetadataInterface, 'id'>;
|
||||
};
|
||||
|
||||
export type SearchFieldMetadataLoaderPayload = {
|
||||
workspaceId: string;
|
||||
objectMetadata: Pick<ObjectMetadataEntity, 'id'>;
|
||||
};
|
||||
|
||||
export type ObjectMetadataLoaderPayload = {
|
||||
workspaceId: string;
|
||||
objectMetadataId: string;
|
||||
@@ -145,6 +152,7 @@ export class DataloaderService {
|
||||
const fieldMetadataLoader = this.createFieldMetadataLoader();
|
||||
const indexMetadataLoader = this.createIndexMetadataLoader();
|
||||
const indexFieldMetadataLoader = this.createIndexFieldMetadataLoader();
|
||||
const searchFieldMetadataLoader = this.createSearchFieldMetadataLoader();
|
||||
const objectMetadataLoader = this.createObjectMetadataLoader();
|
||||
const viewFieldGroupsByViewIdLoader =
|
||||
this.createViewFieldGroupsByViewIdLoader();
|
||||
@@ -168,6 +176,7 @@ export class DataloaderService {
|
||||
fieldMetadataLoader,
|
||||
indexMetadataLoader,
|
||||
indexFieldMetadataLoader,
|
||||
searchFieldMetadataLoader,
|
||||
objectMetadataLoader,
|
||||
viewFieldGroupsByViewIdLoader,
|
||||
viewFieldsByViewFieldGroupIdLoader,
|
||||
@@ -306,6 +315,46 @@ export class DataloaderService {
|
||||
);
|
||||
}
|
||||
|
||||
private createSearchFieldMetadataLoader() {
|
||||
return new DataLoader<
|
||||
SearchFieldMetadataLoaderPayload,
|
||||
SearchFieldMetadataDTO[]
|
||||
>(async (dataLoaderParams: SearchFieldMetadataLoaderPayload[]) => {
|
||||
const workspaceId = dataLoaderParams[0].workspaceId;
|
||||
const objectMetadataIds = dataLoaderParams.map(
|
||||
(dataLoaderParam) => dataLoaderParam.objectMetadata.id,
|
||||
);
|
||||
|
||||
const { flatSearchFieldMetadataMaps, flatObjectMetadataMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: [
|
||||
'flatSearchFieldMetadataMaps',
|
||||
'flatObjectMetadataMaps',
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
return objectMetadataIds.map((objectMetadataId) => {
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: objectMetadataId,
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const searchFieldMetadatas =
|
||||
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityIds: flatObjectMetadata.searchFieldMetadataIds,
|
||||
flatEntityMaps: flatSearchFieldMetadataMaps,
|
||||
});
|
||||
|
||||
return searchFieldMetadatas.map(
|
||||
fromFlatSearchFieldMetadataToSearchFieldMetadataDto,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private createFieldMetadataLoader() {
|
||||
return new DataLoader<FieldMetadataLoaderPayload, FieldMetadataDTO[]>(
|
||||
async (dataLoaderParams: FieldMetadataLoaderPayload[]) => {
|
||||
|
||||
+7
@@ -30,6 +30,7 @@ import { AssignTypeIfIsMorphOrRelationFieldMetadataType } from 'src/engine/metad
|
||||
import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
|
||||
import { SearchFieldMetadataEntity } from 'src/engine/metadata-modules/search-field-metadata/search-field-metadata.entity';
|
||||
import { ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity';
|
||||
import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
|
||||
import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
@@ -238,4 +239,10 @@ export class FieldMetadataEntity<
|
||||
|
||||
@OneToMany(() => ViewSortEntity, (viewSort) => viewSort.fieldMetadata)
|
||||
viewSorts: Relation<ViewSortEntity[]>;
|
||||
|
||||
@OneToMany(
|
||||
() => SearchFieldMetadataEntity,
|
||||
(searchFieldMetadata) => searchFieldMetadata.fieldMetadata,
|
||||
)
|
||||
searchFieldMetadatas: Relation<SearchFieldMetadataEntity[]>;
|
||||
}
|
||||
|
||||
+3
-8
@@ -16,7 +16,6 @@ import {
|
||||
FieldMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
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 { findManyFlatEntityByUniversalIdentifierInUniversalFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-universal-identifier-in-universal-flat-entity-maps-or-throw.util';
|
||||
@@ -114,7 +113,6 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
flatIndexesToDelete,
|
||||
flatIndexesToUpdate,
|
||||
searchFieldMetadatasToDelete,
|
||||
flatSearchVectorFieldsToUpdate,
|
||||
} = fromDeleteFieldInputToFlatFieldMetadatasToDelete({
|
||||
deleteOneFieldInput,
|
||||
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
|
||||
@@ -162,7 +160,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
fieldMetadata: {
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: flatFieldMetadatasToDelete,
|
||||
flatEntityToUpdate: flatSearchVectorFieldsToUpdate,
|
||||
flatEntityToUpdate: [],
|
||||
},
|
||||
index: {
|
||||
flatEntityToCreate: [],
|
||||
@@ -228,7 +226,6 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
flatViewGroupMaps: existingFlatViewGroupMaps,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
flatViewFieldMaps: existingFlatViewFieldMaps,
|
||||
flatSearchFieldMetadataMaps: existingFlatSearchFieldMetadataMaps,
|
||||
} = await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
@@ -240,7 +237,6 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
'flatViewGroupMaps',
|
||||
'flatViewMaps',
|
||||
'flatViewFieldMaps',
|
||||
'flatSearchFieldMetadataMaps',
|
||||
],
|
||||
},
|
||||
);
|
||||
@@ -254,7 +250,6 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
flatViewGroupMaps: existingFlatViewGroupMaps,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
flatViewFieldMaps: existingFlatViewFieldMaps,
|
||||
flatSearchFieldMetadataMaps: existingFlatSearchFieldMetadataMaps,
|
||||
flatApplication: resolvedOwnerFlatApplication,
|
||||
isSystemBuild,
|
||||
});
|
||||
@@ -354,8 +349,8 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
},
|
||||
);
|
||||
|
||||
return findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatFieldMetadatasToUpdate[0].id,
|
||||
return findFlatEntityByUniversalIdentifierOrThrow({
|
||||
universalIdentifier: flatFieldMetadatasToUpdate[0].universalIdentifier,
|
||||
flatEntityMaps: recomputedFlatFieldMetadataMaps,
|
||||
});
|
||||
}
|
||||
|
||||
+5
@@ -1785,6 +1785,11 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
|
||||
toStringify: false,
|
||||
universalProperty: 'fieldMetadataUniversalIdentifier',
|
||||
},
|
||||
tsVectorFieldMetadataId: {
|
||||
toCompare: false,
|
||||
toStringify: false,
|
||||
universalProperty: 'tsVectorFieldMetadataUniversalIdentifier',
|
||||
},
|
||||
position: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
|
||||
+3
@@ -312,6 +312,9 @@ export const ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY = {
|
||||
fieldMetadata: {
|
||||
foreignKey: 'fieldMetadataId',
|
||||
},
|
||||
tsVectorFieldMetadata: {
|
||||
foreignKey: 'tsVectorFieldMetadataId',
|
||||
},
|
||||
},
|
||||
} as const satisfies ManyToOneMetadataRelationsProperties;
|
||||
|
||||
|
||||
+8
-1
@@ -544,10 +544,17 @@ export const ALL_MANY_TO_ONE_METADATA_RELATIONS = {
|
||||
fieldMetadata: {
|
||||
metadataName: 'fieldMetadata',
|
||||
foreignKey: 'fieldMetadataId',
|
||||
inverseOneToManyProperty: null,
|
||||
inverseOneToManyProperty: 'searchFieldMetadatas',
|
||||
isNullable: false,
|
||||
universalForeignKey: 'fieldMetadataUniversalIdentifier',
|
||||
},
|
||||
tsVectorFieldMetadata: {
|
||||
metadataName: 'fieldMetadata',
|
||||
foreignKey: 'tsVectorFieldMetadataId',
|
||||
inverseOneToManyProperty: null,
|
||||
isNullable: false,
|
||||
universalForeignKey: 'tsVectorFieldMetadataUniversalIdentifier',
|
||||
},
|
||||
},
|
||||
} as const satisfies ManyToOneMetadataRelationsProperties;
|
||||
|
||||
|
||||
+6
@@ -77,6 +77,12 @@ export const ALL_ONE_TO_MANY_METADATA_RELATIONS = {
|
||||
universalFlatEntityForeignKeyAggregator:
|
||||
'mainGroupByFieldMetadataViewUniversalIdentifiers',
|
||||
},
|
||||
searchFieldMetadatas: {
|
||||
metadataName: 'searchFieldMetadata',
|
||||
flatEntityForeignKeyAggregator: 'searchFieldMetadataIds',
|
||||
universalFlatEntityForeignKeyAggregator:
|
||||
'searchFieldMetadataUniversalIdentifiers',
|
||||
},
|
||||
},
|
||||
objectMetadata: {
|
||||
fields: {
|
||||
|
||||
+4
-1
@@ -17,7 +17,8 @@ type FieldMetadataOneToManySyncableRelations =
|
||||
type Assertions = [
|
||||
// FieldMetadataEntity OneToMany relations (all targets):
|
||||
// - indexFieldMetadatas, fieldPermissions, viewFields, viewFilters,
|
||||
// - kanbanAggregateOperationViews, calendarViews, mainGroupByFieldMetadataViews, viewSorts
|
||||
// - kanbanAggregateOperationViews, calendarViews, mainGroupByFieldMetadataViews,
|
||||
// - viewSorts, searchFieldMetadatas
|
||||
Expect<
|
||||
Equal<
|
||||
FieldMetadataOneToManyRelations,
|
||||
@@ -29,6 +30,7 @@ type Assertions = [
|
||||
| 'calendarViews'
|
||||
| 'mainGroupByFieldMetadataViews'
|
||||
| 'viewSorts'
|
||||
| 'searchFieldMetadatas'
|
||||
>
|
||||
>,
|
||||
|
||||
@@ -43,6 +45,7 @@ type Assertions = [
|
||||
| 'calendarViews'
|
||||
| 'mainGroupByFieldMetadataViews'
|
||||
| 'viewSorts'
|
||||
| 'searchFieldMetadatas'
|
||||
>
|
||||
>,
|
||||
];
|
||||
|
||||
+2
-1
@@ -10,7 +10,7 @@ type FieldMetadataRelatedProperties =
|
||||
type Assertions = [
|
||||
// FieldMetadataEntity has both ManyToOne and OneToMany relations
|
||||
// ManyToOne: object, workspace, application, relationTargetFieldMetadata, relationTargetObjectMetadata
|
||||
// OneToMany: indexFieldMetadatas, fieldPermissions, viewFields, viewFilters, kanbanAggregateOperationViews, calendarViews, mainGroupByFieldMetadataViews, viewSorts
|
||||
// OneToMany: indexFieldMetadatas, fieldPermissions, viewFields, viewFilters, kanbanAggregateOperationViews, calendarViews, mainGroupByFieldMetadataViews, viewSorts, searchFieldMetadatas
|
||||
Expect<
|
||||
Equal<
|
||||
FieldMetadataRelatedProperties,
|
||||
@@ -27,6 +27,7 @@ type Assertions = [
|
||||
| 'calendarViews'
|
||||
| 'mainGroupByFieldMetadataViews'
|
||||
| 'viewSorts'
|
||||
| 'searchFieldMetadatas'
|
||||
>
|
||||
>,
|
||||
];
|
||||
|
||||
+1
@@ -23,6 +23,7 @@ exports[`getMetadataRelatedMetadataNames should return related metadata names fo
|
||||
"viewField",
|
||||
"viewFilter",
|
||||
"view",
|
||||
"searchFieldMetadata",
|
||||
]
|
||||
`;
|
||||
|
||||
|
||||
+1
-1
@@ -7,11 +7,11 @@ exports[`sortMetadataNamesChildrenFirst should return metadata names sorted with
|
||||
"viewFilter",
|
||||
"commandMenuItem",
|
||||
"fieldPermission",
|
||||
"searchFieldMetadata",
|
||||
"viewField",
|
||||
"objectPermission",
|
||||
"pageLayoutWidget",
|
||||
"rolePermissionFlag",
|
||||
"searchFieldMetadata",
|
||||
"viewSort",
|
||||
"index",
|
||||
"pageLayout",
|
||||
|
||||
+15
@@ -12,6 +12,7 @@ import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity
|
||||
import { type MetadataRelatedFlatEntityMapsKeys } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-flat-entity-maps-keys.type';
|
||||
import { type MetadataFlatEntityAndRelatedFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-types.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util';
|
||||
import { addUniversalFlatEntityToUniversalFlatEntityAndRelatedEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/add-universal-flat-entity-to-universal-flat-entity-and-related-entity-maps-through-mutation-or-throw.util';
|
||||
import { replaceFlatEntityInFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/utils/replace-flat-entity-in-flat-entity-maps-through-mutation-or-throw.util';
|
||||
@@ -22,6 +23,7 @@ type AddFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrowArgs<
|
||||
metadataName: T;
|
||||
flatEntity: MetadataFlatEntity<T>;
|
||||
flatEntityAndRelatedMapsToMutate: MetadataFlatEntityAndRelatedFlatEntityMaps<T>;
|
||||
skipMissingRelatedEntities?: boolean;
|
||||
};
|
||||
|
||||
export const addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow =
|
||||
@@ -29,6 +31,7 @@ export const addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow
|
||||
metadataName,
|
||||
flatEntity,
|
||||
flatEntityAndRelatedMapsToMutate,
|
||||
skipMissingRelatedEntities = false,
|
||||
}: AddFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrowArgs<T>) => {
|
||||
addUniversalFlatEntityToUniversalFlatEntityAndRelatedEntityMapsThroughMutationOrThrow(
|
||||
{
|
||||
@@ -36,6 +39,7 @@ export const addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow
|
||||
universalFlatEntity: flatEntity,
|
||||
universalFlatEntityAndRelatedMapsToMutate:
|
||||
flatEntityAndRelatedMapsToMutate,
|
||||
skipMissingRelatedEntities,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -101,6 +105,17 @@ export const addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow
|
||||
continue;
|
||||
}
|
||||
|
||||
if (skipMissingRelatedEntities) {
|
||||
const maybeRelatedFlatEntity = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatEntityRelatedEntityForeignKeyValue,
|
||||
flatEntityMaps: relatedFlatEntityMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(maybeRelatedFlatEntity)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const relatedFlatEntity = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatEntityRelatedEntityForeignKeyValue,
|
||||
flatEntityMaps: relatedFlatEntityMetadataMaps,
|
||||
|
||||
+2
@@ -57,6 +57,8 @@ export const getFlatFieldMetadataMock = <T extends FieldMetadataType>(
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
viewSortIds: [],
|
||||
viewSortUniversalIdentifiers: [],
|
||||
searchFieldMetadataIds: [],
|
||||
searchFieldMetadataUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
...overrides,
|
||||
};
|
||||
|
||||
+2
@@ -77,6 +77,8 @@ export const getRelationTargetFlatFieldMetadataMock = ({
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
viewSortIds: [],
|
||||
viewSortUniversalIdentifiers: [],
|
||||
searchFieldMetadataIds: [],
|
||||
searchFieldMetadataUniversalIdentifiers: [],
|
||||
universalSettings: settings,
|
||||
};
|
||||
};
|
||||
|
||||
+4
@@ -139,6 +139,7 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataUniversalIdentifier": Any<String>,
|
||||
"relationTargetObjectMetadataUniversalIdentifier": Any<String>,
|
||||
"searchFieldMetadataUniversalIdentifiers": [],
|
||||
"standardOverrides": null,
|
||||
"type": "MORPH_RELATION",
|
||||
"universalIdentifier": Any<String>,
|
||||
@@ -174,6 +175,7 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataUniversalIdentifier": Any<String>,
|
||||
"relationTargetObjectMetadataUniversalIdentifier": Any<String>,
|
||||
"searchFieldMetadataUniversalIdentifiers": [],
|
||||
"standardOverrides": null,
|
||||
"type": "RELATION",
|
||||
"universalIdentifier": Any<String>,
|
||||
@@ -211,6 +213,7 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataUniversalIdentifier": Any<String>,
|
||||
"relationTargetObjectMetadataUniversalIdentifier": Any<String>,
|
||||
"searchFieldMetadataUniversalIdentifiers": [],
|
||||
"standardOverrides": null,
|
||||
"type": "MORPH_RELATION",
|
||||
"universalIdentifier": Any<String>,
|
||||
@@ -246,6 +249,7 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataUniversalIdentifier": Any<String>,
|
||||
"relationTargetObjectMetadataUniversalIdentifier": Any<String>,
|
||||
"searchFieldMetadataUniversalIdentifiers": [],
|
||||
"standardOverrides": null,
|
||||
"type": "RELATION",
|
||||
"universalIdentifier": Any<String>,
|
||||
|
||||
+14
@@ -14,6 +14,7 @@ import { fromFieldMetadataEntityToFlatFieldMetadata } from 'src/engine/metadata-
|
||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { computeUniqueFieldMetadataIdsFromIndexEntities } from 'src/engine/metadata-modules/index-metadata/utils/compute-unique-field-metadata-ids-from-index-entities.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { SearchFieldMetadataEntity } from 'src/engine/metadata-modules/search-field-metadata/search-field-metadata.entity';
|
||||
import { ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity';
|
||||
import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
|
||||
import { ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.entity';
|
||||
@@ -50,6 +51,8 @@ export class WorkspaceFlatFieldMetadataMapCacheService extends WorkspaceCachePro
|
||||
private readonly viewSortRepository: WorkspaceScopedRepository<ViewSortEntity>,
|
||||
@InjectWorkspaceScopedRepository(ViewEntity)
|
||||
private readonly viewRepository: WorkspaceScopedRepository<ViewEntity>,
|
||||
@InjectWorkspaceScopedRepository(SearchFieldMetadataEntity)
|
||||
private readonly searchFieldMetadataRepository: WorkspaceScopedRepository<SearchFieldMetadataEntity>,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -66,6 +69,7 @@ export class WorkspaceFlatFieldMetadataMapCacheService extends WorkspaceCachePro
|
||||
viewFilters,
|
||||
viewSorts,
|
||||
views,
|
||||
searchFieldMetadatas,
|
||||
] = await Promise.all([
|
||||
this.fieldMetadataRepository.find({
|
||||
where: { workspaceId },
|
||||
@@ -108,6 +112,9 @@ export class WorkspaceFlatFieldMetadataMapCacheService extends WorkspaceCachePro
|
||||
],
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.searchFieldMetadataRepository.find(workspaceId, {
|
||||
select: ['id', 'universalIdentifier', 'fieldMetadataId'],
|
||||
}),
|
||||
]);
|
||||
|
||||
const [
|
||||
@@ -117,6 +124,7 @@ export class WorkspaceFlatFieldMetadataMapCacheService extends WorkspaceCachePro
|
||||
kanbanViewsByFieldId,
|
||||
mainGroupByFieldMetadataViewsByFieldId,
|
||||
viewSortsByFieldId,
|
||||
searchFieldMetadatasByFieldId,
|
||||
] = (
|
||||
[
|
||||
{
|
||||
@@ -143,6 +151,10 @@ export class WorkspaceFlatFieldMetadataMapCacheService extends WorkspaceCachePro
|
||||
entities: viewSorts,
|
||||
foreignKey: 'fieldMetadataId',
|
||||
},
|
||||
{
|
||||
entities: searchFieldMetadatas,
|
||||
foreignKey: 'fieldMetadataId',
|
||||
},
|
||||
] as const
|
||||
).map(regroupEntitiesByRelatedEntityId);
|
||||
|
||||
@@ -173,6 +185,8 @@ export class WorkspaceFlatFieldMetadataMapCacheService extends WorkspaceCachePro
|
||||
fieldMetadataEntity.id,
|
||||
) || [],
|
||||
viewSorts: viewSortsByFieldId.get(fieldMetadataEntity.id) || [],
|
||||
searchFieldMetadatas:
|
||||
searchFieldMetadatasByFieldId.get(fieldMetadataEntity.id) || [],
|
||||
},
|
||||
fieldMetadataIdToUniversalIdentifierMap,
|
||||
objectMetadataIdToUniversalIdentifierMap,
|
||||
|
||||
+1
-30
@@ -1,29 +1,23 @@
|
||||
import { type FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { recomputeSearchVectorFieldFromSearchFieldMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/recompute-search-vector-field-from-search-field-metadatas.util';
|
||||
import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type';
|
||||
import { type UniversalFlatSearchFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-search-field-metadata.type';
|
||||
|
||||
export const computeSearchFieldMetadataDeletionForDeletedFields = ({
|
||||
flatFieldMetadatasToDelete,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatSearchFieldMetadataMaps,
|
||||
}: {
|
||||
flatFieldMetadatasToDelete: FlatFieldMetadata[];
|
||||
} & Pick<
|
||||
AllFlatEntityMaps,
|
||||
| 'flatObjectMetadataMaps'
|
||||
| 'flatFieldMetadataMaps'
|
||||
| 'flatSearchFieldMetadataMaps'
|
||||
'flatObjectMetadataMaps' | 'flatSearchFieldMetadataMaps'
|
||||
>): {
|
||||
searchFieldMetadatasToDelete: UniversalFlatSearchFieldMetadata[];
|
||||
flatSearchVectorFieldsToUpdate: FlatFieldMetadata<FieldMetadataType.TS_VECTOR>[];
|
||||
} => {
|
||||
const deletedFieldIds = new Set(
|
||||
flatFieldMetadatasToDelete.map((flatFieldMetadata) => flatFieldMetadata.id),
|
||||
@@ -35,8 +29,6 @@ export const computeSearchFieldMetadataDeletionForDeletedFields = ({
|
||||
);
|
||||
|
||||
const searchFieldMetadatasToDelete: UniversalFlatSearchFieldMetadata[] = [];
|
||||
const flatSearchVectorFieldsToUpdate: FlatFieldMetadata<FieldMetadataType.TS_VECTOR>[] =
|
||||
[];
|
||||
|
||||
for (const objectMetadataId of affectedObjectMetadataIds) {
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
@@ -59,31 +51,10 @@ export const computeSearchFieldMetadataDeletionForDeletedFields = ({
|
||||
deletedFieldIds.has(searchFieldMetadata.fieldMetadataId),
|
||||
);
|
||||
|
||||
if (rowsToDelete.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
searchFieldMetadatasToDelete.push(...rowsToDelete);
|
||||
|
||||
const remainingSearchFieldMetadatas = objectSearchFieldMetadatas.filter(
|
||||
(searchFieldMetadata) =>
|
||||
!deletedFieldIds.has(searchFieldMetadata.fieldMetadataId),
|
||||
);
|
||||
|
||||
const flatSearchVectorFieldToUpdate =
|
||||
recomputeSearchVectorFieldFromSearchFieldMetadatas({
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
searchFieldMetadatas: remainingSearchFieldMetadatas,
|
||||
});
|
||||
|
||||
if (isDefined(flatSearchVectorFieldToUpdate)) {
|
||||
flatSearchVectorFieldsToUpdate.push(flatSearchVectorFieldToUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
searchFieldMetadatasToDelete,
|
||||
flatSearchVectorFieldsToUpdate,
|
||||
};
|
||||
};
|
||||
|
||||
+1
-6
@@ -1,4 +1,3 @@
|
||||
import { type FieldMetadataType } from 'twenty-shared/types';
|
||||
import {
|
||||
isDefined,
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
|
||||
@@ -13,7 +12,6 @@ import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-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 { findManyFlatEntityByUniversalIdentifierInUniversalFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-universal-identifier-in-universal-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { computeFlatFieldMetadataRelatedFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/compute-flat-field-metadata-related-flat-field-metadata.util';
|
||||
import { computeSearchFieldMetadataDeletionForDeletedFields } from 'src/engine/metadata-modules/flat-field-metadata/utils/compute-search-field-metadata-deletion-for-deleted-fields.util';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
@@ -44,7 +42,6 @@ export const fromDeleteFieldInputToFlatFieldMetadatasToDelete = ({
|
||||
flatIndexesToUpdate: UniversalFlatIndexMetadata[];
|
||||
flatIndexesToDelete: UniversalFlatIndexMetadata[];
|
||||
searchFieldMetadatasToDelete: UniversalFlatSearchFieldMetadata[];
|
||||
flatSearchVectorFieldsToUpdate: FlatFieldMetadata<FieldMetadataType.TS_VECTOR>[];
|
||||
} => {
|
||||
const { id: fieldMetadataToDeleteId } =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
@@ -187,11 +184,10 @@ export const fromDeleteFieldInputToFlatFieldMetadatasToDelete = ({
|
||||
},
|
||||
);
|
||||
|
||||
const { searchFieldMetadatasToDelete, flatSearchVectorFieldsToUpdate } =
|
||||
const { searchFieldMetadatasToDelete } =
|
||||
computeSearchFieldMetadataDeletionForDeletedFields({
|
||||
flatFieldMetadatasToDelete,
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
|
||||
flatSearchFieldMetadataMaps: existingFlatSearchFieldMetadataMaps,
|
||||
});
|
||||
|
||||
@@ -200,6 +196,5 @@ export const fromDeleteFieldInputToFlatFieldMetadatasToDelete = ({
|
||||
flatIndexesToDelete,
|
||||
flatIndexesToUpdate,
|
||||
searchFieldMetadatasToDelete,
|
||||
flatSearchVectorFieldsToUpdate,
|
||||
};
|
||||
};
|
||||
|
||||
+6
@@ -78,6 +78,12 @@ export const fromFieldMetadataEntityToFlatFieldMetadata = (
|
||||
fieldMetadataEntity.viewSorts?.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
) ?? [],
|
||||
searchFieldMetadataIds:
|
||||
fieldMetadataEntity.searchFieldMetadatas?.map(({ id }) => id) ?? [],
|
||||
searchFieldMetadataUniversalIdentifiers:
|
||||
fieldMetadataEntity.searchFieldMetadatas?.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
) ?? [],
|
||||
fieldPermissionUniversalIdentifiers:
|
||||
fieldMetadataEntity.fieldPermissions?.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
|
||||
-5
@@ -35,7 +35,6 @@ type FromUpdateFieldInputToFlatFieldMetadataArgs = {
|
||||
| 'flatViewGroupMaps'
|
||||
| 'flatViewMaps'
|
||||
| 'flatViewFieldMaps'
|
||||
| 'flatSearchFieldMetadataMaps'
|
||||
>;
|
||||
|
||||
type FlatFieldMetadataAndIndexToUpdate = {
|
||||
@@ -52,7 +51,6 @@ export const fromUpdateFieldInputToFlatFieldMetadata = ({
|
||||
flatViewGroupMaps,
|
||||
flatViewMaps,
|
||||
flatViewFieldMaps,
|
||||
flatSearchFieldMetadataMaps,
|
||||
isSystemBuild,
|
||||
}: FromUpdateFieldInputToFlatFieldMetadataArgs): FieldInputTranspilationResult<FlatFieldMetadataAndIndexToUpdate> => {
|
||||
const updateFieldInputInformalProperties =
|
||||
@@ -152,7 +150,6 @@ export const fromUpdateFieldInputToFlatFieldMetadata = ({
|
||||
toFlatFieldMetadata,
|
||||
flatViewMaps,
|
||||
flatViewFieldMaps,
|
||||
flatSearchFieldMetadataMaps,
|
||||
flatApplication,
|
||||
});
|
||||
|
||||
@@ -175,14 +172,12 @@ export const fromUpdateFieldInputToFlatFieldMetadata = ({
|
||||
flatViewsToDelete,
|
||||
flatViewFieldsToDelete,
|
||||
flatViewsToUpdate,
|
||||
flatFieldMetadatasToUpdate: flatFieldMetadatasToUpdateFromSideEffect,
|
||||
} = sideEffectResult.result;
|
||||
|
||||
return {
|
||||
flatFieldMetadatasToUpdate: [
|
||||
...accumulator.flatFieldMetadatasToUpdate,
|
||||
toFlatFieldMetadata,
|
||||
...flatFieldMetadatasToUpdateFromSideEffect,
|
||||
],
|
||||
flatIndexMetadatasToUpdate: [
|
||||
...accumulator.flatIndexMetadatasToUpdate,
|
||||
|
||||
+1
@@ -77,5 +77,6 @@ export const getDefaultFlatFieldMetadata = ({
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
universalSettings: settings ?? null,
|
||||
viewSortUniversalIdentifiers: [],
|
||||
searchFieldMetadataUniversalIdentifiers: [],
|
||||
} as const satisfies UniversalFlatFieldMetadata;
|
||||
};
|
||||
|
||||
+1
-28
@@ -1,9 +1,7 @@
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
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 { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FieldInputTranspilationResult } from 'src/engine/metadata-modules/flat-field-metadata/types/field-input-transpilation-result.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { handleEnumFlatFieldMetadataUpdateSideEffects } from 'src/engine/metadata-modules/flat-field-metadata/utils/handle-enum-flat-field-metadata-update-side-effects.util';
|
||||
@@ -16,7 +14,6 @@ import {
|
||||
handleIndexChangesDuringFieldUpdate,
|
||||
} from 'src/engine/metadata-modules/flat-field-metadata/utils/handle-index-changes-during-field-update.util';
|
||||
import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-flat-field-metadata.util';
|
||||
import { recomputeSearchVectorOnFieldRename } from 'src/engine/metadata-modules/flat-field-metadata/utils/recompute-search-vector-on-field-rename.util';
|
||||
import { type FlatViewFiltersToDeleteAndUpdate } from 'src/engine/metadata-modules/flat-field-metadata/utils/recompute-view-filters-on-flat-field-metadata-options-update.util';
|
||||
import { type FlatViewGroupsToDeleteUpdateAndCreate } from 'src/engine/metadata-modules/flat-field-metadata/utils/recompute-view-groups-on-flat-field-metadata-options-update.util';
|
||||
|
||||
@@ -24,9 +21,7 @@ export type FlatFieldMetadataUpdateSideEffects =
|
||||
FlatViewFiltersToDeleteAndUpdate &
|
||||
FlatViewGroupsToDeleteUpdateAndCreate &
|
||||
FieldMetadataUpdateIndexSideEffect &
|
||||
FieldMetadataDeactivationSideEffect & {
|
||||
flatFieldMetadatasToUpdate: FlatFieldMetadata[];
|
||||
};
|
||||
FieldMetadataDeactivationSideEffect;
|
||||
|
||||
type HandleFlatFieldMetadataUpdateSideEffectArgs = FromTo<
|
||||
FlatFieldMetadata,
|
||||
@@ -41,7 +36,6 @@ type HandleFlatFieldMetadataUpdateSideEffectArgs = FromTo<
|
||||
| 'flatViewGroupMaps'
|
||||
| 'flatViewMaps'
|
||||
| 'flatViewFieldMaps'
|
||||
| 'flatSearchFieldMetadataMaps'
|
||||
> & {
|
||||
flatApplication: FlatApplication;
|
||||
};
|
||||
@@ -59,7 +53,6 @@ export const FLAT_FIELD_METADATA_UPDATE_EMPTY_SIDE_EFFECTS: FlatFieldMetadataUpd
|
||||
flatViewsToDelete: [],
|
||||
flatViewFieldsToDelete: [],
|
||||
flatViewsToUpdate: [],
|
||||
flatFieldMetadatasToUpdate: [],
|
||||
};
|
||||
|
||||
export const handleFlatFieldMetadataUpdateSideEffect = ({
|
||||
@@ -72,7 +65,6 @@ export const handleFlatFieldMetadataUpdateSideEffect = ({
|
||||
flatViewGroupMaps,
|
||||
flatViewMaps,
|
||||
flatViewFieldMaps,
|
||||
flatSearchFieldMetadataMaps,
|
||||
flatApplication,
|
||||
}: HandleFlatFieldMetadataUpdateSideEffectArgs): FieldInputTranspilationResult<FlatFieldMetadataUpdateSideEffects> => {
|
||||
const sideEffectResult = structuredClone(
|
||||
@@ -140,25 +132,6 @@ export const handleFlatFieldMetadataUpdateSideEffect = ({
|
||||
return indexChangesSideEffectResult;
|
||||
}
|
||||
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
flatEntityId: fromFlatFieldMetadata.objectMetadataId,
|
||||
});
|
||||
|
||||
const { flatSearchVectorFieldToUpdate } = recomputeSearchVectorOnFieldRename({
|
||||
fromFlatFieldMetadata,
|
||||
toFlatFieldMetadata,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
flatSearchFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (isDefined(flatSearchVectorFieldToUpdate)) {
|
||||
sideEffectResult.flatFieldMetadatasToUpdate.push(
|
||||
flatSearchVectorFieldToUpdate,
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
flatIndexMetadatasToUpdate,
|
||||
flatIndexMetadatasToCreate,
|
||||
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
import { type FieldMetadataType, type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { recomputeSearchVectorFieldFromSearchFieldMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/recompute-search-vector-field-from-search-field-metadatas.util';
|
||||
import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type';
|
||||
|
||||
export type FieldMetadataUpdateSearchVectorSideEffect = {
|
||||
flatSearchVectorFieldToUpdate:
|
||||
| FlatFieldMetadata<FieldMetadataType.TS_VECTOR>
|
||||
| undefined;
|
||||
};
|
||||
|
||||
type RecomputeSearchVectorOnFieldRenameArgs = {
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
} & FromTo<FlatFieldMetadata, 'flatFieldMetadata'> &
|
||||
Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatFieldMetadataMaps' | 'flatSearchFieldMetadataMaps'
|
||||
>;
|
||||
|
||||
const EMPTY_SIDE_EFFECT: FieldMetadataUpdateSearchVectorSideEffect = {
|
||||
flatSearchVectorFieldToUpdate: undefined,
|
||||
};
|
||||
|
||||
export const recomputeSearchVectorOnFieldRename = ({
|
||||
fromFlatFieldMetadata,
|
||||
toFlatFieldMetadata,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
flatSearchFieldMetadataMaps,
|
||||
}: RecomputeSearchVectorOnFieldRenameArgs): FieldMetadataUpdateSearchVectorSideEffect => {
|
||||
if (!flatObjectMetadata.isSearchable) {
|
||||
return EMPTY_SIDE_EFFECT;
|
||||
}
|
||||
|
||||
const objectSearchFieldMetadatas =
|
||||
findManyFlatEntityByIdInFlatEntityMapsOrThrow<FlatSearchFieldMetadata>({
|
||||
flatEntityMaps: flatSearchFieldMetadataMaps,
|
||||
flatEntityIds: flatObjectMetadata.searchFieldMetadataIds,
|
||||
});
|
||||
|
||||
const isIndexedField = objectSearchFieldMetadatas.some(
|
||||
(searchFieldMetadata) =>
|
||||
searchFieldMetadata.fieldMetadataId === toFlatFieldMetadata.id,
|
||||
);
|
||||
|
||||
const hasNameChanged =
|
||||
fromFlatFieldMetadata.name !== toFlatFieldMetadata.name;
|
||||
|
||||
if (!isIndexedField || !hasNameChanged) {
|
||||
return EMPTY_SIDE_EFFECT;
|
||||
}
|
||||
|
||||
return {
|
||||
flatSearchVectorFieldToUpdate:
|
||||
recomputeSearchVectorFieldFromSearchFieldMetadatas({
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
searchFieldMetadatas: objectSearchFieldMetadatas,
|
||||
overrideFlatFieldMetadataById: new Map([
|
||||
[toFlatFieldMetadata.id, toFlatFieldMetadata],
|
||||
]),
|
||||
}),
|
||||
};
|
||||
};
|
||||
-19
@@ -1,11 +1,9 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { type FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { FieldMetadataExceptionCode } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { type FlatFieldMetadataTypeValidationArgs } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-type-validator.type';
|
||||
import { type FlatFieldMetadataValidationError } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-validation-error.type';
|
||||
import { isSafeTsVectorExpression } from 'src/engine/workspace-manager/workspace-migration/utils/remove-sql-injection.util';
|
||||
|
||||
export const validateTsVectorFlatFieldMetadata = ({
|
||||
flatEntityToValidate,
|
||||
@@ -30,22 +28,5 @@ export const validateTsVectorFlatFieldMetadata = ({
|
||||
});
|
||||
}
|
||||
|
||||
const asExpression = flatEntityToValidate.universalSettings?.asExpression;
|
||||
|
||||
if (!isNonEmptyString(asExpression)) {
|
||||
errors.push({
|
||||
code: FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
||||
message:
|
||||
'Field type TS_VECTOR must have an expression. This may have failed to be built because record identifier field does not exist or is not of a searchable type.',
|
||||
userFriendlyMessage: msg`Field type TS_VECTOR must have an expression. This may have failed to be built because record identifier field does not exist or is not of a searchable type.`,
|
||||
});
|
||||
} else if (!isSafeTsVectorExpression(asExpression)) {
|
||||
errors.push({
|
||||
code: FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
||||
message: 'Field type TS_VECTOR expression is invalid',
|
||||
userFriendlyMessage: msg`The search field expression is invalid.`,
|
||||
});
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
|
||||
+19
-29
@@ -1,24 +1,20 @@
|
||||
import { type FieldMetadataType, type FromTo } from 'twenty-shared/types';
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
import { isDefined, isSearchableFieldType } from 'twenty-shared/utils';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { recomputeSearchVectorFieldFromSearchFieldMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/recompute-search-vector-field-from-search-field-metadatas.util';
|
||||
import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type';
|
||||
import { buildFlatSearchFieldMetadataForField } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/build-flat-search-field-metadata-for-field.util';
|
||||
import { findTsVectorFlatFieldMetadataForObject } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/find-ts-vector-flat-field-metadata-for-object.util';
|
||||
import { type UniversalFlatSearchFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-search-field-metadata.type';
|
||||
|
||||
export type LabelIdentifierUpdateSearchVectorSideEffect = {
|
||||
flatSearchVectorFieldToUpdate:
|
||||
| FlatFieldMetadata<FieldMetadataType.TS_VECTOR>
|
||||
| undefined;
|
||||
export type LabelIdentifierUpdateSearchFieldMetadataSideEffect = {
|
||||
searchFieldMetadatasToCreate: UniversalFlatSearchFieldMetadata[];
|
||||
};
|
||||
|
||||
type RecomputeSearchVectorOnLabelIdentifierUpdateArgs = FromTo<
|
||||
type ComputeSearchFieldMetadataToCreateOnLabelIdentifierUpdateArgs = FromTo<
|
||||
FlatObjectMetadata,
|
||||
'flatObjectMetadata'
|
||||
> &
|
||||
@@ -27,21 +23,20 @@ type RecomputeSearchVectorOnLabelIdentifierUpdateArgs = FromTo<
|
||||
'flatFieldMetadataMaps' | 'flatSearchFieldMetadataMaps'
|
||||
>;
|
||||
|
||||
const EMPTY_SIDE_EFFECT: LabelIdentifierUpdateSearchVectorSideEffect = {
|
||||
flatSearchVectorFieldToUpdate: undefined,
|
||||
const EMPTY_SIDE_EFFECT: LabelIdentifierUpdateSearchFieldMetadataSideEffect = {
|
||||
searchFieldMetadatasToCreate: [],
|
||||
};
|
||||
|
||||
// Relabeling is additive: it indexes the new label identifier field while preserving the
|
||||
// existing search surface (e.g. the provisioned name row). We never drop a row here so the
|
||||
// previous label identifier stays searchable. Removal of fields from the search surface is
|
||||
// deferred to a follow-up PR.
|
||||
export const recomputeSearchVectorOnLabelIdentifierUpdate = ({
|
||||
// previous label identifier stays searchable. The searchVector column itself is rebuilt
|
||||
// from the resulting rows by the rebuildSearchVector marker in the migration runner.
|
||||
export const computeSearchFieldMetadataToCreateOnLabelIdentifierUpdate = ({
|
||||
fromFlatObjectMetadata,
|
||||
toFlatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
flatSearchFieldMetadataMaps,
|
||||
}: RecomputeSearchVectorOnLabelIdentifierUpdateArgs): LabelIdentifierUpdateSearchVectorSideEffect => {
|
||||
}: ComputeSearchFieldMetadataToCreateOnLabelIdentifierUpdateArgs): LabelIdentifierUpdateSearchFieldMetadataSideEffect => {
|
||||
const newLabelIdentifierFieldMetadataId =
|
||||
toFlatObjectMetadata.labelIdentifierFieldMetadataId;
|
||||
|
||||
@@ -79,6 +74,15 @@ export const recomputeSearchVectorOnLabelIdentifierUpdate = ({
|
||||
return EMPTY_SIDE_EFFECT;
|
||||
}
|
||||
|
||||
const tsVectorFlatFieldMetadata = findTsVectorFlatFieldMetadataForObject({
|
||||
fieldUniversalIdentifiers: fromFlatObjectMetadata.fieldUniversalIdentifiers,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(tsVectorFlatFieldMetadata)) {
|
||||
return EMPTY_SIDE_EFFECT;
|
||||
}
|
||||
|
||||
const newLabelIdentifierPosition =
|
||||
existingSearchFieldMetadatas.reduce(
|
||||
(maxPosition, searchFieldMetadata) =>
|
||||
@@ -89,25 +93,11 @@ export const recomputeSearchVectorOnLabelIdentifierUpdate = ({
|
||||
const newSearchFieldMetadata = buildFlatSearchFieldMetadataForField({
|
||||
flatObjectMetadata: fromFlatObjectMetadata,
|
||||
flatFieldMetadata: newLabelIdentifierField,
|
||||
tsVectorFlatFieldMetadata,
|
||||
position: newLabelIdentifierPosition,
|
||||
});
|
||||
|
||||
const nextSearchFieldMetadatas = [
|
||||
...existingSearchFieldMetadatas,
|
||||
{
|
||||
fieldMetadataId: newLabelIdentifierFieldMetadataId,
|
||||
position: newLabelIdentifierPosition,
|
||||
universalIdentifier: newSearchFieldMetadata.universalIdentifier,
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
flatSearchVectorFieldToUpdate:
|
||||
recomputeSearchVectorFieldFromSearchFieldMetadatas({
|
||||
flatObjectMetadata: fromFlatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
searchFieldMetadatas: nextSearchFieldMetadatas,
|
||||
}),
|
||||
searchFieldMetadatasToCreate: [newSearchFieldMetadata],
|
||||
};
|
||||
};
|
||||
-2
@@ -102,7 +102,6 @@ export const fromUpdateObjectInputToFlatObjectMetadataAndRelatedFlatEntities =
|
||||
flatViewFieldsToCreate,
|
||||
flatViewFieldsToUpdate,
|
||||
otherObjectFlatFieldMetadatasToUpdate,
|
||||
sameObjectFlatFieldMetadatasToUpdate,
|
||||
searchFieldMetadatasToCreate,
|
||||
} = handleFlatObjectMetadataUpdateSideEffect({
|
||||
fromFlatObjectMetadata: existingFlatObjectMetadata,
|
||||
@@ -121,7 +120,6 @@ export const fromUpdateObjectInputToFlatObjectMetadataAndRelatedFlatEntities =
|
||||
flatViewFieldsToCreate,
|
||||
flatViewFieldsToUpdate,
|
||||
otherObjectFlatFieldMetadatasToUpdate,
|
||||
sameObjectFlatFieldMetadatasToUpdate,
|
||||
searchFieldMetadatasToCreate,
|
||||
};
|
||||
};
|
||||
|
||||
+3
-12
@@ -2,10 +2,9 @@ import { type FromTo } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { computeSearchFieldMetadataToCreateOnLabelIdentifierUpdate } from 'src/engine/metadata-modules/flat-object-metadata/utils/compute-search-field-metadata-to-create-on-label-identifier-update.util';
|
||||
import { recomputeIndexAfterFlatObjectMetadataSingularNameUpdate } from 'src/engine/metadata-modules/flat-object-metadata/utils/recompute-index-after-flat-object-metadata-singular-name-update.util';
|
||||
import { recomputeSearchVectorOnLabelIdentifierUpdate } from 'src/engine/metadata-modules/flat-object-metadata/utils/recompute-search-vector-on-label-identifier-update.util';
|
||||
import { recomputeViewFieldIdentifierAfterFlatObjectIdentifierUpdate } from 'src/engine/metadata-modules/flat-object-metadata/utils/recompute-view-field-identifier-after-flat-object-identifier-update.util';
|
||||
import { renameRelatedMorphFieldOnObjectNamesUpdate } from 'src/engine/metadata-modules/flat-object-metadata/utils/rename-related-morph-field-on-object-names-update.util';
|
||||
import { type UniversalFlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-field-metadata.type';
|
||||
@@ -15,7 +14,6 @@ import { type UniversalFlatViewField } from 'src/engine/workspace-manager/worksp
|
||||
|
||||
export type FlatObjectMetadataUpdateSideEffects = {
|
||||
otherObjectFlatFieldMetadatasToUpdate: UniversalFlatFieldMetadata[];
|
||||
sameObjectFlatFieldMetadatasToUpdate: UniversalFlatFieldMetadata[];
|
||||
flatViewFieldsToUpdate: UniversalFlatViewField[];
|
||||
flatViewFieldsToCreate: UniversalFlatViewField[];
|
||||
flatIndexMetadatasToUpdate: UniversalFlatIndexMetadata[];
|
||||
@@ -89,20 +87,14 @@ export const handleFlatObjectMetadataUpdateSideEffect = ({
|
||||
flatViewFieldsToUpdate: [],
|
||||
};
|
||||
|
||||
const sameObjectFlatFieldMetadatasToUpdate: FlatFieldMetadata[] = [];
|
||||
|
||||
const { flatSearchVectorFieldToUpdate, searchFieldMetadatasToCreate } =
|
||||
recomputeSearchVectorOnLabelIdentifierUpdate({
|
||||
const { searchFieldMetadatasToCreate } =
|
||||
computeSearchFieldMetadataToCreateOnLabelIdentifierUpdate({
|
||||
fromFlatObjectMetadata,
|
||||
toFlatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
flatSearchFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (isDefined(flatSearchVectorFieldToUpdate)) {
|
||||
sameObjectFlatFieldMetadatasToUpdate.push(flatSearchVectorFieldToUpdate);
|
||||
}
|
||||
|
||||
return {
|
||||
flatIndexMetadatasToUpdate: [
|
||||
...morphRelatedFlatIndexesToUpdate,
|
||||
@@ -111,7 +103,6 @@ export const handleFlatObjectMetadataUpdateSideEffect = ({
|
||||
flatViewFieldsToCreate,
|
||||
flatViewFieldsToUpdate,
|
||||
otherObjectFlatFieldMetadatasToUpdate: morphFlatFieldMetadatasToUpdate,
|
||||
sameObjectFlatFieldMetadatasToUpdate,
|
||||
searchFieldMetadatasToCreate,
|
||||
};
|
||||
};
|
||||
|
||||
-112
@@ -1,112 +0,0 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type';
|
||||
import {
|
||||
ObjectMetadataException,
|
||||
ObjectMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
import {
|
||||
buildSearchVectorTargetField,
|
||||
computeSearchVectorAsExpressionFromSearchFieldMetadatas,
|
||||
} from 'src/engine/metadata-modules/flat-search-field-metadata/utils/compute-search-vector-as-expression-from-search-field-metadatas.util';
|
||||
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants';
|
||||
|
||||
type RecomputeSearchVectorFieldFromSearchFieldMetadatasArgs = {
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
// Post-change searchFieldMetadata rows for the object, carrying the field id and the
|
||||
// per-object position that drives the deterministic asExpression order.
|
||||
searchFieldMetadatas: Pick<
|
||||
FlatSearchFieldMetadata,
|
||||
'fieldMetadataId' | 'position' | 'universalIdentifier'
|
||||
>[];
|
||||
// Takes precedence over the flat maps when resolving an id — for a field created or
|
||||
// renamed in the same migration, whose maps entry is absent or stale.
|
||||
overrideFlatFieldMetadataById?: Map<string, FlatFieldMetadata>;
|
||||
} & Pick<AllFlatEntityMaps, 'flatFieldMetadataMaps'>;
|
||||
|
||||
export const recomputeSearchVectorFieldFromSearchFieldMetadatas = ({
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
searchFieldMetadatas,
|
||||
overrideFlatFieldMetadataById,
|
||||
}: RecomputeSearchVectorFieldFromSearchFieldMetadatasArgs):
|
||||
| FlatFieldMetadata<FieldMetadataType.TS_VECTOR>
|
||||
| undefined => {
|
||||
const objectFlatFieldMetadatas =
|
||||
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
flatEntityIds: flatObjectMetadata.fieldIds,
|
||||
});
|
||||
|
||||
const searchVectorField = objectFlatFieldMetadatas.find(
|
||||
(field) => field.name === SEARCH_VECTOR_FIELD.name,
|
||||
);
|
||||
|
||||
if (
|
||||
!isDefined(searchVectorField) ||
|
||||
!isFlatFieldMetadataOfType(searchVectorField, FieldMetadataType.TS_VECTOR)
|
||||
) {
|
||||
throw new ObjectMetadataException(
|
||||
`Search vector field not found for object metadata ${flatObjectMetadata.id}`,
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
);
|
||||
}
|
||||
|
||||
const targetSearchableFields = searchFieldMetadatas.map(
|
||||
(searchFieldMetadata) => {
|
||||
const { fieldMetadataId, position, universalIdentifier } =
|
||||
searchFieldMetadata;
|
||||
|
||||
const overriddenFlatFieldMetadata =
|
||||
overrideFlatFieldMetadataById?.get(fieldMetadataId);
|
||||
|
||||
const flatFieldMetadata =
|
||||
overriddenFlatFieldMetadata ??
|
||||
findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: fieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(flatFieldMetadata)) {
|
||||
throw new ObjectMetadataException(
|
||||
`Field metadata ${fieldMetadataId} referenced by searchFieldMetadata not found for object metadata ${flatObjectMetadata.id}`,
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
);
|
||||
}
|
||||
|
||||
return buildSearchVectorTargetField(
|
||||
flatFieldMetadata,
|
||||
position,
|
||||
universalIdentifier,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
try {
|
||||
const newAsExpression =
|
||||
computeSearchVectorAsExpressionFromSearchFieldMetadatas(
|
||||
targetSearchableFields,
|
||||
);
|
||||
|
||||
return {
|
||||
...searchVectorField,
|
||||
universalSettings: {
|
||||
...searchVectorField.universalSettings,
|
||||
asExpression: newAsExpression,
|
||||
generatedType: 'STORED',
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
throw new ObjectMetadataException(
|
||||
`Failed to compute search vector column expression for object metadata ${flatObjectMetadata.id}: ${error instanceof Error ? error.message : String(error)}`,
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
);
|
||||
}
|
||||
};
|
||||
+41
-2
@@ -1,6 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { WorkspaceCacheProvider } from 'src/engine/workspace-cache/interfaces/workspace-cache-provider.service';
|
||||
@@ -11,6 +13,7 @@ import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-enti
|
||||
import { FlatSearchFieldMetadataMaps } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata-maps.type';
|
||||
import { fromSearchFieldMetadataEntityToFlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/utils/from-search-field-metadata-entity-to-flat-search-field-metadata.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants';
|
||||
import { SearchFieldMetadataEntity } from 'src/engine/metadata-modules/search-field-metadata/search-field-metadata.entity';
|
||||
import { InjectWorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/inject-workspace-scoped-repository.decorator';
|
||||
import { WorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/workspace-scoped-repository';
|
||||
@@ -56,7 +59,13 @@ export class WorkspaceFlatSearchFieldMetadataMapCacheService extends WorkspaceCa
|
||||
}),
|
||||
this.fieldMetadataRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'universalIdentifier'],
|
||||
select: [
|
||||
'id',
|
||||
'universalIdentifier',
|
||||
'name',
|
||||
'type',
|
||||
'objectMetadataId',
|
||||
],
|
||||
withDeleted: true,
|
||||
}),
|
||||
]);
|
||||
@@ -68,12 +77,42 @@ export class WorkspaceFlatSearchFieldMetadataMapCacheService extends WorkspaceCa
|
||||
const fieldMetadataIdToUniversalIdentifierMap =
|
||||
createIdToUniversalIdentifierMap(fieldMetadatas);
|
||||
|
||||
// Each object's single system TS_VECTOR field, used to backfill the FK for
|
||||
// legacy searchFieldMetadata rows still NULL before the 2.18 slow command runs.
|
||||
// TODO: remove this fallback (and the searchVector map) once the minimum
|
||||
// cross-upgrade supported version is past 2.18 — at which point no instance can
|
||||
// still have NULL tsVectorFieldMetadataId rows.
|
||||
const searchVectorFieldIdByObjectMetadataId = new Map<string, string>();
|
||||
|
||||
for (const fieldMetadata of fieldMetadatas) {
|
||||
if (
|
||||
fieldMetadata.type === FieldMetadataType.TS_VECTOR &&
|
||||
fieldMetadata.name === SEARCH_VECTOR_FIELD.name
|
||||
) {
|
||||
searchVectorFieldIdByObjectMetadataId.set(
|
||||
fieldMetadata.objectMetadataId,
|
||||
fieldMetadata.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const flatSearchFieldMetadataMaps = createEmptyFlatEntityMaps();
|
||||
|
||||
for (const searchFieldMetadata of existingSearchFieldMetadatas) {
|
||||
const resolvedTsVectorFieldMetadataId =
|
||||
(searchFieldMetadata.tsVectorFieldMetadataId as string | null) ??
|
||||
searchVectorFieldIdByObjectMetadataId.get(
|
||||
searchFieldMetadata.objectMetadataId,
|
||||
);
|
||||
|
||||
const flatSearchFieldMetadata =
|
||||
fromSearchFieldMetadataEntityToFlatSearchFieldMetadata({
|
||||
entity: searchFieldMetadata,
|
||||
entity: isDefined(resolvedTsVectorFieldMetadataId)
|
||||
? {
|
||||
...searchFieldMetadata,
|
||||
tsVectorFieldMetadataId: resolvedTsVectorFieldMetadataId,
|
||||
}
|
||||
: searchFieldMetadata,
|
||||
applicationIdToUniversalIdentifierMap,
|
||||
objectMetadataIdToUniversalIdentifierMap,
|
||||
fieldMetadataIdToUniversalIdentifierMap,
|
||||
|
||||
+4
@@ -6,6 +6,7 @@ import { type UniversalFlatSearchFieldMetadata } from 'src/engine/workspace-mana
|
||||
export const buildFlatSearchFieldMetadataForField = ({
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadata,
|
||||
tsVectorFlatFieldMetadata,
|
||||
position,
|
||||
}: {
|
||||
flatObjectMetadata: Pick<
|
||||
@@ -13,6 +14,7 @@ export const buildFlatSearchFieldMetadataForField = ({
|
||||
'applicationUniversalIdentifier' | 'universalIdentifier'
|
||||
>;
|
||||
flatFieldMetadata: { universalIdentifier: string };
|
||||
tsVectorFlatFieldMetadata: { universalIdentifier: string };
|
||||
position: number;
|
||||
}): UniversalFlatSearchFieldMetadata => {
|
||||
const createdAt = new Date().toISOString();
|
||||
@@ -26,5 +28,7 @@ export const buildFlatSearchFieldMetadataForField = ({
|
||||
flatObjectMetadata.applicationUniversalIdentifier,
|
||||
objectMetadataUniversalIdentifier: flatObjectMetadata.universalIdentifier,
|
||||
fieldMetadataUniversalIdentifier: flatFieldMetadata.universalIdentifier,
|
||||
tsVectorFieldMetadataUniversalIdentifier:
|
||||
tsVectorFlatFieldMetadata.universalIdentifier,
|
||||
};
|
||||
};
|
||||
|
||||
+9
-5
@@ -15,11 +15,15 @@ export type SearchVectorTargetField = {
|
||||
sortKey: string;
|
||||
};
|
||||
|
||||
export const buildSearchVectorTargetField = (
|
||||
field: { name: string; type: FieldMetadataType },
|
||||
position: number,
|
||||
sortKey: string,
|
||||
): SearchVectorTargetField => ({
|
||||
export const buildSearchVectorTargetField = ({
|
||||
field,
|
||||
position,
|
||||
sortKey,
|
||||
}: {
|
||||
field: { name: string; type: FieldMetadataType };
|
||||
position: number;
|
||||
sortKey: string;
|
||||
}): SearchVectorTargetField => ({
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
position,
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import { type FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import {
|
||||
buildSearchVectorTargetField,
|
||||
computeSearchVectorAsExpressionFromSearchFieldMetadatas,
|
||||
} from 'src/engine/metadata-modules/flat-search-field-metadata/utils/compute-search-vector-as-expression-from-search-field-metadatas.util';
|
||||
import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type';
|
||||
|
||||
export const deriveSearchVectorAsExpressionForTsVectorField = ({
|
||||
tsVectorFieldMetadataId,
|
||||
flatSearchFieldMetadataMaps,
|
||||
indexedFieldById,
|
||||
}: {
|
||||
tsVectorFieldMetadataId: string;
|
||||
flatSearchFieldMetadataMaps: FlatEntityMaps<FlatSearchFieldMetadata>;
|
||||
indexedFieldById: ReadonlyMap<
|
||||
string,
|
||||
{ name: string; type: FieldMetadataType }
|
||||
>;
|
||||
}): string => {
|
||||
const targetSearchableFields = Object.values(
|
||||
flatSearchFieldMetadataMaps.byUniversalIdentifier,
|
||||
)
|
||||
.filter(isDefined)
|
||||
.filter(
|
||||
(flatSearchFieldMetadata) =>
|
||||
flatSearchFieldMetadata.tsVectorFieldMetadataId ===
|
||||
tsVectorFieldMetadataId,
|
||||
)
|
||||
.flatMap((flatSearchFieldMetadata) => {
|
||||
const indexedField = indexedFieldById.get(
|
||||
flatSearchFieldMetadata.fieldMetadataId,
|
||||
);
|
||||
|
||||
if (!isDefined(indexedField)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
buildSearchVectorTargetField({
|
||||
field: indexedField,
|
||||
position: flatSearchFieldMetadata.position,
|
||||
sortKey: flatSearchFieldMetadata.universalIdentifier,
|
||||
}),
|
||||
];
|
||||
});
|
||||
|
||||
return computeSearchVectorAsExpressionFromSearchFieldMetadatas(
|
||||
targetSearchableFields,
|
||||
);
|
||||
};
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants';
|
||||
|
||||
// Resolves the object's single system TS_VECTOR field (the 'searchVector' field) that
|
||||
// every searchFieldMetadata row contributes to.
|
||||
// Could be refactored once https://github.com/twentyhq/twenty/pull/21949 has been merged.
|
||||
export const findTsVectorFlatFieldMetadataForObject = ({
|
||||
fieldUniversalIdentifiers,
|
||||
flatFieldMetadataMaps,
|
||||
}: {
|
||||
fieldUniversalIdentifiers: string[];
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
}): FlatFieldMetadata | undefined =>
|
||||
fieldUniversalIdentifiers
|
||||
.map(
|
||||
(fieldUniversalIdentifier) =>
|
||||
flatFieldMetadataMaps.byUniversalIdentifier[fieldUniversalIdentifier],
|
||||
)
|
||||
.find(
|
||||
(flatFieldMetadata) =>
|
||||
isDefined(flatFieldMetadata) &&
|
||||
flatFieldMetadata.type === FieldMetadataType.TS_VECTOR &&
|
||||
flatFieldMetadata.name === SEARCH_VECTOR_FIELD.name,
|
||||
);
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type';
|
||||
import { type SearchFieldMetadataDTO } from 'src/engine/metadata-modules/search-field-metadata/dtos/search-field-metadata.dto';
|
||||
|
||||
export const fromFlatSearchFieldMetadataToSearchFieldMetadataDto = (
|
||||
flatSearchFieldMetadata: FlatSearchFieldMetadata,
|
||||
): SearchFieldMetadataDTO => ({
|
||||
id: flatSearchFieldMetadata.id,
|
||||
objectMetadataId: flatSearchFieldMetadata.objectMetadataId,
|
||||
fieldMetadataId: flatSearchFieldMetadata.fieldMetadataId,
|
||||
tsVectorFieldMetadataId: flatSearchFieldMetadata.tsVectorFieldMetadataId,
|
||||
position: flatSearchFieldMetadata.position,
|
||||
createdAt: new Date(flatSearchFieldMetadata.createdAt),
|
||||
updatedAt: new Date(flatSearchFieldMetadata.updatedAt),
|
||||
workspaceId: flatSearchFieldMetadata.workspaceId,
|
||||
});
|
||||
+13
@@ -55,6 +55,18 @@ export const fromSearchFieldMetadataEntityToFlatSearchFieldMetadata = ({
|
||||
);
|
||||
}
|
||||
|
||||
const tsVectorFieldMetadataUniversalIdentifier =
|
||||
fieldMetadataIdToUniversalIdentifierMap.get(
|
||||
searchFieldMetadataEntity.tsVectorFieldMetadataId,
|
||||
);
|
||||
|
||||
if (!isDefined(tsVectorFieldMetadataUniversalIdentifier)) {
|
||||
throw new FlatEntityMapsException(
|
||||
`TS_VECTOR field metadata with id ${searchFieldMetadataEntity.tsVectorFieldMetadataId} not found for searchFieldMetadata ${searchFieldMetadataEntity.id}`,
|
||||
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...searchFieldMetadataEntityWithoutRelations,
|
||||
createdAt: searchFieldMetadataEntity.createdAt.toISOString(),
|
||||
@@ -64,5 +76,6 @@ export const fromSearchFieldMetadataEntityToFlatSearchFieldMetadata = ({
|
||||
applicationUniversalIdentifier,
|
||||
objectMetadataUniversalIdentifier,
|
||||
fieldMetadataUniversalIdentifier,
|
||||
tsVectorFieldMetadataUniversalIdentifier,
|
||||
};
|
||||
};
|
||||
|
||||
+8
@@ -40,6 +40,7 @@ const PARTIAL_ID_FIELD = {
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
fieldPermissionUniversalIdentifiers: [],
|
||||
viewSortUniversalIdentifiers: [],
|
||||
searchFieldMetadataUniversalIdentifiers: [],
|
||||
} as const satisfies PartialSystemFlatFieldMetadata<FieldMetadataType.UUID>;
|
||||
|
||||
const PARTIAL_CREATED_AT_FIELD = {
|
||||
@@ -69,6 +70,7 @@ const PARTIAL_CREATED_AT_FIELD = {
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
fieldPermissionUniversalIdentifiers: [],
|
||||
viewSortUniversalIdentifiers: [],
|
||||
searchFieldMetadataUniversalIdentifiers: [],
|
||||
} as const satisfies PartialSystemFlatFieldMetadata<FieldMetadataType.DATE_TIME>;
|
||||
|
||||
const PARTIAL_UPDATED_AT_FIELD = {
|
||||
@@ -98,6 +100,7 @@ const PARTIAL_UPDATED_AT_FIELD = {
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
fieldPermissionUniversalIdentifiers: [],
|
||||
viewSortUniversalIdentifiers: [],
|
||||
searchFieldMetadataUniversalIdentifiers: [],
|
||||
} as const satisfies PartialSystemFlatFieldMetadata<FieldMetadataType.DATE_TIME>;
|
||||
|
||||
const PARTIAL_DELETED_AT_FIELD = {
|
||||
@@ -127,6 +130,7 @@ const PARTIAL_DELETED_AT_FIELD = {
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
fieldPermissionUniversalIdentifiers: [],
|
||||
viewSortUniversalIdentifiers: [],
|
||||
searchFieldMetadataUniversalIdentifiers: [],
|
||||
} as const satisfies PartialSystemFlatFieldMetadata<FieldMetadataType.DATE_TIME>;
|
||||
|
||||
const PARTIAL_CREATED_BY_FIELD = {
|
||||
@@ -156,6 +160,7 @@ const PARTIAL_CREATED_BY_FIELD = {
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
fieldPermissionUniversalIdentifiers: [],
|
||||
viewSortUniversalIdentifiers: [],
|
||||
searchFieldMetadataUniversalIdentifiers: [],
|
||||
} as const satisfies PartialSystemFlatFieldMetadata<FieldMetadataType.ACTOR>;
|
||||
|
||||
const PARTIAL_UPDATED_BY_FIELD = {
|
||||
@@ -185,6 +190,7 @@ const PARTIAL_UPDATED_BY_FIELD = {
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
fieldPermissionUniversalIdentifiers: [],
|
||||
viewSortUniversalIdentifiers: [],
|
||||
searchFieldMetadataUniversalIdentifiers: [],
|
||||
} as const satisfies PartialSystemFlatFieldMetadata<FieldMetadataType.ACTOR>;
|
||||
|
||||
const PARTIAL_POSITION_FIELD = {
|
||||
@@ -214,6 +220,7 @@ const PARTIAL_POSITION_FIELD = {
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
fieldPermissionUniversalIdentifiers: [],
|
||||
viewSortUniversalIdentifiers: [],
|
||||
searchFieldMetadataUniversalIdentifiers: [],
|
||||
} as const satisfies PartialSystemFlatFieldMetadata<FieldMetadataType.POSITION>;
|
||||
|
||||
const PARTIAL_SEARCH_VECTOR_FIELD = {
|
||||
@@ -245,6 +252,7 @@ const PARTIAL_SEARCH_VECTOR_FIELD = {
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
fieldPermissionUniversalIdentifiers: [],
|
||||
viewSortUniversalIdentifiers: [],
|
||||
searchFieldMetadataUniversalIdentifiers: [],
|
||||
} as const satisfies PartialSystemFlatFieldMetadata<FieldMetadataType.TS_VECTOR>;
|
||||
|
||||
export const PARTIAL_SYSTEM_FLAT_FIELD_METADATAS = {
|
||||
|
||||
+22
@@ -30,6 +30,7 @@ import { ObjectRecordCountDTO } from 'src/engine/metadata-modules/object-metadat
|
||||
import { UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
|
||||
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
|
||||
import { ObjectRecordCountService } from 'src/engine/metadata-modules/object-metadata/object-record-count.service';
|
||||
import { SearchFieldMetadataDTO } from 'src/engine/metadata-modules/search-field-metadata/dtos/search-field-metadata.dto';
|
||||
import { objectMetadataGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/object-metadata/utils/object-metadata-graphql-api-exception-handler.util';
|
||||
import { resolveObjectMetadataStandardOverride } from 'src/engine/metadata-modules/object-metadata/utils/resolve-object-metadata-standard-override.util';
|
||||
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
|
||||
@@ -273,4 +274,25 @@ export class ObjectMetadataResolver {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@ResolveField(() => [SearchFieldMetadataDTO], { nullable: false })
|
||||
async searchFieldMetadataList(
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
@Parent() objectMetadata: ObjectMetadataDTO,
|
||||
@Context() context: { loaders: IDataloaders },
|
||||
): Promise<SearchFieldMetadataDTO[]> {
|
||||
try {
|
||||
const searchFieldMetadataItems =
|
||||
await context.loaders.searchFieldMetadataLoader.load({
|
||||
objectMetadata,
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
|
||||
return searchFieldMetadataItems;
|
||||
} catch (error) {
|
||||
objectMetadataGraphqlApiExceptionHandler(error);
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-5
@@ -107,7 +107,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
|
||||
const {
|
||||
otherObjectFlatFieldMetadatasToUpdate,
|
||||
sameObjectFlatFieldMetadatasToUpdate,
|
||||
flatObjectMetadataToUpdate,
|
||||
flatIndexMetadatasToUpdate,
|
||||
flatViewFieldsToCreate,
|
||||
@@ -171,10 +170,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
fieldMetadata: {
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: [
|
||||
...otherObjectFlatFieldMetadatasToUpdate,
|
||||
...sameObjectFlatFieldMetadatasToUpdate,
|
||||
],
|
||||
flatEntityToUpdate: [...otherObjectFlatFieldMetadatasToUpdate],
|
||||
},
|
||||
viewField: {
|
||||
flatEntityToCreate: flatViewFieldsToCreate,
|
||||
|
||||
+2
-7
@@ -2,7 +2,6 @@ import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { PARTIAL_SYSTEM_FLAT_FIELD_METADATAS } from 'src/engine/metadata-modules/object-metadata/constants/partial-system-flat-field-metadatas.constant';
|
||||
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
|
||||
import { type UniversalFlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-field-metadata.type';
|
||||
import { type UniversalFlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-object-metadata.type';
|
||||
|
||||
@@ -154,15 +153,11 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
fieldPermissionUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
viewSortUniversalIdentifiers: [],
|
||||
searchFieldMetadataUniversalIdentifiers: [],
|
||||
};
|
||||
|
||||
const searchVectorUniversalSettings: UniversalFlatFieldMetadata<FieldMetadataType.TS_VECTOR>['universalSettings'] =
|
||||
{
|
||||
asExpression: getTsVectorColumnExpressionFromFields(
|
||||
nameField ? [nameField] : [],
|
||||
),
|
||||
generatedType: 'STORED',
|
||||
};
|
||||
null;
|
||||
|
||||
return {
|
||||
fields: {
|
||||
|
||||
+2
@@ -27,6 +27,8 @@ export const buildDefaultSearchFieldMetadatasForCustomObject = ({
|
||||
const nameSearchFieldMetadata = buildFlatSearchFieldMetadataForField({
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadata: nameField,
|
||||
tsVectorFlatFieldMetadata:
|
||||
defaultFlatFieldForCustomObjectMaps.fields.searchVector,
|
||||
position: 0,
|
||||
});
|
||||
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
import { Field, HideField, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
Authorize,
|
||||
IDField,
|
||||
QueryOptions,
|
||||
} from '@ptc-org/nestjs-query-graphql';
|
||||
import { IsDateString, IsNotEmpty, IsNumber, IsUUID } from 'class-validator';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
@ObjectType('SearchField')
|
||||
@Authorize({
|
||||
// oxlint-disable-next-line typescript/no-explicit-any
|
||||
authorize: (context: any) => ({
|
||||
workspaceId: { eq: context?.req?.workspace?.id },
|
||||
}),
|
||||
})
|
||||
@QueryOptions({
|
||||
defaultResultSize: 10,
|
||||
disableSort: true,
|
||||
maxResultsSize: 1000,
|
||||
})
|
||||
export class SearchFieldMetadataDTO {
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
@IDField(() => UUIDScalarType)
|
||||
id: string;
|
||||
|
||||
objectMetadataId: string;
|
||||
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
@Field(() => UUIDScalarType)
|
||||
fieldMetadataId: string;
|
||||
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
@Field(() => UUIDScalarType)
|
||||
tsVectorFieldMetadataId: string;
|
||||
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
position: number;
|
||||
|
||||
@IsDateString()
|
||||
@Field()
|
||||
createdAt: Date;
|
||||
|
||||
@IsDateString()
|
||||
@Field()
|
||||
updatedAt: Date;
|
||||
|
||||
@HideField()
|
||||
workspaceId: string;
|
||||
}
|
||||
+2
@@ -3,4 +3,6 @@ export enum SearchFieldMetadataExceptionCode {
|
||||
INVALID_SEARCH_FIELD_METADATA_DATA = 'INVALID_SEARCH_FIELD_METADATA_DATA',
|
||||
OBJECT_METADATA_NOT_FOUND = 'OBJECT_METADATA_NOT_FOUND',
|
||||
FIELD_METADATA_NOT_FOUND = 'FIELD_METADATA_NOT_FOUND',
|
||||
TS_VECTOR_FIELD_METADATA_NOT_FOUND = 'TS_VECTOR_FIELD_METADATA_NOT_FOUND',
|
||||
INVALID_TS_VECTOR_FIELD_METADATA = 'INVALID_TS_VECTOR_FIELD_METADATA',
|
||||
}
|
||||
|
||||
+17
-1
@@ -12,6 +12,7 @@ import {
|
||||
} from 'typeorm';
|
||||
|
||||
import { ADD_UNIVERSAL_IDENTIFIER_AND_APPLICATION_ID_TO_SEARCH_FIELD_METADATA_UPGRADE_COMMAND_NAME } from 'src/database/commands/upgrade-version-command/2-16/add-universal-identifier-and-application-id-to-search-field-metadata-upgrade-command-name.constant';
|
||||
import { ADD_TS_VECTOR_FIELD_METADATA_ID_TO_SEARCH_FIELD_METADATA_UPGRADE_COMMAND_NAME } from 'src/database/commands/upgrade-version-command/2-18/add-ts-vector-field-metadata-id-to-search-field-metadata-upgrade-command-name.constant';
|
||||
import { WasIntroducedInUpgrade } from 'src/engine/core-modules/upgrade/decorators/was-introduced-in-upgrade.decorator';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
@@ -46,10 +47,25 @@ export class SearchFieldMetadataEntity extends SyncableEntity {
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
fieldMetadataId: string;
|
||||
|
||||
@ManyToOne(() => FieldMetadataEntity, { onDelete: 'CASCADE' })
|
||||
@ManyToOne(
|
||||
() => FieldMetadataEntity,
|
||||
(fieldMetadata) => fieldMetadata.searchFieldMetadatas,
|
||||
{ onDelete: 'CASCADE' },
|
||||
)
|
||||
@JoinColumn({ name: 'fieldMetadataId' })
|
||||
fieldMetadata: Relation<FieldMetadataEntity>;
|
||||
|
||||
@WasIntroducedInUpgrade({
|
||||
upgradeCommandName:
|
||||
ADD_TS_VECTOR_FIELD_METADATA_ID_TO_SEARCH_FIELD_METADATA_UPGRADE_COMMAND_NAME,
|
||||
})
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
tsVectorFieldMetadataId: string;
|
||||
|
||||
@ManyToOne(() => FieldMetadataEntity, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'tsVectorFieldMetadataId' })
|
||||
tsVectorFieldMetadata: Relation<FieldMetadataEntity>;
|
||||
|
||||
@Column({ nullable: false, type: 'float' })
|
||||
position: number;
|
||||
|
||||
|
||||
+2
@@ -186,6 +186,8 @@ describe('WorkspaceEntityManager', () => {
|
||||
fieldPermissionUniversalIdentifiers: [],
|
||||
viewSortIds: [],
|
||||
viewSortUniversalIdentifiers: [],
|
||||
searchFieldMetadataIds: [],
|
||||
searchFieldMetadataUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
};
|
||||
|
||||
|
||||
+2
@@ -109,6 +109,8 @@ describe('WorkspaceRepository', () => {
|
||||
fieldPermissionUniversalIdentifiers: [],
|
||||
viewSortIds: [],
|
||||
viewSortUniversalIdentifiers: [],
|
||||
searchFieldMetadataIds: [],
|
||||
searchFieldMetadataUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
};
|
||||
|
||||
|
||||
-9
@@ -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,
|
||||
|
||||
-9
@@ -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,
|
||||
|
||||
-9
@@ -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,
|
||||
|
||||
-9
@@ -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,
|
||||
|
||||
-9
@@ -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,
|
||||
|
||||
-8
@@ -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,
|
||||
|
||||
-9
@@ -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,
|
||||
|
||||
-9
@@ -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,
|
||||
|
||||
-9
@@ -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({
|
||||
|
||||
-9
@@ -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,
|
||||
|
||||
-8
@@ -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,
|
||||
|
||||
-9
@@ -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,
|
||||
|
||||
-9
@@ -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,
|
||||
|
||||
-9
@@ -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,
|
||||
|
||||
-9
@@ -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,
|
||||
|
||||
-9
@@ -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,
|
||||
|
||||
-9
@@ -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,
|
||||
|
||||
-9
@@ -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,
|
||||
|
||||
-9
@@ -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,
|
||||
|
||||
-9
@@ -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,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user