fix: refactor logic

This commit is contained in:
yassinedorbozgithub 2025-04-05 21:59:19 +01:00
parent a3b92da470
commit e592a781b3
5 changed files with 133 additions and 107 deletions

View File

@ -30,6 +30,7 @@ import { PageQueryPipe } from '@/utils/pagination/pagination-query.pipe';
import { PopulatePipe } from '@/utils/pipes/populate.pipe';
import { SearchFilterPipe } from '@/utils/pipes/search-filter.pipe';
import { TFilterQuery } from '@/utils/types/filter.types';
import { Format } from '@/utils/types/format.types';
import { NlpValueCreateDto, NlpValueUpdateDto } from '../dto/nlp-value.dto';
import {
@ -147,9 +148,11 @@ export class NlpValueController extends BaseController<
)
filters: TFilterQuery<NlpValue>,
) {
return this.canPopulate(populate)
? await this.nlpValueService.findAndPopulateWithCount(pageQuery, filters)
: await this.nlpValueService.findWithCount(pageQuery, filters);
return await this.nlpValueService.findWithCount(
this.canPopulate(populate) ? Format.FULL : Format.STUB,
pageQuery,
filters,
);
}
/**

View File

@ -20,6 +20,7 @@ import mongoose, {
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
import { TFilterQuery } from '@/utils/types/filter.types';
import { Format } from '@/utils/types/format.types';
import { NlpValueDto } from '../dto/nlp-value.dto';
import { NlpEntity, NlpEntityModel } from '../schemas/nlp-entity.schema';
@ -31,7 +32,7 @@ import {
NlpValueFullWithCount,
NlpValuePopulate,
NlpValueWithCount,
TNlpValueCountFormat,
TNlpValueCount,
} from '../schemas/nlp-value.schema';
import { NlpSampleEntityRepository } from './nlp-sample-entity.repository';
@ -119,31 +120,38 @@ export class NlpValueRepository extends BaseRepository<
}
}
private async aggregateWithCount<T extends 'full' | 'stub' = 'stub'>(
/**
* Performs an aggregation to retrieve NLP values with their sample counts.
*
* @param pageQuery - The pagination parameters
* @param filterQuery - The filter criteria
* @param populatePipelineStages - Optional additional pipeline stages for populating related data
* @returns Aggregated results with sample counts
*/
private async aggregateWithCount<F extends Format>(
format: F,
{
limit = 10,
skip = 0,
sort = ['createdAt', 'desc'],
}: PageQueryDto<NlpValue>,
{ $and = [], ...rest }: TFilterQuery<NlpValue>,
populatePipelineStages: PipelineStage[] = [],
) {
): Promise<TNlpValueCount<F>[]> {
const pipeline: PipelineStage[] = [
{
// support filters
$match: {
...rest,
...($and.length && {
$and:
$and.map(({ entity, ...rest }) =>
entity
? {
...rest,
entity: new Types.ObjectId(String(entity)),
}
: rest,
) || [],
}),
...($and.length
? {
$and: $and.map(({ entity, ...rest }) => ({
...rest,
...(entity
? { entity: new Types.ObjectId(String(entity)) }
: {}),
})),
}
: {}),
},
},
// support pageQuery
@ -196,92 +204,96 @@ export class NlpValueRepository extends BaseRepository<
nlpSamplesCount: 1,
},
},
...populatePipelineStages,
...(format === Format.FULL
? [
{
$lookup: {
from: 'nlpentities',
localField: 'entity',
foreignField: '_id',
as: 'entity',
},
},
{
$unwind: '$entity',
},
]
: []),
{
$sort: {
[sort[0]]: sort[1].toString().startsWith('desc') ? -1 : 1,
_id: sort[1].toString().startsWith('desc') ? -1 : 1,
[sort[0]]:
typeof sort[1] === 'number'
? sort[1]
: sort[1].toString().toLowerCase() === 'desc'
? -1
: 1,
_id:
typeof sort[1] === 'number'
? sort[1]
: sort[1].toString().toLowerCase() === 'desc'
? -1
: 1,
},
},
];
return await this.model.aggregate<TNlpValueCountFormat<T>>(pipeline).exec();
return await this.model.aggregate<TNlpValueCount<F>>(pipeline).exec();
}
private async plainToClass<T extends 'full' | 'stub'>(
format: 'full' | 'stub',
aggregatedResults: (NlpValueWithCount | NlpValueFullWithCount)[],
): Promise<TNlpValueCountFormat<T>[]> {
if (format === 'full') {
const nestedNlpEntities: NlpValueFullWithCount[] = [];
for (const { entity, ...rest } of aggregatedResults) {
const plainNlpValue = {
private async plainToClass<F extends Format>(
format: F,
aggregatedResults: TNlpValueCount<F>[],
): Promise<TNlpValueCount<F>[]> {
const result: typeof aggregatedResults = [];
for (const item of aggregatedResults as TNlpValueCount<F>[]) {
if (format === Format.FULL) {
const { entity, ...rest } = item;
const entityData = await mongoose
.model(NlpEntityModel.name, NlpEntityModel.schema)
.findById(entity)
.lean();
const plainNlpValue: NlpValueFull = {
...rest,
entity: plainToClass(
NlpEntity,
await mongoose
.model(NlpEntityModel.name, NlpEntityModel.schema)
.findById(entity)
.lean(),
{
excludePrefixes: ['_'],
},
),
entity: plainToClass(NlpEntity, entityData, {
excludePrefixes: ['_'],
}),
};
nestedNlpEntities.push(
result.push(
plainToClass(NlpValueFullWithCount, plainNlpValue, {
excludePrefixes: ['_'],
}),
}) as TNlpValueCount<F>,
);
}
return nestedNlpEntities as TNlpValueCountFormat<T>[];
} else {
const nestedNlpEntities: NlpValueWithCount[] = [];
for (const aggregatedResult of aggregatedResults) {
nestedNlpEntities.push(
plainToClass(NlpValueWithCount, aggregatedResult, {
} else {
result.push(
plainToClass(NlpValueWithCount, item, {
excludePrefixes: ['_'],
}),
}) as TNlpValueCount<F>,
);
}
return nestedNlpEntities as TNlpValueCountFormat<T>[];
}
return result;
}
async findWithCount<F extends Format>(
format: F,
pageQuery: PageQueryDto<NlpValue>,
filterQuery: TFilterQuery<NlpValue>,
): Promise<TNlpValueCount<F>[]> {
try {
const aggregatedResults = await this.aggregateWithCount(
format,
pageQuery,
filterQuery,
);
return await this.plainToClass(format, aggregatedResults);
} catch (error) {
this.logger.error(`Error in findWithCount: ${error.message}`, error);
throw error;
}
}
async findWithCount(
pageQuery: PageQueryDto<NlpValue>,
filterQuery: TFilterQuery<NlpValue>,
): Promise<NlpValueWithCount[]> {
const aggregatedResults = await this.aggregateWithCount<'stub'>(
pageQuery,
filterQuery,
);
return await this.plainToClass<'stub'>('stub', aggregatedResults);
}
async findAndPopulateWithCount(
pageQuery: PageQueryDto<NlpValue>,
filterQuery: TFilterQuery<NlpValue>,
): Promise<NlpValueFullWithCount[]> {
const aggregatedResults = await this.aggregateWithCount<'full'>(
pageQuery,
filterQuery,
[
{
$lookup: {
from: 'nlpentities',
localField: 'entity',
foreignField: '_id',
as: 'entity',
},
},
{
$unwind: '$entity',
},
],
);
return await this.plainToClass<'full'>('full', aggregatedResults);
}
}

View File

@ -16,6 +16,7 @@ import {
TFilterPopulateFields,
THydratedDocument,
} from '@/utils/types/filter.types';
import { TStubOrFull } from '@/utils/types/format.types';
import { NlpEntity, NlpEntityFull } from './nlp-entity.schema';
import { NlpValueMap } from './types';
@ -114,10 +115,6 @@ export class NlpValueFullWithCount extends NlpValueFull {
nlpSamplesCount: number;
}
export class NlpValueFullWithCountDto {
nlpSamplesCount: number;
}
export type NlpValueDocument = THydratedDocument<NlpValue>;
export const NlpValueModel: ModelDefinition = LifecycleHookManager.attach({
@ -134,6 +131,8 @@ export type NlpValuePopulate = keyof TFilterPopulateFields<
export const NLP_VALUE_POPULATE: NlpValuePopulate[] = ['entity'];
export type TNlpValueCountFormat<T> = T extends 'stub'
? NlpValueWithCount
: NlpValueFullWithCount;
export type TNlpValueCount<T> = TStubOrFull<
T,
NlpValueWithCount,
NlpValueFullWithCount
>;

View File

@ -12,6 +12,7 @@ import { DeleteResult } from '@/utils/generics/base-repository';
import { BaseService } from '@/utils/generics/base-service';
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
import { TFilterQuery } from '@/utils/types/filter.types';
import { Format } from '@/utils/types/format.types';
import { NlpValueCreateDto, NlpValueDto } from '../dto/nlp-value.dto';
import { NlpValueRepository } from '../repositories/nlp-value.repository';
@ -19,9 +20,8 @@ import { NlpEntity } from '../schemas/nlp-entity.schema';
import {
NlpValue,
NlpValueFull,
NlpValueFullWithCount,
NlpValuePopulate,
NlpValueWithCount,
TNlpValueCount,
} from '../schemas/nlp-value.schema';
import { NlpSampleEntityValue } from '../schemas/types';
@ -223,17 +223,11 @@ export class NlpValueService extends BaseService<
return Promise.all(promises);
}
async findWithCount(
async findWithCount<F extends Format>(
format: F,
pageQuery: PageQueryDto<NlpValue>,
filters: TFilterQuery<NlpValue>,
): Promise<NlpValueWithCount[]> {
return await this.repository.findWithCount(pageQuery, filters);
}
async findAndPopulateWithCount(
pageQuery: PageQueryDto<NlpValue>,
filters: TFilterQuery<NlpValue>,
): Promise<NlpValueFullWithCount[]> {
return await this.repository.findAndPopulateWithCount(pageQuery, filters);
): Promise<TNlpValueCount<F>[]> {
return await this.repository.findWithCount(format, pageQuery, filters);
}
}

View File

@ -0,0 +1,18 @@
/*
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
export enum Format {
NONE = 0,
STUB = 1,
BASIC = 2,
FULL = 3,
}
export type TStubOrFull<TF, TStub, TFull> = TF extends Format.STUB
? TStub
: TFull;