@@ -130,12 +130,32 @@ export class ObjectQLStrategy implements AnalyticsStrategy {
130130
131131 private resolveMeasureAggregation ( cube : Cube , measureName : string ) : { field : string ; method : string } {
132132 const fieldName = measureName . includes ( '.' ) ? measureName . split ( '.' ) [ 1 ] : measureName ;
133- const measure = cube . measures [ fieldName ] ;
134- if ( ! measure ) return { field : '*' , method : 'count' } ;
135- return {
136- field : measure . sql . replace ( / ^ \$ / , '' ) ,
137- method : measure . type === 'count_distinct' ? 'count_distinct' : measure . type ,
138- } ;
133+ const direct = cube . measures [ fieldName ] ;
134+ if ( direct ) {
135+ return {
136+ field : direct . sql . replace ( / ^ \$ / , '' ) ,
137+ method : direct . type === 'count_distinct' ? 'count_distinct' : direct . type ,
138+ } ;
139+ }
140+ // Accept `${field}_${type}` aliases (e.g. 'amount_sum') for measures whose
141+ // canonical name is just `${field}` (e.g. measure 'amount' of type 'sum').
142+ // This matches the convention used by clients that build measure names
143+ // from (field, function) pairs (e.g. the data-objectstack adapter).
144+ const aggTypes = [ 'count' , 'sum' , 'avg' , 'min' , 'max' , 'count_distinct' ] ;
145+ for ( const type of aggTypes ) {
146+ const suffix = `_${ type } ` ;
147+ if ( fieldName . endsWith ( suffix ) ) {
148+ const baseField = fieldName . slice ( 0 , - suffix . length ) ;
149+ const candidate = cube . measures [ baseField ] ;
150+ if ( candidate && candidate . type === type ) {
151+ return {
152+ field : candidate . sql . replace ( / ^ \$ / , '' ) ,
153+ method : candidate . type === 'count_distinct' ? 'count_distinct' : candidate . type ,
154+ } ;
155+ }
156+ }
157+ }
158+ return { field : '*' , method : 'count' } ;
139159 }
140160
141161 private convertFilter ( operator : string , values ?: string [ ] ) : unknown {
0 commit comments