Introduces break in change - remove search... resolvers - rename globalSearch to search - rename searchRecord.objectSingularName > objectNameSingular closes https://github.com/twentyhq/core-team-issues/issues/643
168 lines
6.9 KiB
TypeScript
168 lines
6.9 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
|
|
import { Repository } from 'typeorm';
|
|
import { FieldMetadataType } from 'twenty-shared/types';
|
|
import { isDefined } from 'twenty-shared/utils';
|
|
|
|
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 } 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';
|
|
|
|
@Injectable()
|
|
export class SearchVectorService {
|
|
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.message ?? '',
|
|
description: SEARCH_VECTOR_FIELD.description.message ?? '',
|
|
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.createIndexMetadata(
|
|
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.createIndexCreationMigration(
|
|
workspaceId,
|
|
objectMetadata,
|
|
[existingSearchVectorFieldMetadata],
|
|
false,
|
|
false,
|
|
IndexType.GIN,
|
|
);
|
|
}
|
|
}
|