Skip to content

Commit

Permalink
feat(core): Add aggregate group by
Browse files Browse the repository at this point in the history
  • Loading branch information
doug-martin committed Mar 31, 2021
1 parent 9eff4ae commit d5eb73b
Show file tree
Hide file tree
Showing 10 changed files with 46 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ describe('AssemblerQueryService', () => {
const mockQueryService = mock<QueryService<TestEntity>>();
const assemblerService = new AssemblerQueryService(new TestAssembler(), instance(mockQueryService));
const aggQuery: AggregateQuery<TestDTO> = { count: ['foo'] };
const result: AggregateResponse<TestDTO> = { count: { foo: 1 } };
const result: AggregateResponse<TestDTO>[] = [{ count: { foo: 1 } }];
when(
mockQueryService.aggregateRelations(
TestDTO,
Expand Down Expand Up @@ -233,10 +233,10 @@ describe('AssemblerQueryService', () => {
objectContaining({ foo: { eq: 'bar' } }),
aggQuery,
),
).thenResolve(new Map<TestEntity, AggregateResponse<TestDTO>>());
).thenResolve(new Map<TestEntity, AggregateResponse<TestDTO>[]>());
return expect(
assemblerService.aggregateRelations(TestDTO, 'test', [{ foo: 'bar' }], { foo: { eq: 'bar' } }, aggQuery),
).resolves.toEqual(new Map([[dto, {}]]));
).resolves.toEqual(new Map([[dto, []]]));
});
});

Expand Down
6 changes: 3 additions & 3 deletions packages/core/__tests__/services/proxy-query.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ describe('ProxyQueryService', () => {
it('should proxy to the underlying service when calling aggregate', () => {
const filter = {};
const aggregate: AggregateQuery<TestType> = { 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);
});
Expand Down Expand Up @@ -110,7 +110,7 @@ describe('ProxyQueryService', () => {
const dto = new TestType();
const filter = {};
const aggQuery: AggregateQuery<TestType> = { 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);
});
Expand All @@ -120,7 +120,7 @@ describe('ProxyQueryService', () => {
const dtos = [new TestType()];
const filter = {};
const aggQuery: AggregateQuery<TestType> = { 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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<TestType> = { count: ['foo'] };
Expand All @@ -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 = {};
Expand All @@ -162,7 +162,7 @@ describe('RelationQueryService', () => {
const dto = new TestType();
const filter = {};
const aggregateQuery: AggregateQuery<TestType> = { count: ['foo'] };
const result = { count: { foo: 1 } };
const result = [{ count: { foo: 1 } }];
when(mockQueryService.aggregateRelations(TestType, relationName, dto, filter, aggregateQuery)).thenResolve(
result,
);
Expand All @@ -176,7 +176,7 @@ describe('RelationQueryService', () => {
const dtos = [new TestType()];
const filter = {};
const aggregateQuery: AggregateQuery<TestType> = { 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,
);
Expand Down
13 changes: 13 additions & 0 deletions packages/core/src/interfaces/aggregate-query.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,17 @@ export type AggregateQuery<DTO> = {
avg?: (keyof DTO)[];
max?: (keyof DTO)[];
min?: (keyof DTO)[];
groupBy?: (keyof DTO)[];
};

// const j = `invoiceAgg(filter: {}){
// groupBy {
// currency
// created
// }
// max {
// amount
// date
// };
// }`;
//
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export type AggregateResponse<DTO> = {
avg?: NumberAggregate<DTO>;
max?: TypeAggregate<DTO>;
min?: TypeAggregate<DTO>;
groupBy?: Partial<DTO>;
};
14 changes: 7 additions & 7 deletions packages/core/src/services/assembler-query.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ export class AssemblerQueryService<DTO, Entity, C = DeepPartial<DTO>, CE = DeepP
return this.assembler.convertAsyncToDTOs(this.queryService.query(this.assembler.convertQuery(query)));
}

async aggregate(filter: Filter<DTO>, aggregate: AggregateQuery<DTO>): Promise<AggregateResponse<DTO>> {
async aggregate(filter: Filter<DTO>, aggregate: AggregateQuery<DTO>): Promise<AggregateResponse<DTO>[]> {
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<DTO>): Promise<number> {
Expand Down Expand Up @@ -258,21 +258,21 @@ export class AssemblerQueryService<DTO, Entity, C = DeepPartial<DTO>, CE = DeepP
dto: DTO,
filter: Filter<Relation>,
aggregate: AggregateQuery<Relation>,
): Promise<AggregateResponse<Relation>>;
): Promise<AggregateResponse<Relation>[]>;
aggregateRelations<Relation>(
RelationClass: Class<Relation>,
relationName: string,
dtos: DTO[],
filter: Filter<Relation>,
aggregate: AggregateQuery<Relation>,
): Promise<Map<DTO, AggregateResponse<Relation>>>;
): Promise<Map<DTO, AggregateResponse<Relation>[]>>;
async aggregateRelations<Relation>(
RelationClass: Class<Relation>,
relationName: string,
dto: DTO | DTO[],
filter: Filter<Relation>,
aggregate: AggregateQuery<Relation>,
): Promise<AggregateResponse<Relation> | Map<DTO, AggregateResponse<Relation>>> {
): Promise<AggregateResponse<Relation>[] | Map<DTO, AggregateResponse<Relation>[]>> {
if (Array.isArray(dto)) {
const entities = this.assembler.convertToEntities(dto);
const relationMap = await this.queryService.aggregateRelations(
Expand All @@ -283,10 +283,10 @@ export class AssemblerQueryService<DTO, Entity, C = DeepPartial<DTO>, 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<DTO, AggregateResponse<Relation>>());
}, new Map<DTO, AggregateResponse<Relation>[]>());
}
return this.queryService.aggregateRelations(
RelationClass,
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/services/noop-query.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class NoOpQueryService<DTO, C = DeepPartial<DTO>, U = DeepPartial<DTO>> i
return Promise.reject(new NotImplementedException('query is not implemented'));
}

aggregate(filter: Filter<DTO>, aggregate: AggregateQuery<DTO>): Promise<AggregateResponse<DTO>> {
aggregate(filter: Filter<DTO>, aggregate: AggregateQuery<DTO>): Promise<AggregateResponse<DTO>[]> {
return Promise.reject(new NotImplementedException('aggregate is not implemented'));
}

Expand Down Expand Up @@ -183,23 +183,23 @@ export class NoOpQueryService<DTO, C = DeepPartial<DTO>, U = DeepPartial<DTO>> i
dto: DTO,
filter: Filter<Relation>,
aggregate: AggregateQuery<Relation>,
): Promise<AggregateResponse<Relation>>;
): Promise<AggregateResponse<Relation>[]>;

aggregateRelations<Relation>(
RelationClass: Class<Relation>,
relationName: string,
dtos: DTO[],
filter: Filter<Relation>,
aggregate: AggregateQuery<Relation>,
): Promise<Map<DTO, AggregateResponse<Relation>>>;
): Promise<Map<DTO, AggregateResponse<Relation>[]>>;

aggregateRelations<Relation>(
RelationClass: Class<Relation>,
relationName: string,
dto: DTO | DTO[],
filter: Filter<Relation>,
aggregate: AggregateQuery<Relation>,
): Promise<AggregateResponse<Relation> | Map<DTO, AggregateResponse<Relation>>> {
): Promise<AggregateResponse<Relation>[] | Map<DTO, AggregateResponse<Relation>[]>> {
return Promise.reject(new NotImplementedException('aggregateRelations is not implemented'));
}
}
8 changes: 4 additions & 4 deletions packages/core/src/services/proxy-query.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ export class ProxyQueryService<DTO, C = DeepPartial<DTO>, U = DeepPartial<DTO>>
return this.proxied.query(query);
}

aggregate(filter: Filter<DTO>, query: AggregateQuery<DTO>): Promise<AggregateResponse<DTO>> {
aggregate(filter: Filter<DTO>, query: AggregateQuery<DTO>): Promise<AggregateResponse<DTO>[]> {
return this.proxied.aggregate(filter, query);
}

Expand All @@ -210,21 +210,21 @@ export class ProxyQueryService<DTO, C = DeepPartial<DTO>, U = DeepPartial<DTO>>
dto: DTO,
filter: Filter<Relation>,
aggregate: AggregateQuery<Relation>,
): Promise<AggregateResponse<Relation>>;
): Promise<AggregateResponse<Relation>[]>;
aggregateRelations<Relation>(
RelationClass: Class<Relation>,
relationName: string,
dtos: DTO[],
filter: Filter<Relation>,
aggregate: AggregateQuery<Relation>,
): Promise<Map<DTO, AggregateResponse<Relation>>>;
): Promise<Map<DTO, AggregateResponse<Relation>[]>>;
async aggregateRelations<Relation>(
RelationClass: Class<Relation>,
relationName: string,
dto: DTO | DTO[],
filter: Filter<Relation>,
aggregate: AggregateQuery<Relation>,
): Promise<AggregateResponse<Relation> | Map<DTO, AggregateResponse<Relation>>> {
): Promise<AggregateResponse<Relation>[] | Map<DTO, AggregateResponse<Relation>[]>> {
if (Array.isArray(dto)) {
return this.proxied.aggregateRelations(RelationClass, relationName, dto, filter, aggregate);
}
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/services/query.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export interface QueryService<DTO, C = DeepPartial<DTO>, U = DeepPartial<DTO>> {
* @param filter
* @param aggregate
*/
aggregate(filter: Filter<DTO>, aggregate: AggregateQuery<DTO>): Promise<AggregateResponse<DTO>>;
aggregate(filter: Filter<DTO>, aggregate: AggregateQuery<DTO>): Promise<AggregateResponse<DTO>[]>;

/**
* Count the number of records that match the filter.
Expand Down Expand Up @@ -85,15 +85,15 @@ export interface QueryService<DTO, C = DeepPartial<DTO>, U = DeepPartial<DTO>> {
dto: DTO,
filter: Filter<Relation>,
aggregate: AggregateQuery<Relation>,
): Promise<AggregateResponse<Relation>>;
): Promise<AggregateResponse<Relation>[]>;

aggregateRelations<Relation>(
RelationClass: Class<Relation>,
relationName: string,
dtos: DTO[],
filter: Filter<Relation>,
aggregate: AggregateQuery<Relation>,
): Promise<Map<DTO, AggregateResponse<Relation>>>;
): Promise<Map<DTO, AggregateResponse<Relation>[]>>;

/**
* Count the number of relations
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/services/relation-query.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,23 +95,23 @@ export class RelationQueryService<DTO, C = DeepPartial<DTO>, U = DeepPartial<DTO
dto: DTO,
filter: Filter<Relation>,
aggregate: AggregateQuery<Relation>,
): Promise<AggregateResponse<Relation>>;
): Promise<AggregateResponse<Relation>[]>;

async aggregateRelations<Relation>(
RelationClass: Class<Relation>,
relationName: string,
dtos: DTO[],
filter: Filter<Relation>,
aggregate: AggregateQuery<Relation>,
): Promise<Map<DTO, AggregateResponse<Relation>>>;
): Promise<Map<DTO, AggregateResponse<Relation>[]>>;

async aggregateRelations<Relation>(
RelationClass: Class<Relation>,
relationName: string,
dto: DTO | DTO[],
filter: Filter<Relation>,
aggregate: AggregateQuery<Relation>,
): Promise<AggregateResponse<Relation> | Map<DTO, AggregateResponse<Relation>>> {
): Promise<AggregateResponse<Relation>[] | Map<DTO, AggregateResponse<Relation>[]>> {
const serviceRelation = this.getRelation<Relation>(relationName);
if (!serviceRelation) {
if (Array.isArray(dto)) {
Expand All @@ -121,7 +121,7 @@ export class RelationQueryService<DTO, C = DeepPartial<DTO>, U = DeepPartial<DTO
}
const { query: qf, service } = serviceRelation;
if (Array.isArray(dto)) {
const map = new Map<DTO, AggregateResponse<Relation>>();
const map = new Map<DTO, AggregateResponse<Relation>[]>();
await Promise.all(
dto.map(async (d) => {
const relations = await service.aggregate(mergeQuery({ filter }, qf(d)).filter ?? {}, aggregate);
Expand Down

0 comments on commit d5eb73b

Please sign in to comment.