diff --git a/packages/core/__tests__/services/assembler-query.service.spec.ts b/packages/core/__tests__/services/assembler-query.service.spec.ts index c5bd65fa9..db0352a68 100644 --- a/packages/core/__tests__/services/assembler-query.service.spec.ts +++ b/packages/core/__tests__/services/assembler-query.service.spec.ts @@ -178,7 +178,7 @@ describe('AssemblerQueryService', () => { const mockQueryService = mock>(); const assemblerService = new AssemblerQueryService(new TestAssembler(), instance(mockQueryService)); const aggQuery: AggregateQuery = { count: ['foo'] }; - const result: AggregateResponse = { count: { foo: 1 } }; + const result: AggregateResponse[] = [{ count: { foo: 1 } }]; when( mockQueryService.aggregateRelations( TestDTO, @@ -233,10 +233,10 @@ describe('AssemblerQueryService', () => { objectContaining({ foo: { eq: 'bar' } }), aggQuery, ), - ).thenResolve(new Map>()); + ).thenResolve(new Map[]>()); return expect( assemblerService.aggregateRelations(TestDTO, 'test', [{ foo: 'bar' }], { foo: { eq: 'bar' } }, aggQuery), - ).resolves.toEqual(new Map([[dto, {}]])); + ).resolves.toEqual(new Map([[dto, []]])); }); }); diff --git a/packages/core/__tests__/services/proxy-query.service.spec.ts b/packages/core/__tests__/services/proxy-query.service.spec.ts index cfa50f48e..a6888612c 100644 --- a/packages/core/__tests__/services/proxy-query.service.spec.ts +++ b/packages/core/__tests__/services/proxy-query.service.spec.ts @@ -77,7 +77,7 @@ describe('ProxyQueryService', () => { it('should proxy to the underlying service when calling aggregate', () => { const filter = {}; const aggregate: AggregateQuery = { count: ['foo'] }; - const result = { count: { foo: 1 } }; + const result = [{ count: { foo: 1 } }]; when(mockQueryService.aggregate(filter, aggregate)).thenResolve(result); return expect(queryService.aggregate(filter, aggregate)).resolves.toBe(result); }); @@ -110,7 +110,7 @@ describe('ProxyQueryService', () => { const dto = new TestType(); const filter = {}; const aggQuery: AggregateQuery = { count: ['foo'] }; - const result = { count: { foo: 1 } }; + const result = [{ count: { foo: 1 } }]; when(mockQueryService.aggregateRelations(TestType, relationName, dto, filter, aggQuery)).thenResolve(result); return expect(queryService.aggregateRelations(TestType, relationName, dto, filter, aggQuery)).resolves.toBe(result); }); @@ -120,7 +120,7 @@ describe('ProxyQueryService', () => { const dtos = [new TestType()]; const filter = {}; const aggQuery: AggregateQuery = { count: ['foo'] }; - const result = new Map([[{ foo: 'bar' }, { count: { foo: 1 } }]]); + const result = new Map([[{ foo: 'bar' }, [{ count: { foo: 1 } }]]]); when(mockQueryService.aggregateRelations(TestType, relationName, dtos, filter, aggQuery)).thenResolve(result); return expect(queryService.aggregateRelations(TestType, relationName, dtos, filter, aggQuery)).resolves.toBe( result, diff --git a/packages/core/__tests__/services/relation-query.service.spec.ts b/packages/core/__tests__/services/relation-query.service.spec.ts index b75b45a1f..c9e3137b2 100644 --- a/packages/core/__tests__/services/relation-query.service.spec.ts +++ b/packages/core/__tests__/services/relation-query.service.spec.ts @@ -128,7 +128,7 @@ describe('RelationQueryService', () => { it('should proxy to the underlying service when calling queryRelations with one dto', async () => { const relationName = 'test'; const dto = new TestType(); - const result = { count: { foo: 1 } }; + const result = [{ count: { foo: 1 } }]; const filter = {}; const relationFilter = {}; const relationAggregateQuery: AggregateQuery = { count: ['foo'] }; @@ -143,7 +143,7 @@ describe('RelationQueryService', () => { it('should proxy to the underlying service when calling queryRelations with many dtos', async () => { const relationName = 'test'; const dtos = [new TestType()]; - const relationResults = { count: { foo: 1 } }; + const relationResults = [{ count: { foo: 1 } }]; const result = new Map([[dtos[0], relationResults]]); const filter = {}; const relationFilter = {}; @@ -162,7 +162,7 @@ describe('RelationQueryService', () => { const dto = new TestType(); const filter = {}; const aggregateQuery: AggregateQuery = { count: ['foo'] }; - const result = { count: { foo: 1 } }; + const result = [{ count: { foo: 1 } }]; when(mockQueryService.aggregateRelations(TestType, relationName, dto, filter, aggregateQuery)).thenResolve( result, ); @@ -176,7 +176,7 @@ describe('RelationQueryService', () => { const dtos = [new TestType()]; const filter = {}; const aggregateQuery: AggregateQuery = { count: ['foo'] }; - const result = new Map([[dtos[0], { count: { foo: 1 } }]]); + const result = new Map([[dtos[0], [{ count: { foo: 1 } }]]]); when(mockQueryService.aggregateRelations(TestType, relationName, dtos, filter, aggregateQuery)).thenResolve( result, ); diff --git a/packages/core/src/interfaces/aggregate-query.interface.ts b/packages/core/src/interfaces/aggregate-query.interface.ts index 6db2812d9..589321758 100644 --- a/packages/core/src/interfaces/aggregate-query.interface.ts +++ b/packages/core/src/interfaces/aggregate-query.interface.ts @@ -4,4 +4,17 @@ export type AggregateQuery = { avg?: (keyof DTO)[]; max?: (keyof DTO)[]; min?: (keyof DTO)[]; + groupBy?: (keyof DTO)[]; }; + +// const j = `invoiceAgg(filter: {}){ +// groupBy { +// currency +// created +// } +// max { +// amount +// date +// }; +// }`; +// diff --git a/packages/core/src/interfaces/aggregate-response.interface.ts b/packages/core/src/interfaces/aggregate-response.interface.ts index 4da63417a..c95b7b0b9 100644 --- a/packages/core/src/interfaces/aggregate-response.interface.ts +++ b/packages/core/src/interfaces/aggregate-response.interface.ts @@ -12,4 +12,5 @@ export type AggregateResponse = { avg?: NumberAggregate; max?: TypeAggregate; min?: TypeAggregate; + groupBy?: Partial; }; diff --git a/packages/core/src/services/assembler-query.service.ts b/packages/core/src/services/assembler-query.service.ts index ebd876eee..b096ebb92 100644 --- a/packages/core/src/services/assembler-query.service.ts +++ b/packages/core/src/services/assembler-query.service.ts @@ -71,12 +71,12 @@ export class AssemblerQueryService, CE = DeepP return this.assembler.convertAsyncToDTOs(this.queryService.query(this.assembler.convertQuery(query))); } - async aggregate(filter: Filter, aggregate: AggregateQuery): Promise> { + async aggregate(filter: Filter, aggregate: AggregateQuery): Promise[]> { const aggregateResponse = await this.queryService.aggregate( this.assembler.convertQuery({ filter }).filter || {}, this.assembler.convertAggregateQuery(aggregate), ); - return this.assembler.convertAggregateResponse(aggregateResponse); + return aggregateResponse.map((agg) => this.assembler.convertAggregateResponse(agg)); } count(filter: Filter): Promise { @@ -258,21 +258,21 @@ export class AssemblerQueryService, CE = DeepP dto: DTO, filter: Filter, aggregate: AggregateQuery, - ): Promise>; + ): Promise[]>; aggregateRelations( RelationClass: Class, relationName: string, dtos: DTO[], filter: Filter, aggregate: AggregateQuery, - ): Promise>>; + ): Promise[]>>; async aggregateRelations( RelationClass: Class, relationName: string, dto: DTO | DTO[], filter: Filter, aggregate: AggregateQuery, - ): Promise | Map>> { + ): Promise[] | Map[]>> { if (Array.isArray(dto)) { const entities = this.assembler.convertToEntities(dto); const relationMap = await this.queryService.aggregateRelations( @@ -283,10 +283,10 @@ export class AssemblerQueryService, CE = DeepP aggregate, ); return entities.reduce((map, e, index) => { - const entry = relationMap.get(e) ?? {}; + const entry = relationMap.get(e) ?? []; map.set(dto[index], entry); return map; - }, new Map>()); + }, new Map[]>()); } return this.queryService.aggregateRelations( RelationClass, diff --git a/packages/core/src/services/noop-query.service.ts b/packages/core/src/services/noop-query.service.ts index 4a3f76d64..f697f29f5 100644 --- a/packages/core/src/services/noop-query.service.ts +++ b/packages/core/src/services/noop-query.service.ts @@ -88,7 +88,7 @@ export class NoOpQueryService, U = DeepPartial> i return Promise.reject(new NotImplementedException('query is not implemented')); } - aggregate(filter: Filter, aggregate: AggregateQuery): Promise> { + aggregate(filter: Filter, aggregate: AggregateQuery): Promise[]> { return Promise.reject(new NotImplementedException('aggregate is not implemented')); } @@ -183,7 +183,7 @@ export class NoOpQueryService, U = DeepPartial> i dto: DTO, filter: Filter, aggregate: AggregateQuery, - ): Promise>; + ): Promise[]>; aggregateRelations( RelationClass: Class, @@ -191,7 +191,7 @@ export class NoOpQueryService, U = DeepPartial> i dtos: DTO[], filter: Filter, aggregate: AggregateQuery, - ): Promise>>; + ): Promise[]>>; aggregateRelations( RelationClass: Class, @@ -199,7 +199,7 @@ export class NoOpQueryService, U = DeepPartial> i dto: DTO | DTO[], filter: Filter, aggregate: AggregateQuery, - ): Promise | Map>> { + ): Promise[] | Map[]>> { return Promise.reject(new NotImplementedException('aggregateRelations is not implemented')); } } diff --git a/packages/core/src/services/proxy-query.service.ts b/packages/core/src/services/proxy-query.service.ts index 8d75cd536..bcfc0d61a 100644 --- a/packages/core/src/services/proxy-query.service.ts +++ b/packages/core/src/services/proxy-query.service.ts @@ -188,7 +188,7 @@ export class ProxyQueryService, U = DeepPartial> return this.proxied.query(query); } - aggregate(filter: Filter, query: AggregateQuery): Promise> { + aggregate(filter: Filter, query: AggregateQuery): Promise[]> { return this.proxied.aggregate(filter, query); } @@ -210,21 +210,21 @@ export class ProxyQueryService, U = DeepPartial> dto: DTO, filter: Filter, aggregate: AggregateQuery, - ): Promise>; + ): Promise[]>; aggregateRelations( RelationClass: Class, relationName: string, dtos: DTO[], filter: Filter, aggregate: AggregateQuery, - ): Promise>>; + ): Promise[]>>; async aggregateRelations( RelationClass: Class, relationName: string, dto: DTO | DTO[], filter: Filter, aggregate: AggregateQuery, - ): Promise | Map>> { + ): Promise[] | Map[]>> { if (Array.isArray(dto)) { return this.proxied.aggregateRelations(RelationClass, relationName, dto, filter, aggregate); } diff --git a/packages/core/src/services/query.service.ts b/packages/core/src/services/query.service.ts index d34a4d1d8..5af6d06ba 100644 --- a/packages/core/src/services/query.service.ts +++ b/packages/core/src/services/query.service.ts @@ -33,7 +33,7 @@ export interface QueryService, U = DeepPartial> { * @param filter * @param aggregate */ - aggregate(filter: Filter, aggregate: AggregateQuery): Promise>; + aggregate(filter: Filter, aggregate: AggregateQuery): Promise[]>; /** * Count the number of records that match the filter. @@ -85,7 +85,7 @@ export interface QueryService, U = DeepPartial> { dto: DTO, filter: Filter, aggregate: AggregateQuery, - ): Promise>; + ): Promise[]>; aggregateRelations( RelationClass: Class, @@ -93,7 +93,7 @@ export interface QueryService, U = DeepPartial> { dtos: DTO[], filter: Filter, aggregate: AggregateQuery, - ): Promise>>; + ): Promise[]>>; /** * Count the number of relations diff --git a/packages/core/src/services/relation-query.service.ts b/packages/core/src/services/relation-query.service.ts index 5af5593ba..fb68f9f61 100644 --- a/packages/core/src/services/relation-query.service.ts +++ b/packages/core/src/services/relation-query.service.ts @@ -95,7 +95,7 @@ export class RelationQueryService, U = DeepPartial, aggregate: AggregateQuery, - ): Promise>; + ): Promise[]>; async aggregateRelations( RelationClass: Class, @@ -103,7 +103,7 @@ export class RelationQueryService, U = DeepPartial, aggregate: AggregateQuery, - ): Promise>>; + ): Promise[]>>; async aggregateRelations( RelationClass: Class, @@ -111,7 +111,7 @@ export class RelationQueryService, U = DeepPartial, aggregate: AggregateQuery, - ): Promise | Map>> { + ): Promise[] | Map[]>> { const serviceRelation = this.getRelation(relationName); if (!serviceRelation) { if (Array.isArray(dto)) { @@ -121,7 +121,7 @@ export class RelationQueryService, U = DeepPartial>(); + const map = new Map[]>(); await Promise.all( dto.map(async (d) => { const relations = await service.aggregate(mergeQuery({ filter }, qf(d)).filter ?? {}, aggregate);