Skip to content

Commit 913e939

Browse files
committed
make real full join name path member comparison for pre-agg matching
1 parent b9418ae commit 913e939

File tree

2 files changed

+58
-72
lines changed

2 files changed

+58
-72
lines changed

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

Lines changed: 52 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -390,15 +390,20 @@ export class PreAggregations {
390390
}
391391

392392
public static transformQueryToCanUseForm(query: BaseQuery): TransformedQuery {
393-
const flattenDimensionMembers = this.flattenDimensionMembers(query);
394-
const sortedDimensions = this.squashDimensions(flattenDimensionMembers);
395393
const allBackAliasMembers = query.allBackAliasMembers();
394+
const resolveFullMemberPath = query.resolveFullMemberPathFn();
395+
const flattenDimensionMembers = this.flattenDimensionMembers(query);
396+
const sortedDimensions = this.squashDimensions(flattenDimensionMembers).map(resolveFullMemberPath);
396397
const measures: (BaseMeasure | BaseFilter | BaseGroupFilter)[] = [...query.measures, ...query.measureFilters];
397-
const measurePaths = R.uniq(this.flattenMembers(measures).filter((m): m is BaseMeasure => m instanceof BaseMeasure).map(m => m.expressionPath()));
398+
const measurePaths = (R.uniq(
399+
this.flattenMembers(measures)
400+
.filter((m): m is BaseMeasure => m instanceof BaseMeasure)
401+
.map(m => m.expressionPath())
402+
)).map(resolveFullMemberPath);
398403
const collectLeafMeasures = query.collectLeafMeasures.bind(query);
399404
const dimensionsList = query.dimensions.map(dim => dim.expressionPath());
400405
const segmentsList = query.segments.map(s => s.expressionPath());
401-
const ownedDimensions = PreAggregations.ownedMembers(query, flattenDimensionMembers);
406+
const ownedDimensions = PreAggregations.ownedMembers(query, flattenDimensionMembers).map(resolveFullMemberPath);
402407
const ownedTimeDimensions = query.timeDimensions.map(td => {
403408
const owned = PreAggregations.ownedMembers(query, [td]);
404409
let { dimension } = td;
@@ -444,7 +449,8 @@ export class PreAggregations {
444449
return leafMeasures;
445450
}),
446451
R.unnest,
447-
R.uniq
452+
R.uniq,
453+
R.map(resolveFullMemberPath),
448454
)(measures);
449455

450456
function allValuesEq1(map) {
@@ -458,8 +464,10 @@ export class PreAggregations {
458464

459465
const sortedTimeDimensions = PreAggregations.sortTimeDimensionsWithRollupGranularity(query.timeDimensions);
460466
const timeDimensions = PreAggregations.timeDimensionsAsIs(query.timeDimensions);
461-
const ownedTimeDimensionsWithRollupGranularity = PreAggregations.sortTimeDimensionsWithRollupGranularity(ownedTimeDimensions);
462-
const ownedTimeDimensionsAsIs = PreAggregations.timeDimensionsAsIs(ownedTimeDimensions);
467+
const ownedTimeDimensionsWithRollupGranularity = PreAggregations.sortTimeDimensionsWithRollupGranularity(ownedTimeDimensions)
468+
.map(([d, g]) => [resolveFullMemberPath(d), g]);
469+
const ownedTimeDimensionsAsIs = PreAggregations.timeDimensionsAsIs(ownedTimeDimensions)
470+
.map(([d, g]) => [resolveFullMemberPath(d), g]);
463471

464472
const hasNoTimeDimensionsWithoutGranularity = !query.timeDimensions.filter(d => !d.granularity).length;
465473

@@ -577,31 +585,6 @@ export class PreAggregations {
577585
* for specified query, or its value for `refs` if specified.
578586
*/
579587
public static canUsePreAggregationForTransformedQueryFn(transformedQuery: TransformedQuery, refs: PreAggregationReferences | null = null): CanUsePreAggregationFn {
580-
function trimmedReferences(references: PreAggregationReferences): PreAggregationReferences {
581-
const timeDimensionsTrimmed = references
582-
.timeDimensions
583-
.map(td => ({
584-
...td,
585-
dimension: CubeSymbols.joinHintFromPath(td.dimension).path,
586-
}));
587-
const measuresTrimmed = references
588-
.measures
589-
.map(m => CubeSymbols.joinHintFromPath(m).path);
590-
const dimensionsTrimmed = references
591-
.dimensions
592-
.map(d => CubeSymbols.joinHintFromPath(d).path);
593-
const multipliedMeasuresTrimmed = references
594-
.multipliedMeasures?.map(m => CubeSymbols.joinHintFromPath(m).path) || [];
595-
596-
return {
597-
...references,
598-
dimensions: dimensionsTrimmed,
599-
measures: measuresTrimmed,
600-
timeDimensions: timeDimensionsTrimmed,
601-
multipliedMeasures: multipliedMeasuresTrimmed,
602-
};
603-
}
604-
605588
/**
606589
* Returns an array of 2-elements arrays with the dimension and granularity
607590
* sorted by the concatenated dimension + granularity key.
@@ -638,14 +621,12 @@ export class PreAggregations {
638621
* Determine whether pre-aggregation can be used or not.
639622
*/
640623
const canUsePreAggregationNotAdditive: CanUsePreAggregationFn = (references: PreAggregationReferences): boolean => {
641-
const referencesTrimmed = trimmedReferences(references);
642-
643-
const refTimeDimensions = backAlias(sortTimeDimensions(referencesTrimmed.timeDimensions));
624+
const refTimeDimensions = backAlias(sortTimeDimensions(references.timeDimensions));
644625
const qryTimeDimensions = references.allowNonStrictDateRangeMatch
645626
? transformedQuery.timeDimensions
646627
: transformedQuery.sortedTimeDimensions;
647-
const backAliasMeasures = backAlias(referencesTrimmed.measures);
648-
const backAliasDimensions = backAlias(referencesTrimmed.dimensions);
628+
const backAliasMeasures = backAlias(references.measures);
629+
const backAliasDimensions = backAlias(references.dimensions);
649630
return ((
650631
transformedQuery.hasNoTimeDimensionsWithoutGranularity
651632
) && (
@@ -657,7 +638,7 @@ export class PreAggregations {
657638
R.equals(transformedQuery.timeDimensions, refTimeDimensions)
658639
) && (
659640
filterDimensionsSingleValueEqual &&
660-
referencesTrimmed.dimensions.length === filterDimensionsSingleValueEqual.size &&
641+
references.dimensions.length === filterDimensionsSingleValueEqual.size &&
661642
R.all(d => filterDimensionsSingleValueEqual.has(d), backAliasDimensions) ||
662643
transformedQuery.allFiltersWithinSelectedDimensions &&
663644
R.equals(backAliasDimensions, transformedQuery.sortedDimensions)
@@ -705,7 +686,8 @@ export class PreAggregations {
705686
if (!resolvedGranularity) {
706687
return [[dimension, '*']]; // Any granularity should fit
707688
}
708-
return expandGranularity(dimension, resolvedGranularity)
689+
const trimmedDim = dimension.split('.').slice(-2).join('.');
690+
return expandGranularity(trimmedDim, resolvedGranularity)
709691
.map((newGranularity) => [dimension, newGranularity]);
710692
};
711693

@@ -722,30 +704,36 @@ export class PreAggregations {
722704
? transformedQuery.ownedTimeDimensionsAsIs.map(expandTimeDimension)
723705
: transformedQuery.ownedTimeDimensionsWithRollupGranularity.map(expandTimeDimension);
724706

725-
const referencesTrimmed = trimmedReferences(references);
726-
727707
// Even if there are no multiplied measures in the query (because no multiplier dimensions are requested)
728708
// but the same measures are multiplied in the pre-aggregation, we can't use pre-aggregation
729709
// for such queries.
730-
if (referencesTrimmed.multipliedMeasures) {
731-
const backAliasMultipliedMeasures = backAlias(referencesTrimmed.multipliedMeasures);
710+
if (references.multipliedMeasures) {
711+
const backAliasMultipliedMeasures = backAlias(references.multipliedMeasures);
732712

733-
if (transformedQuery.leafMeasures.some(m => referencesTrimmed.multipliedMeasures?.includes(m)) ||
713+
if (transformedQuery.leafMeasures.some(m => references.multipliedMeasures?.includes(m)) ||
734714
transformedQuery.measures.some(m => backAliasMultipliedMeasures.includes(m))
735715
) {
736716
return false;
737717
}
738718
}
739719

720+
// In 'rollupJoin' / 'rollupLambda' pre-aggregations fullName members will be empty, because there are
721+
// no connections in the joinTree between cubes from different datasources
722+
const dimsToMatch = references.fullNameDimensions.length > 0 ? references.fullNameDimensions : references.dimensions;
723+
740724
const dimensionsMatch = (dimensions, doBackAlias) => R.all(
741725
d => (
742726
doBackAlias ?
743-
backAlias(referencesTrimmed.dimensions) :
744-
(referencesTrimmed.dimensions)
727+
backAlias(dimsToMatch) :
728+
(dimsToMatch)
745729
).indexOf(d) !== -1,
746730
dimensions
747731
);
748732

733+
// In 'rollupJoin' / 'rollupLambda' pre-aggregations fullName members will be empty, because there are
734+
// no connections in the joinTree between cubes from different datasources
735+
const timeDimsToMatch = references.fullNameTimeDimensions.length > 0 ? references.fullNameTimeDimensions : references.timeDimensions;
736+
749737
const timeDimensionsMatch = (timeDimensionsList, doBackAlias) => R.allPass(
750738
timeDimensionsList.map(
751739
tds => R.anyPass(tds.map((td: [string, string]) => {
@@ -758,15 +746,15 @@ export class PreAggregations {
758746
)
759747
)(
760748
doBackAlias ?
761-
backAlias(sortTimeDimensions(referencesTrimmed.timeDimensions)) :
762-
(sortTimeDimensions(referencesTrimmed.timeDimensions))
749+
backAlias(sortTimeDimensions(timeDimsToMatch)) :
750+
(sortTimeDimensions(timeDimsToMatch))
763751
);
764752

765753
if (transformedQuery.ungrouped) {
766754
const allReferenceCubes = R.pipe(R.map((name: string) => name?.split('.')[0]), R.uniq, R.sortBy(R.identity))([
767-
...referencesTrimmed.measures,
768-
...referencesTrimmed.dimensions,
769-
...referencesTrimmed.timeDimensions.map(td => td.dimension),
755+
...references.measures.map(m => CubeSymbols.joinHintFromPath(m).path),
756+
...references.dimensions.map(d => CubeSymbols.joinHintFromPath(d).path),
757+
...references.timeDimensions.map(td => CubeSymbols.joinHintFromPath(td.dimension).path),
770758
]);
771759
if (
772760
!R.equals(transformedQuery.sortedAllCubeNames, allReferenceCubes) ||
@@ -778,12 +766,12 @@ export class PreAggregations {
778766
}
779767
}
780768

781-
const backAliasMeasures = backAlias(referencesTrimmed.measures);
769+
const backAliasMeasures = backAlias(references.measures);
782770
return ((
783771
windowGranularityMatches(references)
784772
) && (
785773
R.all(
786-
(m: string) => referencesTrimmed.measures.indexOf(m) !== -1,
774+
(m: string) => references.measures.indexOf(m) !== -1,
787775
transformedQuery.leafMeasures,
788776
) || R.all(
789777
m => backAliasMeasures.indexOf(m) !== -1,
@@ -1036,22 +1024,6 @@ export class PreAggregations {
10361024
);
10371025
}
10381026

1039-
private doesQueryAndPreAggJoinTreeMatch(references: PreAggregationReferences): boolean {
1040-
const { query } = this;
1041-
const preAggTree = references.joinTree || { joins: [] };
1042-
const preAggEdges = new Set(preAggTree.joins.map((e: JoinEdge) => `${e.from}->${e.to}`));
1043-
const queryTree = query.join;
1044-
1045-
for (const edge of queryTree.joins) {
1046-
const key = `${edge.from}->${edge.to}`;
1047-
if (!preAggEdges.has(key)) {
1048-
return false;
1049-
}
1050-
}
1051-
1052-
return true;
1053-
}
1054-
10551027
private evaluatedPreAggregationObj(
10561028
cube: string,
10571029
preAggregationName: string,
@@ -1063,8 +1035,7 @@ export class PreAggregations {
10631035
preAggregationName,
10641036
preAggregation,
10651037
cube,
1066-
// There are no connections in the joinTree between cubes from different datasources for 'rollupJoin' pre-aggs
1067-
canUsePreAggregation: canUsePreAggregation(references) && (preAggregation.type === 'rollupJoin' || this.doesQueryAndPreAggJoinTreeMatch(references)),
1038+
canUsePreAggregation: canUsePreAggregation(references),
10681039
references,
10691040
preAggregationId: `${cube}.${preAggregationName}`
10701041
};
@@ -1304,7 +1275,16 @@ export class PreAggregations {
13041275
const preAggQuery = this.query.preAggregationQueryForSqlEvaluation(cube, aggregation, { inPreAggEvaluation: true });
13051276
const aggregateMeasures = preAggQuery?.fullKeyQueryAggregateMeasures({ hasMultipliedForPreAggregation: true });
13061277
references.multipliedMeasures = aggregateMeasures?.multipliedMeasures?.map(m => m.measure);
1307-
references.joinTree = preAggQuery?.join;
1278+
if (preAggQuery) {
1279+
references.joinTree = preAggQuery.join;
1280+
const root = references.joinTree?.root || '';
1281+
references.fullNameMeasures = references.measures.map(m => (m.startsWith(root) ? m : `${root}.${m}`));
1282+
references.fullNameDimensions = references.dimensions.map(d => (d.startsWith(root) ? d : `${root}.${d}`));
1283+
references.fullNameTimeDimensions = references.timeDimensions.map(d => ({
1284+
dimension: (d.dimension.startsWith(root) ? d.dimension : `${root}.${d.dimension}`),
1285+
granularity: d.granularity,
1286+
}));
1287+
}
13081288
}
13091289
if (aggregation.type === 'rollupLambda') {
13101290
if (references.rollups.length > 0) {

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,11 @@ export type JoinTree = {
140140
export type PreAggregationReferences = {
141141
allowNonStrictDateRangeMatch?: boolean,
142142
dimensions: Array<string>,
143+
fullNameDimensions: Array<string>,
143144
measures: Array<string>,
145+
fullNameMeasures: Array<string>,
144146
timeDimensions: Array<PreAggregationTimeDimensionReference>,
147+
fullNameTimeDimensions: Array<PreAggregationTimeDimensionReference>,
145148
rollups: Array<string>,
146149
multipliedMeasures?: Array<string>,
147150
joinTree?: JoinTree;
@@ -838,6 +841,9 @@ export class CubeEvaluator extends CubeSymbols {
838841
timeDimensions,
839842
rollups:
840843
aggregation.rollupReferences && this.evaluateReferences(cube, aggregation.rollupReferences, { originalSorting: true }) || [],
844+
fullNameDimensions: [], // May be filled in PreAggregations.evaluateAllReferences()
845+
fullNameMeasures: [], // May be filled in PreAggregations.evaluateAllReferences()
846+
fullNameTimeDimensions: [], // May be filled in PreAggregations.evaluateAllReferences()
841847
};
842848
}
843849
}

0 commit comments

Comments
 (0)