Skip to content

Commit 910a49d

Browse files
authored
fix: Non-additive pre-aggregations based on proxy dimension doesn't match anymore (#7396)
* fix: Non-additive pre-aggregations based on proxy dimension doesn't match anymore * Measures can have grouped measure filters so make them flat * prestodb-sandbox by Ahana is missing so disable test for now
1 parent fbdc0d2 commit 910a49d

File tree

7 files changed

+164
-67
lines changed

7 files changed

+164
-67
lines changed

.github/actions/smoke.sh

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@ echo "::group::Multidb"
3838
yarn lerna run --concurrency 1 --stream --no-prefix smoke:multidb
3939
echo "::endgroup::"
4040

41-
echo "::group::Prestodb"
42-
docker rm -vf $(docker ps -aq)
43-
docker rmi -f $(docker images -aq)
44-
docker pull ahanaio/prestodb-sandbox:0.281
45-
yarn lerna run --concurrency 1 --stream --no-prefix smoke:prestodb
46-
echo "::endgroup::"
41+
#echo "::group::Prestodb"
42+
#docker rm -vf $(docker ps -aq)
43+
#docker rmi -f $(docker images -aq)
44+
#docker pull ahanaio/prestodb-sandbox:0.281
45+
#yarn lerna run --concurrency 1 --stream --no-prefix smoke:prestodb
46+
#echo "::endgroup::"
4747

4848
echo "::group::Trino"
4949
yarn lerna run --concurrency 1 --stream --no-prefix smoke:trino

packages/cubejs-schema-compiler/src/adapter/BaseDimension.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,11 @@ export class BaseDimension {
100100
}
101101
return this.query.cubeEvaluator.parsePath('dimensions', this.dimension);
102102
}
103+
104+
expressionPath() {
105+
if (this.expression) {
106+
return `expr:${this.expression.expressionName}`;
107+
}
108+
return this.query.cubeEvaluator.pathFromArray(this.path());
109+
}
103110
}

packages/cubejs-schema-compiler/src/adapter/BaseMeasure.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,11 @@ export class BaseMeasure {
164164
}
165165
return this.query.cubeEvaluator.parsePath('measures', this.measure);
166166
}
167+
168+
expressionPath() {
169+
if (this.expression) {
170+
return `expr:${this.expression.expressionName}`;
171+
}
172+
return this.query.cubeEvaluator.pathFromArray(this.path());
173+
}
167174
}

packages/cubejs-schema-compiler/src/adapter/BaseSegment.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,12 @@ export class BaseSegment {
3535
path() {
3636
return this.query.cubeEvaluator.parsePath('segments', this.segment);
3737
}
38+
39+
expressionPath() {
40+
// TODO expression support
41+
// if (this.expression) {
42+
// return `expr:${this.expression.expressionName}`;
43+
// }
44+
return this.query.cubeEvaluator.pathFromArray(this.path());
45+
}
3846
}

packages/cubejs-schema-compiler/src/adapter/PreAggregations.js

Lines changed: 70 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ export class PreAggregations {
142142
}
143143
return [];
144144
}
145-
145+
146146
preAggregationDescriptionFor(cube, foundPreAggregation) {
147147
const { preAggregationName, preAggregation, references } = foundPreAggregation;
148148

@@ -170,7 +170,7 @@ export class PreAggregations {
170170
}[preAggregation.type] || uniqueKeyColumnsDefault)();
171171

172172
const aggregationsColumns = this.aggregationsColumns(cube, preAggregation);
173-
173+
174174
return {
175175
preAggregationId: `${cube}.${preAggregationName}`,
176176
timezone: this.query.options && this.query.options.timezone,
@@ -268,12 +268,12 @@ export class PreAggregations {
268268
static transformQueryToCanUseForm(query) {
269269
const flattenDimensionMembers = this.flattenDimensionMembers(query);
270270
const sortedDimensions = this.squashDimensions(query, flattenDimensionMembers);
271-
const aliasedToFirstNonAliasedDimension = this.aliasedToFirstNonAliasedDimension(query, flattenDimensionMembers);
271+
const allBackAliasMembers = this.allBackAliasMembers(query);
272272
const measures = query.measures.concat(query.measureFilters);
273-
const measurePaths = R.uniq(PreAggregations.firstNonAliasMember(query, measures));
273+
const measurePaths = R.uniq(this.flattenMembers(measures).map(m => m.expressionPath()));
274274
const collectLeafMeasures = query.collectLeafMeasures.bind(query);
275-
const dimensionsList = PreAggregations.firstNonAliasMember(query, query.dimensions);
276-
const segmentsList = PreAggregations.firstNonAliasMember(query, query.segments);
275+
const dimensionsList = query.dimensions.map(dim => dim.expressionPath());
276+
const segmentsList = query.segments.map(s => s.expressionPath());
277277
const ownedDimensions = PreAggregations.ownedMembers(query, flattenDimensionMembers);
278278
const ownedTimeDimensions = query.timeDimensions.map(td => {
279279
const owned = PreAggregations.ownedMembers(query, [td]);
@@ -290,19 +290,6 @@ export class PreAggregations {
290290
});
291291
}).map(d => query.newTimeDimension(d));
292292

293-
const firstNonAliasTimeDimensions = query.timeDimensions.map(td => {
294-
const nonAlias = PreAggregations.firstNonAliasMember(query, [td]);
295-
let { dimension } = td;
296-
if (nonAlias.length === 1) {
297-
[dimension] = nonAlias;
298-
}
299-
return query.newTimeDimension({
300-
dimension,
301-
dateRange: td.dateRange,
302-
granularity: td.granularity,
303-
});
304-
}).map(d => query.newTimeDimension(d));
305-
306293
const measureToLeafMeasures = {};
307294

308295
const leafMeasurePaths =
@@ -311,14 +298,14 @@ export class PreAggregations {
311298
const leafMeasures = query.collectFrom([m], collectLeafMeasures, 'collectLeafMeasures');
312299
measureToLeafMeasures[m.measure] = leafMeasures.map((measure) => {
313300
const baseMeasure = query.newMeasure(measure);
314-
301+
315302
return {
316303
measure,
317304
additive: baseMeasure.isAdditive(),
318305
type: baseMeasure.definition().type
319306
};
320307
});
321-
308+
322309
return leafMeasures;
323310
}),
324311
R.unnest,
@@ -334,8 +321,8 @@ export class PreAggregations {
334321
return true;
335322
}
336323

337-
const sortedTimeDimensions = PreAggregations.sortTimeDimensionsWithRollupGranularity(firstNonAliasTimeDimensions);
338-
const timeDimensions = PreAggregations.timeDimensionsAsIs(firstNonAliasTimeDimensions);
324+
const sortedTimeDimensions = PreAggregations.sortTimeDimensionsWithRollupGranularity(query.timeDimensions);
325+
const timeDimensions = PreAggregations.timeDimensionsAsIs(query.timeDimensions);
339326
const ownedTimeDimensionsWithRollupGranularity = PreAggregations.sortTimeDimensionsWithRollupGranularity(ownedTimeDimensions);
340327
const ownedTimeDimensionsAsIs = PreAggregations.timeDimensionsAsIs(ownedTimeDimensions);
341328

@@ -345,7 +332,7 @@ export class PreAggregations {
345332
R.all(d => dimensionsList.indexOf(d) !== -1)(
346333
R.flatten(
347334
query.filters.map(f => f.getMembers())
348-
).map(f => PreAggregations.firstNonAliasMember(query, [f])[0])
335+
).map(f => query.cubeEvaluator.pathFromArray(f.path()))
349336
);
350337

351338
const isAdditive = R.all(m => m.isAdditive(), query.measures);
@@ -363,7 +350,6 @@ export class PreAggregations {
363350
let filterDimensionsSingleValueEqual = this.collectFilterDimensionsWithSingleValueEqual(
364351
query.filters,
365352
dimensionsList.concat(segmentsList).reduce((map, d) => map.set(d, 1), new Map()),
366-
aliasedToFirstNonAliasedDimension
367353
);
368354

369355
filterDimensionsSingleValueEqual =
@@ -387,7 +373,8 @@ export class PreAggregations {
387373
filterDimensionsSingleValueEqual,
388374
ownedDimensions,
389375
ownedTimeDimensionsWithRollupGranularity,
390-
ownedTimeDimensionsAsIs
376+
ownedTimeDimensionsAsIs,
377+
allBackAliasMembers
391378
};
392379
}
393380

@@ -399,36 +386,52 @@ export class PreAggregations {
399386
);
400387
}
401388

402-
static firstNonAliasMember(query, members) {
389+
static backAliasMembers(query, members) {
403390
return members.map(
404-
member => query
405-
.collectFrom([member], query.collectMemberNamesFor.bind(query), 'collectMemberNamesFor')
406-
.find(d => !query.cubeEvaluator.byPathAnyType(d).aliasMember)
407-
);
391+
member => {
392+
const collectedMembers = query
393+
.collectFrom([member], query.collectMemberNamesFor.bind(query), 'collectMemberNamesFor');
394+
const memberPath = member.expressionPath();
395+
let nonAliasSeen = false;
396+
return collectedMembers
397+
.filter(d => {
398+
if (!query.cubeEvaluator.byPathAnyType(d).aliasMember) {
399+
nonAliasSeen = true;
400+
}
401+
return !nonAliasSeen;
402+
})
403+
.map(d => (
404+
{ [query.cubeEvaluator.byPathAnyType(d).aliasMember]: memberPath }
405+
)).reduce((a, b) => ({ ...a, ...b }), {});
406+
}
407+
).reduce((a, b) => ({ ...a, ...b }), {});
408+
}
409+
410+
static allBackAliasMembers(query) {
411+
return this.backAliasMembers(query, this.flattenAllMembers(query));
408412
}
409413

410414
static sortTimeDimensionsWithRollupGranularity(timeDimensions) {
411415
return timeDimensions && R.sortBy(
412416
R.prop(0),
413-
timeDimensions.map(d => [d.dimension, d.rollupGranularity()])
417+
timeDimensions.map(d => [d.expressionPath(), d.rollupGranularity()])
414418
) || [];
415419
}
416420

417421
static timeDimensionsAsIs(timeDimensions) {
418422
return timeDimensions && R.sortBy(
419423
R.prop(0),
420-
timeDimensions.map(d => [d.dimension, d.granularity]),
424+
timeDimensions.map(d => [d.expressionPath(), d.granularity]),
421425
) || [];
422426
}
423427

424-
static collectFilterDimensionsWithSingleValueEqual(filters, map, aliasedToFirstNonAliasedDimension) {
428+
static collectFilterDimensionsWithSingleValueEqual(filters, map) {
425429
// eslint-disable-next-line no-restricted-syntax
426430
for (const f of filters) {
427431
if (f.operator === 'equals') {
428-
const nonAliasedDimension = aliasedToFirstNonAliasedDimension[f.dimension] || f.dimension;
429-
map.set(nonAliasedDimension, Math.min(map.get(nonAliasedDimension) || 2, f.values.length));
432+
map.set(f.expressionPath(), Math.min(map.get(f.expressionPath()) || 2, f.values.length));
430433
} else if (f.operator === 'and') {
431-
const res = this.collectFilterDimensionsWithSingleValueEqual(f.values, map, aliasedToFirstNonAliasedDimension);
434+
const res = this.collectFilterDimensionsWithSingleValueEqual(f.values, map);
432435
if (res == null) return null;
433436
} else {
434437
return null;
@@ -512,16 +515,20 @@ export class PreAggregations {
512515
)
513516
));
514517

518+
const backAlias = (references) => references.map(r => (
519+
Array.isArray(r) ?
520+
[transformedQuery.allBackAliasMembers[r[0]] || r[0], r[1]] :
521+
transformedQuery.allBackAliasMembers[r] || r
522+
));
523+
515524
/**
516525
* Determine whether pre-aggregation can be used or not.
517526
* @param {*} references
518527
* @returns {boolean}
519528
*/
520529
const canUsePreAggregationNotAdditive = (references) => {
521530
const refTimeDimensions =
522-
references.sortedTimeDimensions ||
523-
sortTimeDimensions(references.timeDimensions);
524-
531+
backAlias(references.sortedTimeDimensions || sortTimeDimensions(references.timeDimensions));
525532
const qryTimeDimensions = references.allowNonStrictDateRangeMatch
526533
? transformedQuery.timeDimensions
527534
: transformedQuery.sortedTimeDimensions;
@@ -538,12 +545,13 @@ export class PreAggregations {
538545
) && (
539546
filterDimensionsSingleValueEqual &&
540547
references.dimensions.length === filterDimensionsSingleValueEqual.size &&
541-
R.all(d => filterDimensionsSingleValueEqual.has(d), references.dimensions) ||
548+
R.all(d => filterDimensionsSingleValueEqual.has(d), backAlias(references.dimensions)) ||
542549
transformedQuery.allFiltersWithinSelectedDimensions &&
543-
R.equals(references.sortedDimensions || references.dimensions, transformedQuery.sortedDimensions)
550+
R.equals(backAlias(references.sortedDimensions || references.dimensions), transformedQuery.sortedDimensions)
544551
) && (
545-
R.all(m => references.measures.indexOf(m) !== -1, transformedQuery.measures) ||
546-
R.all(m => references.measures.indexOf(m) !== -1, transformedQuery.leafMeasures)
552+
R.all(m => backAlias(references.measures).indexOf(m) !== -1, transformedQuery.measures) ||
553+
// TODO do we need backAlias here?
554+
R.all(m => backAlias(references.measures).indexOf(m) !== -1, transformedQuery.leafMeasures)
547555
));
548556
};
549557

@@ -649,7 +657,7 @@ export class PreAggregations {
649657
transformedQuery.leafMeasureAdditive &&
650658
!transformedQuery.hasMultipliedMeasures
651659
? (r) => canUsePreAggregationLeafMeasureAdditive(r) ||
652-
canUsePreAggregationNotAdditive(r)
660+
canUsePreAggregationNotAdditive(r)
653661
: canUsePreAggregationNotAdditive;
654662

655663
if (refs) {
@@ -661,25 +669,32 @@ export class PreAggregations {
661669

662670
static squashDimensions(query, flattenDimensionMembers) {
663671
return R.pipe(R.uniq, R.sortBy(R.identity))(
664-
PreAggregations.firstNonAliasMember(
665-
query,
666-
flattenDimensionMembers
667-
)
672+
flattenDimensionMembers.map(d => d.expressionPath())
668673
);
669674
}
670675

671-
static aliasedToFirstNonAliasedDimension(query, flattenDimensionMembers) {
672-
return flattenDimensionMembers
673-
.map(member => (
674-
{ [member.dimension]: PreAggregations.firstNonAliasMember(query, [member])[0] }
675-
)).reduce((a, b) => ({ ...a, ...b }), {});
676+
static flattenMembers(members) {
677+
return R.flatten(
678+
members.map(m => m.getMembers()),
679+
);
676680
}
677681

678682
static flattenDimensionMembers(query) {
679-
return R.flatten(
683+
return this.flattenMembers(
680684
query.dimensions
681685
.concat(query.filters)
682686
.concat(query.segments)
687+
);
688+
}
689+
690+
static flattenAllMembers(query) {
691+
return R.flatten(
692+
query.measures
693+
.concat(query.dimensions)
694+
.concat(query.segments)
695+
.concat(query.filters)
696+
.concat(query.measureFilters)
697+
.concat(query.timeDimensions)
683698
.map(m => m.getMembers()),
684699
);
685700
}

packages/cubejs-schema-compiler/src/compiler/CubeEvaluator.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export class CubeEvaluator extends CubeSymbols {
138138

139139
for (const memberName of Object.keys(members)) {
140140
let ownedByCube = true;
141-
let aliasMember = false;
141+
let aliasMember;
142142

143143
const member = members[memberName];
144144
if (member.sql && !member.subQuery) {
@@ -151,8 +151,8 @@ export class CubeEvaluator extends CubeSymbols {
151151
ownedByCube = false;
152152
}
153153
// Aliases one to one some another member as in case of views
154-
if (!ownedByCube && pathReferencesUsed.length === 1 && this.pathFromArray(pathReferencesUsed[0]) === evaluatedSql) {
155-
aliasMember = true;
154+
if (!ownedByCube && !member.filters && BaseQuery.isCalculatedMeasureType(member.type) && pathReferencesUsed.length === 1 && this.pathFromArray(pathReferencesUsed[0]) === evaluatedSql) {
155+
aliasMember = this.pathFromArray(pathReferencesUsed[0]);
156156
}
157157
const foreignCubes = cubeReferencesUsed.filter(usedCube => usedCube !== cube.name);
158158
if (foreignCubes.length > 0) {
@@ -164,7 +164,10 @@ export class CubeEvaluator extends CubeSymbols {
164164
errorReporter.error(`View '${cube.name}' defines own member '${cube.name}.${memberName}'. Please move this member definition to one of the cubes.`);
165165
}
166166

167-
members[memberName] = { ...members[memberName], ownedByCube, aliasMember };
167+
members[memberName] = { ...members[memberName], ownedByCube };
168+
if (aliasMember) {
169+
members[memberName].aliasMember = aliasMember;
170+
}
168171
}
169172
}
170173

0 commit comments

Comments
 (0)