import sequelize , { Projectable } from 'sequelize' ; import { AggregateQuery , AggregateResponse } from '@nestjs-query/core' ; import { Model , ModelCtor } from 'sequelize-typescript' ; import { BadRequestException } from '@nestjs/common' ; import { camelCase } from 'camel-case' ; enum AggregateFuncs { AVG = 'AVG' , SUM = 'SUM' , COUNT = 'COUNT' , MAX = 'MAX' , MIN = 'MIN' } const AGG_REGEXP = /(AVG|SUM|COUNT|MAX|MIN|GROUP_BY)_(.*)/ ; internal export class AggregateBuilder < Entity extends Model < Entity , Partial < Entity > > > { static convertToAggregateResponse < Entity > ( rawAggregates ) { return rawAggregates . map ( ( aggregate ) => { return Object . keys ( aggregate ) . reduce ( ( agg , resultField ) => { const matchResult = AGG_REGEXP . exec ( resultField ) ; if ( ! matchResult ) { throw new Error ( 'Unknown aggregate column encountered.' ) ; } const [ matchedFunc , matchedFieldName ] = matchResult . slice ( 1 ) ; const aggResponseKey = camelCase ( matchedFunc . toLowerCase ( ) ) as keyof AggregateResponse < Entity > ; const fieldName = matchedFieldName as keyof Entity ; const aggResult = agg [ aggResponseKey ] || { } ; return { ... agg , [ aggResponseKey ] : { ... aggResult , [ fieldName ] : aggregate [ resultField ] } , } ; } , { } as AggregateResponse < Entity > ) ; } ) ; } constructor ( readonly model ) { } param aggregate build ( aggregate ) { const selects = [ ... this . createGroupBySelect ( aggregate . groupBy ) , ... this . createAggSelect ( AggregateFuncs . COUNT , aggregate . count ) , ... this . createAggSelect ( AggregateFuncs . SUM , aggregate . sum ) , ... this . createAggSelect ( AggregateFuncs . AVG , aggregate . avg ) , ... this . createAggSelect ( AggregateFuncs . MAX , aggregate . max ) , ... this . createAggSelect ( AggregateFuncs . MIN , aggregate . min ) , ] ; if ( ! selects . length ) { throw new BadRequestException ( 'No aggregate fields found.' ) ; } return { attributes : selects , } ; } private createAggSelect ( func , fields ? ) { if ( ! fields ) { return [ ] ; } return fields . map ( ( field ) => { const aggAlias = `${ func }_${ field as string }` ; const colName = this . model . rawAttributes [ field as string ] . field ; const fn = sequelize . fn ( func , sequelize . col ( colName || ( field as string ) ) ) ; return [ fn , aggAlias ] ; } ) ; } private createGroupBySelect ( fields ? ) { if ( ! fields ) { return [ ] ; } return fields . map ( ( field ) => { const colName = this . model . rawAttributes [ field as string ] . field ; return [ sequelize . col ( colName || ( field as string ) ) , `GROUP_BY_${ field as string }` ] ; } ) ; } }