diff --git a/packages/cubejs-api-gateway/src/gateway.ts b/packages/cubejs-api-gateway/src/gateway.ts index 2aae848d78b96..8de353043cf11 100644 --- a/packages/cubejs-api-gateway/src/gateway.ts +++ b/packages/cubejs-api-gateway/src/gateway.ts @@ -1468,6 +1468,7 @@ class ApiGateway { }, response: any, responseType?: ResultType, + castNumerics: boolean = false ) { return { query: normalizedQuery, @@ -1482,6 +1483,7 @@ class ApiGateway { normalizedQuery, queryType, responseType, + castNumerics ), lastRefreshTime: response.lastRefreshTime?.toISOString(), ...( @@ -1621,6 +1623,7 @@ class ApiGateway { annotation, response, resType, + normalizedQuery.castNumerics ); }) ); @@ -1690,11 +1693,13 @@ class ApiGateway { query = this.parseQueryParam(request.query); let resType: ResultType = ResultType.DEFAULT; + // let castNumerics = false; query = this.parseMemberExpressionsInQueries(query); if (!Array.isArray(query) && query.responseFormat) { resType = query.responseFormat; + // castNumerics = Boolean(query.castNumerics); } this.log({ @@ -1797,6 +1802,7 @@ class ApiGateway { annotation, response, resType, + // castNumerics ); }) ); diff --git a/packages/cubejs-api-gateway/src/helpers/transformData.ts b/packages/cubejs-api-gateway/src/helpers/transformData.ts index a9c4f4b9a294e..43b7975a06e99 100644 --- a/packages/cubejs-api-gateway/src/helpers/transformData.ts +++ b/packages/cubejs-api-gateway/src/helpers/transformData.ts @@ -303,14 +303,36 @@ function transformData( data: { [sqlAlias: string]: unknown }[], query: NormalizedQuery, queryType: QueryType, - resType?: ResultType + resType?: ResultType, + castNumerics: boolean = false ): { members: string[], dataset: DBResponsePrimitive[][] } | { [member: string]: DBResponsePrimitive }[] { - const d = data as { [sqlAlias: string]: DBResponseValue }[]; + const numericKeys = Object.entries(annotation).map(([k, v]) => { + if (v.type === 'number') { + return k.split('.').join('__'); + } + + return null; + }).filter(Boolean) as string[]; + + const d = (data as { [sqlAlias: string]: DBResponseValue }[]).map((row) => { + if (castNumerics) { + return Object.fromEntries(Object.entries(row).map(([k, v]) => { + if (numericKeys.includes(k)) { + return [k, v != null ? Number(v) : v]; + } + + return [k, v]; + })); + } + + return row; + }); + const membersToAliasMap = getMembers( queryType, query, diff --git a/packages/cubejs-api-gateway/src/query.js b/packages/cubejs-api-gateway/src/query.js index 02e13451a32f5..7274f6796c3d6 100644 --- a/packages/cubejs-api-gateway/src/query.js +++ b/packages/cubejs-api-gateway/src/query.js @@ -111,6 +111,7 @@ const querySchema = Joi.object().keys({ renewQuery: Joi.boolean(), ungrouped: Joi.boolean(), responseFormat: Joi.valid('default', 'compact'), + castNumerics: Joi.boolean(), }); const normalizeQueryOrder = order => { diff --git a/packages/cubejs-api-gateway/src/types/query.ts b/packages/cubejs-api-gateway/src/types/query.ts index 9aa5cf1cc6248..0315bdefb3e38 100644 --- a/packages/cubejs-api-gateway/src/types/query.ts +++ b/packages/cubejs-api-gateway/src/types/query.ts @@ -10,7 +10,6 @@ import { TimeMember, FilterOperator, QueryTimeDimensionGranularity, - QueryOrderType, } from './strings'; import { ResultType } from './enums'; @@ -74,6 +73,7 @@ interface Query { renewQuery?: boolean; ungrouped?: boolean; responseFormat?: ResultType; + castNumerics?: boolean; } /**