New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SIP-5] Build metrics in query_object in the client #6423
Conversation
Codecov Report
@@ Coverage Diff @@
## master #6423 +/- ##
=======================================
Coverage 73.31% 73.31%
=======================================
Files 67 67
Lines 9591 9591
=======================================
Hits 7032 7032
Misses 2559 2559
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks great overall, had just a couple small comments! 😎
it('should build granularity for sql alchemy datasources', () => { | ||
const query = build({datasource: '5__table', granularity_sqla: 'ds'}); | ||
query = build({datasource: '5__table', granularity_sqla: 'ds'}); | ||
expect(query.granularity).toEqual('ds'); | ||
}); | ||
|
||
it('should build granularity for sql alchemy datasources', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this say druid
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Fixing. 🔨
superset/assets/src/query/Column.ts
Outdated
// TODO: fill out additional fields of the Column interface | ||
export default interface Column { | ||
id: number; | ||
type: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we have enough information to enumerate these yet or string
best for now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so. Found these in connectors/base/models.py
num_types = (
'DOUBLE', 'FLOAT', 'INT', 'BIGINT',
'LONG', 'REAL', 'NUMERIC', 'DECIMAL', 'MONEY',
)
date_types = ('DATE', 'TIME', 'DATETIME')
str_types = ('VARCHAR', 'STRING', 'CHAR')
I can make this an enum
. 🙌
import FormData from './FormData'; | ||
|
||
export enum MetricKey { | ||
METRIC = 'metric', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit -- could we make the key + values match / be UPPER_CASE
in all of the enum
s? there's a mix now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean making it SECONDARY_METRIC = 'SECONDARY_METRIC'
?
Yeah, we can. Just need to change the values hardcoded on the server side. It is currently expecting lower_snake_case like secondary_metric
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hrm, I'm thinking maybe we should leave the values as lower_snake_case
as they're used in the form_data json (example below). It may be odd to have UPPER_CASE
keys in the json. Thoughts?
"secondary_metric": {
"type": "SelectControl",
"label": "Color Metric",
"default": null,
"description": "A metric to use for color"
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah that makes sense, I'm okay with it for that reason but can we add a comment explaining why they keys/values have different casing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup. Adding.
|
||
export type AdhocMetric = { | ||
label?: string, | ||
optionName?: string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we know what optionName
is? sounds redundant with label
🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The naming is very confusing. It is either provided via adhocMetric
or generated (e.g. metric_vgops097wej_g8uff99zhk7
).
https://github.com/apache/incubator-superset/blob/4579b12732f975f2d278476975d0f876ab470ae1/superset/assets/src/explore/AdhocMetric.js#L48-L50
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, so it's more like an id
, hmmmm. guess we can leave since it prob would require a db migration 😣
superset/assets/src/query/Metric.ts
Outdated
} else { | ||
label = metric.sqlExpression; | ||
} | ||
return label.length < 43 ? label : `${label.substring(0, 40)}...`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could we define 43
as a const
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
...formData, | ||
metric: adhocMetric, | ||
}); | ||
expect(metrics.getLabels()).toEqual(['COUNT(verrrrrrrrry_loooooooooooooooooooo...']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
might be more robust to assert that length is less than the full length in the case that someone changes the char limit? (or they can just update the test, too 😄 )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 😄
groupby: List[str], | ||
metrics: List[Metric], | ||
filters: List[str], | ||
groupby: List[str] = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any reason to not just default these to []
instead of None
since we do below anyway?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In python, setting the default value as []
violates lint rules, and this is recommended way to do so.
58f7f00
to
2940853
Compare
- Unify the metric interface (absorb the current plain string metric for built-in metric keys into the format used by adhoc metric) - Port the logic in adhocMetric on the client and process_metrics in the backend to the new typed Metrics class - Omit hasCustomLabel and formFromData properties from the new metric interface as their value can be inferred from label and optionName - Expose from the Metrics class both metrics and their labels as public methods to match the all_metrics and metric_labels fields in the backend code - Provide defaut values for filters, metrics and groupby in the backend
2940853
to
a4597af
Compare
|
||
type Metric = { | ||
label: string; | ||
} & Partial<AdhocMetric>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh weird, my comment here got lost.
Should this instead be a | AdhocMetric
instead of & Partial<AdhocMetric>
? it seems like a Metric
is either a saved metric (with just a required label) or an AdhocMetric
with required expressionType
and either sqlExpression
or column
+aggregate
.
My making it a Partial
aren't we losing the required AdhocMetric
fields?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, Partial
here makes AdhocMetrics
optional in its entirety (we can test this by removing required fields like aggregate
for the Simple
expressionType
columns in our unit tests and we are seeing type errors 😄). Since we are now requiring label field, this also makes label
a required field in the resulting Metric
s we return from getMetrics()
.
We could alternatively write this as {label: string} | (AdhocMetric & { label: string })
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha, {label: string} | (AdhocMetric & { label: string })
makes sense based on my understanding.
Partial
here makesAdhocMetrics
optional in its entirety
I thought that Partial
made all fields optional, but if you do indeed get type errors upon removing aggregate
, etc. I'm okay with this way of writing it 👍. Can you verify that and then we can merge (unless @kristw or @conglei have feedback)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep. Verified that in the unit tests by removing aggregate
.
Btw, Partial
does make all fields optional (it is what I meant by "optional in its entirety"), so your understanding on Partial
is definitely correct. 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This lgtm with the one question about the Metric
type (|
vs &
)
@kristw @williaster @conglei