-
Notifications
You must be signed in to change notification settings - Fork 1.9k
fix(schema-compiler): Handle measures with dimension-only member expressions #9335
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
Conversation
8c331af to
8f73be2
Compare
| throw new UserError(`The query contains \`COUNT(*)\` expression but cube/view ${cubeName}is missing \`count\` measure`); | ||
| } | ||
| return [typeof m.measure === 'string' ? m.measure : `${m.measure.cubeName}.${m.measure.name}`, collectedMeasures]; | ||
| if (collectedMeasures.length === 0 && m.isMemberExpression) { |
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.
Maybe we should put this part related to the member expression over dimension into a separate function and just call it here? There is a lot of code inside the lambda function - it's quite hard to read.
| const nonViewMembers = memberNamesForMeasure | ||
| .filter(mem => { | ||
| const cubeName = this.cubeEvaluator.parsePathAnyType(mem)[0]; | ||
| const cubeDef = this.cubeEvaluator.getCubeDefinition(cubeName); | ||
| return !cubeDef.isView; | ||
| }) | ||
| .map(m => this.memberInstanceByPath(m)); |
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.
Maybe it would be better to use ownedByCube here?
Something like
const nonViewMembers = memberNamesForMeasure
.map(m => this.memberInstanceByPath(m))
.filter(mem => {
return mem.definition().ownedByCube;
});
20a456f to
695b941
Compare
…sage in checkShouldBuildJoinForMeasureSelect
…essions in fullKeyQueryAggregateMeasures
`fullKeyQueryAggregate` is using measure.cube() a lot, like for grouping measures to key cube
This can be wrong for measure targeting view: we want to attach measure to subquery
for its definition cube, and there should be no subquery for view itself
It's expected that `fullKeyQueryAggregateMeasures` would resolve and prepare all measures from its original form in query to actual leaf measures in cubes, then build subqueries with those, and then `joinFullKeyQueryAggregate` would use `renderedReference` to point to these leaf measures
Hard case is a measure that:
* targeting view
* is a member expression
* references only dimensions from that view
Measure like that are "leaf" - there's nowhere to push it, member expression has to be directly in leaf subquery
If measure references dimension from a single cube it can be multiplied (think `SUM(${view.deep_dimension})`)
So, three points:
* it must be accounted for in `multipliedMeasures`
* it must be attached to a proper cube subquery
* it must use renderedReference correctly
`collectRootMeasureToHieararchy` will not drop such measure completely. Now it will check is there's 0 measures collected, try to collect all referenced members to gather used cubes and detect multiplication, and add new measure in hierarchy.
Because new returned measure is patched, it will be attached to correct cube subquery in `fullKeyQueryAggregate`
Then `outerMeasuresJoinFullKeyQueryAggregate` needs to generate proper alias for it, so outer unpatched measure would pick up alias from inner patched one
695b941 to
7893f39
Compare
…essions (cube-js#9335) `fullKeyQueryAggregate` is using measure.cube() a lot, like for grouping measures to key cube This can be wrong for measure targeting view: we want to attach measure to subquery for its definition cube, and there should be no subquery for view itself It's expected that `fullKeyQueryAggregateMeasures` would resolve and prepare all measures from its original form in query to actual leaf measures in cubes, then build subqueries with those, and then `joinFullKeyQueryAggregate` would use `renderedReference` to point to these leaf measures Hard case is a measure that: * targeting view * is a member expression * references only dimensions from that view Measure like that are "leaf" - there's nowhere to push it, member expression has to be directly in leaf subquery If measure references dimension from a single cube it can be multiplied (think `SUM(${view.deep_dimension})`) So, three points: * it must be accounted for in `multipliedMeasures` * it must be attached to a proper cube subquery * it must use `renderedReference` correctly `collectRootMeasureToHieararchy` will not drop such measure completely. Now it will check is there's 0 measures collected, try to collect all referenced members to gather used cubes and detect multiplication, and add new measure in hierarchy. Because new returned measure is patched, it will be attached to correct cube subquery in `fullKeyQueryAggregate` Then `outerMeasuresJoinFullKeyQueryAggregate` needs to generate proper alias for it, so outer unpatched measure would pick up alias from inner patched one Another special case of those measures are zero reference measures. It's member expression has no reference to any member at all, and it NOT a `COUNT(*)`. Like a `SUM(1)`. Measures like that are considered regular, and are calculated on top of join tree.
Check List
Description of Changes Made (if issue reference is not provided)
fullKeyQueryAggregateis using measure.cube() a lot, like for grouping measures to key cubeThis can be wrong for measure targeting view: we want to attach measure to subquery
for its definition cube, and there should be no subquery for view itself
It's expected that
fullKeyQueryAggregateMeasureswould resolve and prepare all measures from its original form in query to actual leaf measures in cubes, then build subqueries with those, and thenjoinFullKeyQueryAggregatewould userenderedReferenceto point to these leaf measuresHard case is a measure that:
Measure like that are "leaf" - there's nowhere to push it, member expression has to be directly in leaf subquery
If measure references dimension from a single cube it can be multiplied (think
SUM(${view.deep_dimension}))So, three points:
multipliedMeasurescollectRootMeasureToHieararchywill not drop such measure completely. Now it will check is there's 0 measures collected, try to collect all referenced members to gather used cubes and detect multiplication, and add new measure in hierarchy.Because new returned measure is patched, it will be attached to correct cube subquery in
fullKeyQueryAggregateThen
outerMeasuresJoinFullKeyQueryAggregateneeds to generate proper alias for it, so outer unpatched measure would pick up alias from inner patched oneFinally,
checkShouldBuildJoinForMeasureSelecthas to be adjusted: it collected cube names and join hints, and for dimensions-only measures on views it would collect too many, and decide that measure is multiplied, when it should not. So now it first collects non-view members, and then collect cubes and join hints from those.