Update searchVector at label identifier update for custom fields (#7588)
By default, when custom fields are created, a searchVector field is created based on the "name" field, which is also the label identifier by default. When this label identifier is updated, we want to update the searchVector field to use this field as searchable field instead, if it is of "searchable type" (today it is only possible to select a text or number field as label identifier, while number fields are not searchable).
This commit is contained in:
@ -29,7 +29,6 @@ import {
|
||||
import { generateNullable } from 'src/engine/metadata-modules/field-metadata/utils/generate-nullable';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
|
||||
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||
import {
|
||||
RelationMetadataEntity,
|
||||
@ -76,7 +75,6 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
private readonly objectMetadataService: ObjectMetadataService,
|
||||
private readonly workspaceMigrationFactory: WorkspaceMigrationFactory,
|
||||
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
||||
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
OneToMany,
|
||||
PrimaryGeneratedColumn,
|
||||
Relation,
|
||||
Unique,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
@ -18,6 +19,11 @@ export enum IndexType {
|
||||
GIN = 'GIN',
|
||||
}
|
||||
|
||||
@Unique('IndexOnNameAndWorkspaceIdAndObjectMetadataUnique', [
|
||||
'name',
|
||||
'workspaceId',
|
||||
'objectMetadataId',
|
||||
])
|
||||
@Entity('indexMetadata')
|
||||
export class IndexMetadataEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
|
||||
@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { isDefined } from 'class-validator';
|
||||
import { Repository } from 'typeorm';
|
||||
import { InsertResult, Repository } from 'typeorm';
|
||||
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import {
|
||||
@ -45,32 +45,37 @@ export class IndexMetadataService {
|
||||
|
||||
const indexName = `IDX_${generateDeterministicIndexName([tableName, ...columnNames])}`;
|
||||
|
||||
let savedIndexMetadata: IndexMetadataEntity;
|
||||
let result: InsertResult;
|
||||
|
||||
try {
|
||||
savedIndexMetadata = await this.indexMetadataRepository.save({
|
||||
name: indexName,
|
||||
tableName,
|
||||
indexFieldMetadatas: fieldMetadataToIndex.map(
|
||||
(fieldMetadata, index) => {
|
||||
return {
|
||||
fieldMetadataId: fieldMetadata.id,
|
||||
order: index,
|
||||
};
|
||||
},
|
||||
),
|
||||
workspaceId,
|
||||
objectMetadataId: objectMetadata.id,
|
||||
...(isDefined(indexType) ? { indexType: indexType } : {}),
|
||||
isCustom: isCustom,
|
||||
});
|
||||
result = await this.indexMetadataRepository.upsert(
|
||||
{
|
||||
name: indexName,
|
||||
indexFieldMetadatas: fieldMetadataToIndex.map(
|
||||
(fieldMetadata, index) => {
|
||||
return {
|
||||
fieldMetadataId: fieldMetadata.id,
|
||||
order: index,
|
||||
};
|
||||
},
|
||||
),
|
||||
workspaceId,
|
||||
objectMetadataId: objectMetadata.id,
|
||||
...(isDefined(indexType) ? { indexType: indexType } : {}),
|
||||
isCustom: isCustom,
|
||||
},
|
||||
{
|
||||
conflictPaths: ['workspaceId', 'name', 'objectMetadataId'],
|
||||
skipUpdateIfNoValuesChanged: true,
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to create index ${indexName} on object metadata ${objectMetadata.nameSingular}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!savedIndexMetadata) {
|
||||
if (!result.identifiers.length) {
|
||||
throw new Error(
|
||||
`Failed to return saved index ${indexName} on object metadata ${objectMetadata.nameSingular}`,
|
||||
);
|
||||
|
||||
@ -14,12 +14,12 @@ import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { IndexMetadataModule } from 'src/engine/metadata-modules/index-metadata/index-metadata.module';
|
||||
import { BeforeUpdateOneObject } from 'src/engine/metadata-modules/object-metadata/hooks/before-update-one-object.hook';
|
||||
import { ObjectMetadataGraphqlApiExceptionInterceptor } from 'src/engine/metadata-modules/object-metadata/interceptors/object-metadata-graphql-api-exception.interceptor';
|
||||
import { ObjectMetadataResolver } from 'src/engine/metadata-modules/object-metadata/object-metadata.resolver';
|
||||
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { RemoteTableRelationsModule } from 'src/engine/metadata-modules/remote-server/remote-table/remote-table-relations/remote-table-relations.module';
|
||||
import { SearchModule } from 'src/engine/metadata-modules/search/search.module';
|
||||
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
|
||||
import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
|
||||
import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module';
|
||||
@ -46,8 +46,8 @@ import { UpdateObjectPayload } from './dtos/update-object.input';
|
||||
WorkspaceMigrationRunnerModule,
|
||||
WorkspaceMetadataVersionModule,
|
||||
RemoteTableRelationsModule,
|
||||
IndexMetadataModule,
|
||||
FeatureFlagModule,
|
||||
SearchModule,
|
||||
],
|
||||
services: [ObjectMetadataService],
|
||||
resolvers: [
|
||||
|
||||
@ -5,30 +5,20 @@ import console from 'console';
|
||||
|
||||
import { Query, QueryOptions } from '@ptc-org/nestjs-query-core';
|
||||
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||
import { isDefined } from 'class-validator';
|
||||
import { FindManyOptions, FindOneOptions, In, Repository } from 'typeorm';
|
||||
|
||||
import { FieldMetadataSettings } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/constants/search-vector-field.constants';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import {
|
||||
FieldMetadataEntity,
|
||||
FieldMetadataType,
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import {
|
||||
computeColumnName,
|
||||
FieldTypeAndNameMetadata,
|
||||
} from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
import { IndexType } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { IndexMetadataService } from 'src/engine/metadata-modules/index-metadata/index-metadata.service';
|
||||
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
import { DeleteOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/delete-object.input';
|
||||
import { UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
|
||||
import { DEFAULT_LABEL_IDENTIFIER_FIELD_NAME } from 'src/engine/metadata-modules/object-metadata/object-metadata.constants';
|
||||
import {
|
||||
ObjectMetadataException,
|
||||
ObjectMetadataExceptionCode,
|
||||
@ -43,8 +33,8 @@ import {
|
||||
import { RelationToDelete } from 'src/engine/metadata-modules/relation-metadata/types/relation-to-delete';
|
||||
import { RemoteTableRelationsService } from 'src/engine/metadata-modules/remote-server/remote-table/remote-table-relations/remote-table-relations.service';
|
||||
import { mapUdtNameToFieldType } from 'src/engine/metadata-modules/remote-server/remote-table/utils/udt-name-mapper.util';
|
||||
import { SearchService } from 'src/engine/metadata-modules/search/search.service';
|
||||
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
||||
import { TsVectorColumnActionFactory } from 'src/engine/metadata-modules/workspace-migration/factories/ts-vector-column-action.factory';
|
||||
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
|
||||
import {
|
||||
WorkspaceMigrationColumnActionType,
|
||||
@ -72,7 +62,7 @@ import {
|
||||
createForeignKeyDeterministicUuid,
|
||||
createRelationDeterministicUuid,
|
||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/utils/create-deterministic-uuid.util';
|
||||
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/get-ts-vector-column-expression.util';
|
||||
import { isSearchableFieldType } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util';
|
||||
import { FavoriteWorkspaceEntity } from 'src/modules/favorite/standard-objects/favorite.workspace-entity';
|
||||
import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity';
|
||||
|
||||
@ -94,17 +84,14 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
|
||||
private readonly remoteTableRelationsService: RemoteTableRelationsService,
|
||||
|
||||
private readonly tsVectorColumnActionFactory: TsVectorColumnActionFactory,
|
||||
|
||||
private readonly dataSourceService: DataSourceService,
|
||||
private readonly typeORMService: TypeORMService,
|
||||
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
||||
|
||||
private readonly indexMetadataService: IndexMetadataService,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
|
||||
private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly searchService: SearchService,
|
||||
private readonly workspaceMigrationFactory: WorkspaceMigrationFactory,
|
||||
) {
|
||||
super(objectMetadataRepository);
|
||||
@ -372,17 +359,10 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
createdObjectMetadata,
|
||||
);
|
||||
|
||||
const isSearchEnabled = await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsSearchEnabled,
|
||||
objectMetadataInput.workspaceId,
|
||||
await this.searchService.createSearchVectorFieldForObject(
|
||||
objectMetadataInput,
|
||||
createdObjectMetadata,
|
||||
);
|
||||
|
||||
if (isSearchEnabled) {
|
||||
await this.createSearchVectorField(
|
||||
objectMetadataInput,
|
||||
createdObjectMetadata,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
await this.remoteTableRelationsService.createForeignKeysMetadataAndMigrations(
|
||||
objectMetadataInput.workspaceId,
|
||||
@ -447,6 +427,32 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
await this.updateObjectRelationships(input.id, input.update.isActive);
|
||||
}
|
||||
|
||||
if (input.update.labelIdentifierFieldMetadataId) {
|
||||
const labelIdentifierFieldMetadata =
|
||||
await this.fieldMetadataRepository.findOneByOrFail({
|
||||
id: input.update.labelIdentifierFieldMetadataId,
|
||||
objectMetadataId: input.id,
|
||||
workspaceId: workspaceId,
|
||||
});
|
||||
|
||||
if (isSearchableFieldType(labelIdentifierFieldMetadata.type)) {
|
||||
await this.searchService.updateSearchVector(
|
||||
input.id,
|
||||
[
|
||||
{
|
||||
name: labelIdentifierFieldMetadata.name,
|
||||
type: labelIdentifierFieldMetadata.type,
|
||||
},
|
||||
],
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
await this.workspaceMetadataVersionService.incrementMetadataVersion(
|
||||
workspaceId,
|
||||
);
|
||||
@ -611,73 +617,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
);
|
||||
}
|
||||
|
||||
private async createSearchVectorField(
|
||||
objectMetadataInput: CreateObjectInput,
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
) {
|
||||
const searchVectorFieldMetadata = await this.fieldMetadataRepository.save({
|
||||
standardId: CUSTOM_OBJECT_STANDARD_FIELD_IDS.searchVector,
|
||||
objectMetadataId: createdObjectMetadata.id,
|
||||
workspaceId: objectMetadataInput.workspaceId,
|
||||
isCustom: false,
|
||||
isActive: false,
|
||||
isSystem: true,
|
||||
type: FieldMetadataType.TS_VECTOR,
|
||||
name: SEARCH_VECTOR_FIELD.name,
|
||||
label: SEARCH_VECTOR_FIELD.label,
|
||||
description: SEARCH_VECTOR_FIELD.description,
|
||||
isNullable: true,
|
||||
});
|
||||
|
||||
const searchableFieldForCustomObject =
|
||||
createdObjectMetadata.labelIdentifierFieldMetadataId
|
||||
? createdObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.id === createdObjectMetadata.labelIdentifierFieldMetadataId,
|
||||
)
|
||||
: createdObjectMetadata.fields.find(
|
||||
(field) => field.name === DEFAULT_LABEL_IDENTIFIER_FIELD_NAME,
|
||||
);
|
||||
|
||||
if (!isDefined(searchableFieldForCustomObject)) {
|
||||
throw new Error('No searchable field found for custom object');
|
||||
}
|
||||
|
||||
this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(
|
||||
`update-${createdObjectMetadata.nameSingular}-add-searchVector`,
|
||||
),
|
||||
createdObjectMetadata.workspaceId,
|
||||
[
|
||||
{
|
||||
name: computeTableName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
createdObjectMetadata.isCustom,
|
||||
),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: this.tsVectorColumnActionFactory.handleCreateAction({
|
||||
...searchVectorFieldMetadata,
|
||||
defaultValue: undefined,
|
||||
generatedType: 'STORED',
|
||||
asExpression: getTsVectorColumnExpressionFromFields([
|
||||
searchableFieldForCustomObject as FieldTypeAndNameMetadata,
|
||||
]),
|
||||
options: undefined,
|
||||
} as FieldMetadataInterface<FieldMetadataType.TS_VECTOR>),
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
await this.indexMetadataService.createIndex(
|
||||
objectMetadataInput.workspaceId,
|
||||
createdObjectMetadata,
|
||||
[searchVectorFieldMetadata],
|
||||
false,
|
||||
false,
|
||||
IndexType.GIN,
|
||||
);
|
||||
}
|
||||
|
||||
private async createActivityTargetRelation(
|
||||
workspaceId: string,
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
|
||||
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { IndexMetadataModule } from 'src/engine/metadata-modules/index-metadata/index-metadata.module';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { SearchService } from 'src/engine/metadata-modules/search/search.service';
|
||||
import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
NestjsQueryTypeOrmModule.forFeature(
|
||||
[ObjectMetadataEntity, FieldMetadataEntity],
|
||||
'metadata',
|
||||
),
|
||||
IndexMetadataModule,
|
||||
WorkspaceMigrationModule,
|
||||
],
|
||||
providers: [SearchService],
|
||||
exports: [SearchService],
|
||||
})
|
||||
export class SearchModule {}
|
||||
@ -0,0 +1,169 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/constants/search-vector-field.constants';
|
||||
import {
|
||||
FieldMetadataEntity,
|
||||
FieldMetadataType,
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { IndexType } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { IndexMetadataService } from 'src/engine/metadata-modules/index-metadata/index-metadata.service';
|
||||
import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input';
|
||||
import { DEFAULT_LABEL_IDENTIFIER_FIELD_NAME } from 'src/engine/metadata-modules/object-metadata/object-metadata.constants';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { TsVectorColumnActionFactory } from 'src/engine/metadata-modules/workspace-migration/factories/ts-vector-column-action.factory';
|
||||
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
|
||||
import {
|
||||
WorkspaceMigrationColumnActionType,
|
||||
WorkspaceMigrationTableActionType,
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import { WorkspaceMigrationFactory } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.factory';
|
||||
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
|
||||
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
|
||||
import { CUSTOM_OBJECT_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import {
|
||||
FieldTypeAndNameMetadata,
|
||||
getTsVectorColumnExpressionFromFields,
|
||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/utils/get-ts-vector-column-expression.util';
|
||||
import { SearchableFieldType } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util';
|
||||
import { isDefined } from 'src/utils/is-defined';
|
||||
|
||||
@Injectable()
|
||||
export class SearchService {
|
||||
constructor(
|
||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
private readonly tsVectorColumnActionFactory: TsVectorColumnActionFactory,
|
||||
private readonly indexMetadataService: IndexMetadataService,
|
||||
@InjectRepository(FieldMetadataEntity, 'metadata')
|
||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
||||
private readonly workspaceMigrationFactory: WorkspaceMigrationFactory,
|
||||
) {}
|
||||
|
||||
public async createSearchVectorFieldForObject(
|
||||
objectMetadataInput: CreateObjectInput,
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
) {
|
||||
const searchVectorFieldMetadata = await this.fieldMetadataRepository.save({
|
||||
standardId: CUSTOM_OBJECT_STANDARD_FIELD_IDS.searchVector,
|
||||
objectMetadataId: createdObjectMetadata.id,
|
||||
workspaceId: objectMetadataInput.workspaceId,
|
||||
isCustom: false,
|
||||
isActive: false,
|
||||
isSystem: true,
|
||||
type: FieldMetadataType.TS_VECTOR,
|
||||
name: SEARCH_VECTOR_FIELD.name,
|
||||
label: SEARCH_VECTOR_FIELD.label,
|
||||
description: SEARCH_VECTOR_FIELD.description,
|
||||
isNullable: true,
|
||||
});
|
||||
|
||||
const searchableFieldForCustomObject =
|
||||
createdObjectMetadata.labelIdentifierFieldMetadataId
|
||||
? createdObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.id === createdObjectMetadata.labelIdentifierFieldMetadataId,
|
||||
)
|
||||
: createdObjectMetadata.fields.find(
|
||||
(field) => field.name === DEFAULT_LABEL_IDENTIFIER_FIELD_NAME,
|
||||
);
|
||||
|
||||
if (!isDefined(searchableFieldForCustomObject)) {
|
||||
throw new Error(
|
||||
`No searchable field found for custom object (object name: ${createdObjectMetadata.nameSingular})`,
|
||||
);
|
||||
}
|
||||
|
||||
await this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(`create-${createdObjectMetadata.nameSingular}`),
|
||||
createdObjectMetadata.workspaceId,
|
||||
[
|
||||
{
|
||||
name: computeTableName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
createdObjectMetadata.isCustom,
|
||||
),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: this.tsVectorColumnActionFactory.handleCreateAction({
|
||||
...searchVectorFieldMetadata,
|
||||
defaultValue: undefined,
|
||||
generatedType: 'STORED',
|
||||
asExpression: getTsVectorColumnExpressionFromFields([
|
||||
{
|
||||
type: searchableFieldForCustomObject.type as SearchableFieldType,
|
||||
name: searchableFieldForCustomObject.name,
|
||||
},
|
||||
]),
|
||||
options: undefined,
|
||||
} as FieldMetadataInterface<FieldMetadataType.TS_VECTOR>),
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
await this.indexMetadataService.createIndex(
|
||||
objectMetadataInput.workspaceId,
|
||||
createdObjectMetadata,
|
||||
[searchVectorFieldMetadata],
|
||||
false,
|
||||
false,
|
||||
IndexType.GIN,
|
||||
);
|
||||
}
|
||||
|
||||
public async updateSearchVector(
|
||||
objectMetadataId: string,
|
||||
fieldMetadataNameAndTypeForSearch: FieldTypeAndNameMetadata[],
|
||||
workspaceId: string,
|
||||
) {
|
||||
const objectMetadata = await this.objectMetadataRepository.findOneByOrFail({
|
||||
id: objectMetadataId,
|
||||
});
|
||||
|
||||
const existingSearchVectorFieldMetadata =
|
||||
await this.fieldMetadataRepository.findOneByOrFail({
|
||||
name: SEARCH_VECTOR_FIELD.name,
|
||||
objectMetadataId,
|
||||
});
|
||||
|
||||
await this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(`update-${objectMetadata.nameSingular}`),
|
||||
workspaceId,
|
||||
[
|
||||
{
|
||||
name: computeTableName(
|
||||
objectMetadata.nameSingular,
|
||||
objectMetadata.isCustom,
|
||||
),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: this.workspaceMigrationFactory.createColumnActions(
|
||||
WorkspaceMigrationColumnActionType.ALTER,
|
||||
existingSearchVectorFieldMetadata,
|
||||
{
|
||||
...existingSearchVectorFieldMetadata,
|
||||
asExpression: getTsVectorColumnExpressionFromFields(
|
||||
fieldMetadataNameAndTypeForSearch,
|
||||
),
|
||||
generatedType: 'STORED', // Not stored on fieldMetadata
|
||||
options: undefined,
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
// index needs to be recreated as typeorm deletes then recreates searchVector column at alter
|
||||
await this.indexMetadataService.createIndex(
|
||||
workspaceId,
|
||||
objectMetadata,
|
||||
[existingSearchVectorFieldMetadata],
|
||||
false,
|
||||
false,
|
||||
IndexType.GIN,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
import { WorkspaceColumnActionOptions } from 'src/engine/metadata-modules/workspace-migration/interfaces/workspace-column-action-options.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
@ -12,10 +11,6 @@ import {
|
||||
WorkspaceMigrationColumnAlter,
|
||||
WorkspaceMigrationColumnCreate,
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import {
|
||||
WorkspaceMigrationException,
|
||||
WorkspaceMigrationExceptionCode,
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.exception';
|
||||
|
||||
export type TsVectorFieldMetadataType = FieldMetadataType.TS_VECTOR;
|
||||
|
||||
@ -40,14 +35,28 @@ export class TsVectorColumnActionFactory extends ColumnActionAbstractFactory<TsV
|
||||
];
|
||||
}
|
||||
|
||||
protected handleAlterAction(
|
||||
_currentFieldMetadata: FieldMetadataInterface<TsVectorFieldMetadataType>,
|
||||
_alteredFieldMetadata: FieldMetadataInterface<TsVectorFieldMetadataType>,
|
||||
_options?: WorkspaceColumnActionOptions,
|
||||
handleAlterAction(
|
||||
currentFieldMetadata: FieldMetadataInterface<TsVectorFieldMetadataType>,
|
||||
alteredFieldMetadata: FieldMetadataInterface<TsVectorFieldMetadataType>,
|
||||
): WorkspaceMigrationColumnAlter[] {
|
||||
throw new WorkspaceMigrationException(
|
||||
`TsVectorColumnActionFactory.handleAlterAction has not been implemented yet.`,
|
||||
WorkspaceMigrationExceptionCode.INVALID_FIELD_METADATA,
|
||||
);
|
||||
return [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.ALTER,
|
||||
currentColumnDefinition: {
|
||||
columnName: currentFieldMetadata.name,
|
||||
columnType: fieldMetadataTypeToColumnType(currentFieldMetadata.type),
|
||||
isNullable: currentFieldMetadata.isNullable ?? true,
|
||||
defaultValue: undefined,
|
||||
},
|
||||
alteredColumnDefinition: {
|
||||
columnName: alteredFieldMetadata.name,
|
||||
columnType: fieldMetadataTypeToColumnType(alteredFieldMetadata.type),
|
||||
isNullable: alteredFieldMetadata.isNullable ?? true,
|
||||
defaultValue: undefined,
|
||||
asExpression: alteredFieldMetadata.asExpression,
|
||||
generatedType: alteredFieldMetadata.generatedType,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user