From 9e0df0d530fafb1f64b01e8106936598dbdc5cf8 Mon Sep 17 00:00:00 2001 From: Mohamed Marrouchi Date: Tue, 13 May 2025 10:56:55 +0100 Subject: [PATCH] feat(api): add nlp pattern lookup strategy --- .../lib/__test__/base-nlp-helper.spec.ts | 4 ++-- .../controllers/nlp-value.controller.spec.ts | 2 +- .../nlp/controllers/nlp-value.controller.ts | 20 ++++++++++++++++--- api/src/nlp/dto/nlp-entity.dto.ts | 6 +++--- api/src/nlp/dto/nlp-value.dto.ts | 9 ++++++++- api/src/nlp/schemas/nlp-entity.schema.ts | 17 +++++++++++----- api/src/nlp/schemas/nlp-value.schema.ts | 8 ++++---- api/src/nlp/schemas/types.ts | 17 ++++++++++++++++ api/src/nlp/services/nlp-entity.service.ts | 4 ++-- 9 files changed, 66 insertions(+), 21 deletions(-) diff --git a/api/src/helper/lib/__test__/base-nlp-helper.spec.ts b/api/src/helper/lib/__test__/base-nlp-helper.spec.ts index c9509b1d..404592de 100644 --- a/api/src/helper/lib/__test__/base-nlp-helper.spec.ts +++ b/api/src/helper/lib/__test__/base-nlp-helper.spec.ts @@ -160,7 +160,7 @@ describe('BaseNlpHelper', () => { updatedAt: new Date(), builtin: false, expressions: [], - metadata: [], + metadata: {}, }, value2: { id: new ObjectId().toString(), @@ -170,7 +170,7 @@ describe('BaseNlpHelper', () => { updatedAt: new Date(), builtin: false, expressions: [], - metadata: [], + metadata: {}, }, }); diff --git a/api/src/nlp/controllers/nlp-value.controller.spec.ts b/api/src/nlp/controllers/nlp-value.controller.spec.ts index be6af201..19e80937 100644 --- a/api/src/nlp/controllers/nlp-value.controller.spec.ts +++ b/api/src/nlp/controllers/nlp-value.controller.spec.ts @@ -95,7 +95,7 @@ describe('NlpValueController', () => { entity: nlpEntities[0].id, value: 'valuetest', expressions: ['synonym1', 'synonym2'], - metadata: { firstkey: 'firstvalue', secondKey: 1995 }, + metadata: {}, builtin: false, doc: '', }; diff --git a/api/src/nlp/controllers/nlp-value.controller.ts b/api/src/nlp/controllers/nlp-value.controller.ts index c9d98078..9c61de51 100644 --- a/api/src/nlp/controllers/nlp-value.controller.ts +++ b/api/src/nlp/controllers/nlp-value.controller.ts @@ -71,14 +71,17 @@ export class NlpValueController extends BaseController< async create( @Body() createNlpValueDto: NlpValueCreateDto, ): Promise { + const nlpEntity = createNlpValueDto.entity + ? await this.nlpEntityService.findOne(createNlpValueDto.entity!) + : null; + this.validate({ dto: createNlpValueDto, allowedIds: { - entity: createNlpValueDto.entity - ? (await this.nlpEntityService.findOne(createNlpValueDto.entity))?.id - : null, + entity: nlpEntity?.id, }, }); + return await this.nlpValueService.create(createNlpValueDto); } @@ -171,6 +174,17 @@ export class NlpValueController extends BaseController< @Param('id') id: string, @Body() updateNlpValueDto: NlpValueUpdateDto, ): Promise { + const nlpEntity = updateNlpValueDto.entity + ? await this.nlpEntityService.findOne(updateNlpValueDto.entity!) + : null; + + this.validate({ + dto: updateNlpValueDto, + allowedIds: { + entity: nlpEntity?.id, + }, + }); + return await this.nlpValueService.updateOne(id, updateNlpValueDto); } diff --git a/api/src/nlp/dto/nlp-entity.dto.ts b/api/src/nlp/dto/nlp-entity.dto.ts index d82b6689..c986a707 100644 --- a/api/src/nlp/dto/nlp-entity.dto.ts +++ b/api/src/nlp/dto/nlp-entity.dto.ts @@ -21,7 +21,7 @@ import { import { DtoConfig } from '@/utils/types/dto.types'; -export type Lookup = 'keywords' | 'trait' | 'free-text'; +import { Lookup, LookupStrategy } from '../schemas/types'; export class NlpEntityCreateDto { @ApiProperty({ description: 'Name of the nlp entity', type: String }) @@ -33,10 +33,10 @@ export class NlpEntityCreateDto { @ApiPropertyOptional({ isArray: true, - enum: ['keywords', 'trait', 'free-text'], + enum: Object.values(LookupStrategy), }) @IsArray() - @IsIn(['keywords', 'trait', 'free-text'], { each: true }) + @IsIn(Object.values(LookupStrategy), { each: true }) @IsOptional() lookups?: Lookup[]; diff --git a/api/src/nlp/dto/nlp-value.dto.ts b/api/src/nlp/dto/nlp-value.dto.ts index 5c1c42d9..4f88318e 100644 --- a/api/src/nlp/dto/nlp-value.dto.ts +++ b/api/src/nlp/dto/nlp-value.dto.ts @@ -19,6 +19,8 @@ import { import { DtoConfig } from '@/utils/types/dto.types'; import { IsObjectId } from '@/utils/validation-rules/is-object-id'; +import { NlpMetadata } from '../schemas/types'; + export class NlpValueCreateDto { @ApiProperty({ description: 'Nlp value', type: String }) @IsString() @@ -37,7 +39,7 @@ export class NlpValueCreateDto { @ApiPropertyOptional({ description: 'Nlp value metadata', type: Object }) @IsOptional() @IsObject() - metadata?: Record; + metadata?: NlpMetadata; @ApiPropertyOptional({ description: 'Nlp Value Description', type: String }) @IsString() @@ -82,6 +84,11 @@ export class NlpValueUpdateDto { @IsObjectId({ message: 'Entity must be a valid ObjectId' }) entity?: string | null; + @ApiPropertyOptional({ description: 'Nlp Metadata', type: Object }) + @IsObject() + @IsOptional() + metadata?: NlpMetadata; + @ApiPropertyOptional({ description: 'Nlp Value Description', type: String }) @IsString() @IsOptional() diff --git a/api/src/nlp/schemas/nlp-entity.schema.ts b/api/src/nlp/schemas/nlp-entity.schema.ts index a5879d5d..d0058594 100644 --- a/api/src/nlp/schemas/nlp-entity.schema.ts +++ b/api/src/nlp/schemas/nlp-entity.schema.ts @@ -16,10 +16,8 @@ import { THydratedDocument, } from '@/utils/types/filter.types'; -import { Lookup } from '../dto/nlp-entity.dto'; - import { NlpValue } from './nlp-value.schema'; -import { NlpEntityMap } from './types'; +import { Lookup, LookupStrategy, NlpEntityMap } from './types'; @Schema({ timestamps: true }) export class NlpEntityStub extends BaseSchema { @@ -41,9 +39,18 @@ export class NlpEntityStub extends BaseSchema { name: string; /** - * Lookup strategy can contain : keywords, trait, free-text + * Lookup strategy */ - @Prop({ type: [String], default: ['keywords'] }) + @Prop({ + type: [String], + default: ['keywords'], + validate: { + validator: (lookups: string[]) => + lookups.every((lookup) => + Object.values(LookupStrategy).includes(lookup as LookupStrategy), + ), + }, + }) lookups: Lookup[]; /** diff --git a/api/src/nlp/schemas/nlp-value.schema.ts b/api/src/nlp/schemas/nlp-value.schema.ts index a49eea5c..aef7149a 100644 --- a/api/src/nlp/schemas/nlp-value.schema.ts +++ b/api/src/nlp/schemas/nlp-value.schema.ts @@ -19,9 +19,9 @@ import { import { TStubOrFull } from '@/utils/types/format.types'; import { NlpEntity, NlpEntityFull } from './nlp-entity.schema'; -import { NlpValueMap } from './types'; +import { NlpMetadata, NlpValueMap } from './types'; -@Schema({ timestamps: true }) +@Schema({ timestamps: true, minimize: false }) export class NlpValueStub extends BaseSchema { /** * This value content. @@ -44,8 +44,8 @@ export class NlpValueStub extends BaseSchema { /** * Metadata are additional data that can be associated to this values, most of the time, the metadata contains system values or ids (e.g: value: "coffee", metadata: "item_11") . */ - @Prop({ type: JSON, default: {} }) - metadata: Record; + @Prop({ type: JSON, default: () => {} }) + metadata?: NlpMetadata; /** * Description of the entity's value purpose. diff --git a/api/src/nlp/schemas/types.ts b/api/src/nlp/schemas/types.ts index 6e87dee3..87c9f027 100644 --- a/api/src/nlp/schemas/types.ts +++ b/api/src/nlp/schemas/types.ts @@ -9,6 +9,15 @@ import { NlpEntityFull, NlpEntityStub } from './nlp-entity.schema'; import { NlpValueStub } from './nlp-value.schema'; +export enum LookupStrategy { + keywords = 'keywords', + trait = 'trait', + free_text = 'free-text', + pattern = 'pattern', +} + +export type Lookup = `${LookupStrategy}`; + export interface NlpSampleEntityValue { entity: string; // entity name value: string; // entity value @@ -27,3 +36,11 @@ export enum NlpSampleState { } export type NlpCacheMap = Map; + +export type NlpMetadata = { + // Required when lookups is "pattern" + pattern?: string; + removeSpaces?: boolean; + toLowerCase?: boolean; + stripDiacritics?: boolean; +}; diff --git a/api/src/nlp/services/nlp-entity.service.ts b/api/src/nlp/services/nlp-entity.service.ts index 0f38c920..9e6025f2 100644 --- a/api/src/nlp/services/nlp-entity.service.ts +++ b/api/src/nlp/services/nlp-entity.service.ts @@ -15,14 +15,14 @@ import { NLP_MAP_CACHE_KEY } from '@/utils/constants/cache'; import { Cacheable } from '@/utils/decorators/cacheable.decorator'; import { BaseService } from '@/utils/generics/base-service'; -import { Lookup, NlpEntityDto } from '../dto/nlp-entity.dto'; +import { NlpEntityDto } from '../dto/nlp-entity.dto'; import { NlpEntityRepository } from '../repositories/nlp-entity.repository'; import { NlpEntity, NlpEntityFull, NlpEntityPopulate, } from '../schemas/nlp-entity.schema'; -import { NlpCacheMap, NlpSampleEntityValue } from '../schemas/types'; +import { Lookup, NlpCacheMap, NlpSampleEntityValue } from '../schemas/types'; import { NlpValueService } from './nlp-value.service';