Consistently return numeric values as strings in REST API and GraphQL API responses
igorlukanin opened this issue · 0 comments
Is your feature request related to a problem? Please describe.
It has become a known issue or design choice that the REST API returns numeric values either as numbers or strings, depending on multiple factors, and GraphQL API always returns numeric values as numbers. (Regarding the REST API, it will always return numeric values as strings if a pre-aggregation is used or if a measure is calculated.)
This makes it slightly inconvenient to handle numeric values at the data presentation layer, because it should both handle numbers and strings and properly cast them to desired types.
There's the castNumerics
option in the JavaScript SDK that would cast all numeric values to numbers automatically, however, this might lead to precision loss since such a conversion would not be correct in case of numbers more than Number.MAX_SAFE_INTEGER
or less than Number.MIN_SAFE_INTEGER
as they can't be represented as JavaScript Number
.
Describe the solution you'd like
Both REST API and GraphQL API should always return numeric values as strings so that the data presentation layer can parse and cast them consistently.
Potentially, these APIs can also return the native type (integer, float, or decimal) as part of the dataset or metadata, so that users who don't want to use castNumerics
can use that information for the type conversion.
Describe alternatives you've considered
—
Additional context
Here's an example of how Cube currently works, as of v0.36.2.
Data model:
cubes:
- name: numerics
sql: >
SELECT
CAST(1.23 AS FLOAT64) AS value_float64,
CAST(4.56 AS NUMERIC) AS value_numeric,
CAST(789 AS INT64) AS value_int64,
TRUE AS without_pre_aggregation
dimensions:
- name: value_float64
sql: value_float64
type: number
- name: value_numeric
sql: value_numeric
type: number
- name: value_int64
sql: value_int64
type: number
- name: without_pre_aggregation
sql: without_pre_aggregation
type: boolean
measures:
- name: max_float64
sql: value_float64
type: max
- name: max_numeric
sql: value_numeric
type: max
- name: max_int64
sql: value_int64
type: max
- name: calculated_float64
sql: "1.0 * {max_float64}"
type: number
- name: calculated_numeric
sql: "1.0 * {max_numeric}"
type: number
- name: calculated_int64
sql: "1.0 * {max_int64}"
type: number
pre_aggregations:
- name: main
dimensions:
- value_float64
- value_numeric
- value_int64
measures:
- max_float64
- max_numeric
- max_int64
Example query:
{
"limit": 5000,
"dimensions": [
"numerics.value_float64",
"numerics.value_int64",
"numerics.value_numeric",
"numerics.without_pre_aggregation"
],
"measures": [
"numerics.max_float64",
"numerics.max_int64",
"numerics.max_numeric",
"numerics.calculated_float64",
"numerics.calculated_int64",
"numerics.calculated_numeric"
]
}
REST API, query that does not use a pre-aggregation:
{
"numerics.value_float64": 1.23,
"numerics.value_int64": 789,
"numerics.value_numeric": "4.56",
"numerics.without_pre_aggregation": true,
"numerics.max_float64": 1.23,
"numerics.max_int64": 789,
"numerics.max_numeric": "4.56",
"numerics.calculated_float64": 1.23,
"numerics.calculated_int64": 789,
"numerics.calculated_numeric": "4.56"
}
REST API, query that uses a pre-aggregation:
{
"numerics.value_float64": "1.23",
"numerics.value_int64": "789",
"numerics.value_numeric": "4.56",
"numerics.max_float64": "1.23",
"numerics.max_int64": "789",
"numerics.max_numeric": "4.56",
"numerics.calculated_float64": "1.23",
"numerics.calculated_int64": "789",
"numerics.calculated_numeric": "4.56"
}
GraphQL API, any query:
{
"data": {
"cube": [
{
"numerics": {
"calculated_float64": 1.23,
"calculated_int64": 789,
"calculated_numeric": 4.56,
"max_float64": 1.23,
"max_int64": 789,
"max_numeric": 4.56,
"value_float64": 1.23,
"value_int64": 789,
"value_numeric": 4.56,
"without_pre_aggregation": "true"
}
}
]
}
}