fix(api): enhance projection parameters typing

This commit is contained in:
yassinedorbozgithub 2025-06-22 11:11:20 +01:00
parent 0e3187c844
commit 34636de9c5
6 changed files with 41 additions and 34 deletions

View File

@ -219,12 +219,10 @@ describe('NlpSampleRepository', () => {
skip: 0, skip: 0,
sort: ['text', 'asc'], sort: ['text', 'asc'],
} as PageQueryDto<NlpSample>; } as PageQueryDto<NlpSample>;
const projection = { text: 1 };
const result = await nlpSampleRepository.findByEntitiesAndPopulate( const result = await nlpSampleRepository.findByEntitiesAndPopulate(
{ filters, values }, { filters, values },
page, page,
projection, { text: 1 },
); );
expect(Array.isArray(result)).toBe(true); expect(Array.isArray(result)).toBe(true);

View File

@ -14,14 +14,13 @@ import {
Document, Document,
Model, Model,
PipelineStage, PipelineStage,
ProjectionType,
Query, Query,
Types, Types,
} from 'mongoose'; } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository'; import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto'; import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
import { TFilterQuery } from '@/utils/types/filter.types'; import { TFilterQuery, TProjectionType } from '@/utils/types/filter.types';
import { TNlpSampleDto } from '../dto/nlp-sample.dto'; import { TNlpSampleDto } from '../dto/nlp-sample.dto';
import { NlpSampleEntity } from '../schemas/nlp-sample-entity.schema'; import { NlpSampleEntity } from '../schemas/nlp-sample-entity.schema';
@ -183,7 +182,7 @@ export class NlpSampleRepository extends BaseRepository<
values: NlpValue[]; values: NlpValue[];
}, },
page?: PageQueryDto<NlpSample>, page?: PageQueryDto<NlpSample>,
projection?: ProjectionType<NlpSample>, projection?: TProjectionType<NlpSample>,
): Aggregate<NlpSampleDocument[]> { ): Aggregate<NlpSampleDocument[]> {
return this.model.aggregate<NlpSampleDocument>([ return this.model.aggregate<NlpSampleDocument>([
...this.buildFindByEntitiesStages(criterias), ...this.buildFindByEntitiesStages(criterias),
@ -211,7 +210,7 @@ export class NlpSampleRepository extends BaseRepository<
values: NlpValue[]; values: NlpValue[];
}, },
page?: PageQueryDto<NlpSample>, page?: PageQueryDto<NlpSample>,
projection?: ProjectionType<NlpSample>, projection?: TProjectionType<NlpSample>,
): Promise<NlpSample[]> { ): Promise<NlpSample[]> {
const aggregation = this.findByEntitiesAggregation( const aggregation = this.findByEntitiesAggregation(
criterias, criterias,
@ -239,7 +238,7 @@ export class NlpSampleRepository extends BaseRepository<
values: NlpValue[]; values: NlpValue[];
}, },
page?: PageQueryDto<NlpSample>, page?: PageQueryDto<NlpSample>,
projection?: ProjectionType<NlpSample>, projection?: TProjectionType<NlpSample>,
): Promise<NlpSampleFull[]> { ): Promise<NlpSampleFull[]> {
const aggregation = this.findByEntitiesAggregation( const aggregation = this.findByEntitiesAggregation(
criterias, criterias,

View File

@ -12,7 +12,7 @@ import {
NotFoundException, NotFoundException,
} from '@nestjs/common'; } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter'; import { OnEvent } from '@nestjs/event-emitter';
import { Document, ProjectionType, Query } from 'mongoose'; import { Document, Query } from 'mongoose';
import Papa from 'papaparse'; import Papa from 'papaparse';
import { Message } from '@/chat/schemas/message.schema'; import { Message } from '@/chat/schemas/message.schema';
@ -22,7 +22,11 @@ import { LanguageService } from '@/i18n/services/language.service';
import { DeleteResult } from '@/utils/generics/base-repository'; import { DeleteResult } from '@/utils/generics/base-repository';
import { BaseService } from '@/utils/generics/base-service'; import { BaseService } from '@/utils/generics/base-service';
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto'; import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
import { TFilterQuery, THydratedDocument } from '@/utils/types/filter.types'; import {
TFilterQuery,
THydratedDocument,
TProjectionType,
} from '@/utils/types/filter.types';
import { NlpSampleEntityCreateDto } from '../dto/nlp-sample-entity.dto'; import { NlpSampleEntityCreateDto } from '../dto/nlp-sample-entity.dto';
import { NlpSampleCreateDto, TNlpSampleDto } from '../dto/nlp-sample.dto'; import { NlpSampleCreateDto, TNlpSampleDto } from '../dto/nlp-sample.dto';
@ -78,7 +82,7 @@ export class NlpSampleService extends BaseService<
patterns: NlpValueMatchPattern[]; patterns: NlpValueMatchPattern[];
}, },
page?: PageQueryDto<NlpSample>, page?: PageQueryDto<NlpSample>,
projection?: ProjectionType<NlpSample>, projection?: TProjectionType<NlpSample>,
): Promise<NlpSample[]> { ): Promise<NlpSample[]> {
if (!patterns.length) { if (!patterns.length) {
return await this.repository.find(filters, page, projection); return await this.repository.find(filters, page, projection);
@ -118,7 +122,7 @@ export class NlpSampleService extends BaseService<
patterns: NlpValueMatchPattern[]; patterns: NlpValueMatchPattern[];
}, },
page?: PageQueryDto<NlpSample>, page?: PageQueryDto<NlpSample>,
projection?: ProjectionType<NlpSample>, projection?: TProjectionType<NlpSample>,
): Promise<NlpSampleFull[]> { ): Promise<NlpSampleFull[]> {
if (!patterns.length) { if (!patterns.length) {
return await this.repository.findAndPopulate(filters, page, projection); return await this.repository.findAndPopulate(filters, page, projection);

View File

@ -20,7 +20,6 @@ import {
HydratedDocument, HydratedDocument,
Model, Model,
PipelineStage, PipelineStage,
ProjectionType,
Query, Query,
SortOrder, SortOrder,
UpdateQuery, UpdateQuery,
@ -33,6 +32,7 @@ import {
TFilterQuery, TFilterQuery,
TFlattenOption, TFlattenOption,
THydratedDocument, THydratedDocument,
TProjectionType,
TQueryOptions, TQueryOptions,
} from '@/utils/types/filter.types'; } from '@/utils/types/filter.types';
@ -351,7 +351,7 @@ export abstract class BaseRepository<
*/ */
protected findOneQuery( protected findOneQuery(
criteria: string | TFilterQuery<T>, criteria: string | TFilterQuery<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Query<T | null, T, object, T, 'findOne', object> { ): Query<T | null, T, object, T, 'findOne', object> {
if (!criteria) { if (!criteria) {
// An empty criteria would return the first document that it finds // An empty criteria would return the first document that it finds
@ -378,7 +378,7 @@ export abstract class BaseRepository<
async findOne( async findOne(
criteria: string | TFilterQuery<T>, criteria: string | TFilterQuery<T>,
options?: ClassTransformOptions, options?: ClassTransformOptions,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Promise<T | null> { ): Promise<T | null> {
if (!criteria) { if (!criteria) {
// @TODO : Issue a warning ? // @TODO : Issue a warning ?
@ -401,7 +401,7 @@ export abstract class BaseRepository<
*/ */
async findOneAndPopulate( async findOneAndPopulate(
criteria: string | TFilterQuery<T>, criteria: string | TFilterQuery<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Promise<TFull | null> { ): Promise<TFull | null> {
this.ensureCanPopulate(); this.ensureCanPopulate();
const query = this.findOneQuery(criteria, projection).populate( const query = this.findOneQuery(criteria, projection).populate(
@ -413,7 +413,7 @@ export abstract class BaseRepository<
protected findQuery( protected findQuery(
filter: TFilterQuery<T>, filter: TFilterQuery<T>,
pageQuery?: PageQueryDto<T>, pageQuery?: PageQueryDto<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Query<T[], T, object, T, 'find', object>; ): Query<T[], T, object, T, 'find', object>;
/** /**
@ -422,7 +422,7 @@ export abstract class BaseRepository<
protected findQuery( protected findQuery(
filter: TFilterQuery<T>, filter: TFilterQuery<T>,
pageQuery?: QuerySortDto<T>, pageQuery?: QuerySortDto<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Query<T[], T, object, T, 'find', object>; ): Query<T[], T, object, T, 'find', object>;
/** /**
@ -439,7 +439,7 @@ export abstract class BaseRepository<
protected findQuery( protected findQuery(
filter: TFilterQuery<T>, filter: TFilterQuery<T>,
pageQuery?: QuerySortDto<T> | PageQueryDto<T>, pageQuery?: QuerySortDto<T> | PageQueryDto<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Query<T[], T, object, T, 'find', object> { ): Query<T[], T, object, T, 'find', object> {
if (Array.isArray(pageQuery)) { if (Array.isArray(pageQuery)) {
const query = this.model.find<T>(filter, projection); const query = this.model.find<T>(filter, projection);
@ -461,7 +461,7 @@ export abstract class BaseRepository<
async find( async find(
filter: TFilterQuery<T>, filter: TFilterQuery<T>,
pageQuery?: PageQueryDto<T>, pageQuery?: PageQueryDto<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Promise<T[]>; ): Promise<T[]>;
/** /**
@ -470,7 +470,7 @@ export abstract class BaseRepository<
async find( async find(
filter: TFilterQuery<T>, filter: TFilterQuery<T>,
pageQuery?: QuerySortDto<T>, pageQuery?: QuerySortDto<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Promise<T[]>; ): Promise<T[]>;
/** /**
@ -490,7 +490,7 @@ export abstract class BaseRepository<
async find( async find(
filter: TFilterQuery<T>, filter: TFilterQuery<T>,
pageQuery?: QuerySortDto<T> | PageQueryDto<T>, pageQuery?: QuerySortDto<T> | PageQueryDto<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Promise<T[]> { ): Promise<T[]> {
if (Array.isArray(pageQuery)) { if (Array.isArray(pageQuery)) {
const query = this.findQuery(filter, pageQuery, projection); const query = this.findQuery(filter, pageQuery, projection);
@ -518,7 +518,7 @@ export abstract class BaseRepository<
async findAndPopulate( async findAndPopulate(
filters: TFilterQuery<T>, filters: TFilterQuery<T>,
pageQuery?: PageQueryDto<T>, pageQuery?: PageQueryDto<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Promise<TFull[]>; ): Promise<TFull[]>;
/** /**
@ -527,7 +527,7 @@ export abstract class BaseRepository<
async findAndPopulate( async findAndPopulate(
filters: TFilterQuery<T>, filters: TFilterQuery<T>,
pageQuery?: QuerySortDto<T>, pageQuery?: QuerySortDto<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Promise<TFull[]>; ): Promise<TFull[]>;
/** /**
@ -547,7 +547,7 @@ export abstract class BaseRepository<
async findAndPopulate( async findAndPopulate(
filters: TFilterQuery<T>, filters: TFilterQuery<T>,
pageQuery?: QuerySortDto<T> | PageQueryDto<T>, pageQuery?: QuerySortDto<T> | PageQueryDto<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Promise<TFull[]> { ): Promise<TFull[]> {
this.ensureCanPopulate(); this.ensureCanPopulate();
if (Array.isArray(pageQuery)) { if (Array.isArray(pageQuery)) {

View File

@ -9,12 +9,12 @@
import { ConflictException, Inject } from '@nestjs/common'; import { ConflictException, Inject } from '@nestjs/common';
import { ClassTransformOptions } from 'class-transformer'; import { ClassTransformOptions } from 'class-transformer';
import { MongoError } from 'mongodb'; import { MongoError } from 'mongodb';
import { ProjectionType } from 'mongoose';
import { LoggerService } from '@/logger/logger.service'; import { LoggerService } from '@/logger/logger.service';
import { import {
TFilterQuery, TFilterQuery,
TFlattenOption, TFlattenOption,
TProjectionType,
TQueryOptions, TQueryOptions,
} from '@/utils/types/filter.types'; } from '@/utils/types/filter.types';
@ -51,14 +51,14 @@ export abstract class BaseService<
async findOne( async findOne(
criteria: string | TFilterQuery<T>, criteria: string | TFilterQuery<T>,
options?: ClassTransformOptions, options?: ClassTransformOptions,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Promise<T | null> { ): Promise<T | null> {
return await this.repository.findOne(criteria, options, projection); return await this.repository.findOne(criteria, options, projection);
} }
async findOneAndPopulate( async findOneAndPopulate(
criteria: string | TFilterQuery<T>, criteria: string | TFilterQuery<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
) { ) {
return await this.repository.findOneAndPopulate(criteria, projection); return await this.repository.findOneAndPopulate(criteria, projection);
} }
@ -66,7 +66,7 @@ export abstract class BaseService<
async find( async find(
filter: TFilterQuery<T>, filter: TFilterQuery<T>,
pageQuery?: PageQueryDto<T>, pageQuery?: PageQueryDto<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Promise<T[]>; ): Promise<T[]>;
/** /**
@ -75,13 +75,13 @@ export abstract class BaseService<
async find( async find(
filter: TFilterQuery<T>, filter: TFilterQuery<T>,
pageQuery?: QuerySortDto<T>, pageQuery?: QuerySortDto<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Promise<T[]>; ): Promise<T[]>;
async find( async find(
filter: TFilterQuery<T>, filter: TFilterQuery<T>,
pageQuery?: QuerySortDto<T> | PageQueryDto<T>, pageQuery?: QuerySortDto<T> | PageQueryDto<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Promise<T[]> { ): Promise<T[]> {
if (Array.isArray(pageQuery)) if (Array.isArray(pageQuery))
return await this.repository.find(filter, pageQuery, projection); return await this.repository.find(filter, pageQuery, projection);
@ -92,7 +92,7 @@ export abstract class BaseService<
async findAndPopulate( async findAndPopulate(
filters: TFilterQuery<T>, filters: TFilterQuery<T>,
pageQuery?: PageQueryDto<T>, pageQuery?: PageQueryDto<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Promise<TFull[]>; ): Promise<TFull[]>;
/** /**
@ -101,13 +101,13 @@ export abstract class BaseService<
async findAndPopulate( async findAndPopulate(
filters: TFilterQuery<T>, filters: TFilterQuery<T>,
pageQuery?: QuerySortDto<T>, pageQuery?: QuerySortDto<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Promise<TFull[]>; ): Promise<TFull[]>;
async findAndPopulate( async findAndPopulate(
filters: TFilterQuery<T>, filters: TFilterQuery<T>,
pageQuery?: QuerySortDto<T> | PageQueryDto<T>, pageQuery?: QuerySortDto<T> | PageQueryDto<T>,
projection?: ProjectionType<T>, projection?: TProjectionType<T>,
): Promise<TFull[]> { ): Promise<TFull[]> {
if (Array.isArray(pageQuery)) if (Array.isArray(pageQuery))
return await this.repository.findAndPopulate( return await this.repository.findAndPopulate(

View File

@ -146,3 +146,9 @@ export type THydratedDocument<T> = TOmitId<HydratedDocument<T>>;
export type TFlattenOption = { shouldFlatten?: boolean }; export type TFlattenOption = { shouldFlatten?: boolean };
export type TQueryOptions<D> = (QueryOptions<D> & TFlattenOption) | null; export type TQueryOptions<D> = (QueryOptions<D> & TFlattenOption) | null;
export type TProjectField = 0 | 1;
export type TProjectionType<T> = {
[K in keyof T]?: TProjectField;
};