Skip to content

Commit 9f154d0

Browse files
committed
build fullNames for rollupJoin/Lambda in the evaluatedPreAggregationObj()
1 parent de80e2f commit 9f154d0

File tree

1 file changed

+83
-99
lines changed

1 file changed

+83
-99
lines changed

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

Lines changed: 83 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ export type FullPreAggregationDescription = any;
9292
*/
9393
export type TransformedQuery = any;
9494

95+
type BuildRollupJoinResult = {
96+
rollupJoin: RollupJoin;
97+
existingJoins: JoinEdgeWithMembers[];
98+
};
99+
95100
export class PreAggregations {
96101
private readonly query: BaseQuery;
97102

@@ -521,18 +526,7 @@ export class PreAggregations {
521526
filterDimensionsSingleValueEqual =
522527
allValuesEq1(filterDimensionsSingleValueEqual) ? new Set(filterDimensionsSingleValueEqual?.keys()) : null;
523528

524-
// Build reverse query joins map, which is used for
525-
// rollupLambda and rollupJoin pre-aggs matching later
526-
const joinsMap: Record<string, string> = {};
527-
if (query.join) {
528-
for (const j of query.join.joins) {
529-
joinsMap[j.to] = j.from;
530-
}
531-
}
532-
533529
return {
534-
joinGraphRoot: query.join?.root,
535-
joinsMap,
536530
sortedDimensions,
537531
sortedTimeDimensions,
538532
timeDimensions,
@@ -651,16 +645,6 @@ export class PreAggregations {
651645
transformedQuery.allBackAliasMembers[r] || r
652646
));
653647

654-
const buildPath = (cube: string): string[] => {
655-
const path = [cube];
656-
const parentMap = transformedQuery.joinsMap;
657-
while (parentMap[cube]) {
658-
cube = parentMap[cube];
659-
path.push(cube);
660-
}
661-
return path.reverse();
662-
};
663-
664648
/**
665649
* Determine whether pre-aggregation can be used or not.
666650
*/
@@ -669,37 +653,9 @@ export class PreAggregations {
669653
? transformedQuery.timeDimensions
670654
: transformedQuery.sortedTimeDimensions;
671655

672-
let dimsToMatch: string[];
673-
let measToMatch: string[];
674-
let timeDimsToMatch: PreAggregationTimeDimensionReference[];
675-
676-
if (references.rollups.length > 0) {
677-
// In 'rollupJoin' / 'rollupLambda' pre-aggregations fullName members will be empty, because there are
678-
// no connections in the joinTree between cubes from different datasources
679-
// but joinGraph of the query has all the connections, necessary for serving the query,
680-
// so we use this information to complete the full paths of members from the root of the query
681-
// up to the pre-agg cube.
682-
// We use references from the underlying pre-aggregations, filtered with members existing in the root
683-
// pre-aggregation itself.
684-
685-
dimsToMatch = references.rollupsReferences
686-
.flatMap(rolRef => rolRef.fullNameDimensions)
687-
.filter(d => references.dimensions.some(rd => d.endsWith(rd)));
688-
timeDimsToMatch = references.rollupsReferences
689-
.flatMap(rolRef => rolRef.fullNameTimeDimensions)
690-
.filter(d => references.timeDimensions.some(rd => d.dimension.endsWith(rd.dimension)));
691-
measToMatch = references.rollupsReferences
692-
.flatMap(rolRef => rolRef.fullNameMeasures)
693-
.filter(m => references.measures.some(rm => m.endsWith(rm)));
694-
} else {
695-
dimsToMatch = references.fullNameDimensions;
696-
timeDimsToMatch = references.fullNameTimeDimensions;
697-
measToMatch = references.fullNameMeasures;
698-
}
699-
700-
const refTimeDimensions = backAlias(sortTimeDimensions(timeDimsToMatch));
701-
const backAliasMeasures = backAlias(measToMatch);
702-
const backAliasDimensions = backAlias(dimsToMatch);
656+
const refTimeDimensions = backAlias(sortTimeDimensions(references.fullNameTimeDimensions));
657+
const backAliasMeasures = backAlias(references.fullNameMeasures);
658+
const backAliasDimensions = backAlias(references.fullNameDimensions);
703659
return ((
704660
transformedQuery.hasNoTimeDimensionsWithoutGranularity
705661
) && (
@@ -716,9 +672,9 @@ export class PreAggregations {
716672
transformedQuery.allFiltersWithinSelectedDimensions &&
717673
R.equals(backAliasDimensions, transformedQuery.sortedDimensions)
718674
) && (
719-
R.all(m => backAliasMeasures.indexOf(m) !== -1, transformedQuery.measures) ||
675+
R.all(m => backAliasMeasures.includes(m), transformedQuery.measures) ||
720676
// TODO do we need backAlias here?
721-
R.all(m => backAliasMeasures.indexOf(m) !== -1, transformedQuery.leafMeasures)
677+
R.all(m => backAliasMeasures.includes(m), transformedQuery.leafMeasures)
722678
));
723679
};
724680

@@ -790,36 +746,8 @@ export class PreAggregations {
790746
}
791747
}
792748

793-
let dimsToMatch: string[];
794-
let timeDimsToMatch: PreAggregationTimeDimensionReference[];
795-
796-
if (references.rollups.length > 0) {
797-
// In 'rollupJoin' / 'rollupLambda' pre-aggregations fullName members will be empty, because there are
798-
// no connections in the joinTree between cubes from different datasources
799-
// but joinGraph of the query has all the connections, necessary for serving the query,
800-
// so we use this information to complete the full paths of members from the root of the query
801-
// up to the pre-agg cube.
802-
// We use references from the underlying pre-aggregations, filtered with members existing in the root
803-
// pre-aggregation itself.
804-
805-
dimsToMatch = references.rollupsReferences
806-
.flatMap(rolRef => rolRef.fullNameDimensions)
807-
.filter(d => references.dimensions.some(rd => d.endsWith(rd)))
808-
.map(d => {
809-
const [cube, ...restPath] = d.split('.');
810-
if (cube === transformedQuery.joinGraphRoot) {
811-
return d;
812-
}
813-
const path = buildPath(cube);
814-
return `${path.join('.')}.${restPath.join('.')}`;
815-
});
816-
timeDimsToMatch = references.rollupsReferences
817-
.flatMap(rolRef => rolRef.fullNameTimeDimensions)
818-
.filter(d => references.timeDimensions.some(rd => d.dimension.endsWith(rd.dimension)));
819-
} else {
820-
dimsToMatch = references.fullNameDimensions;
821-
timeDimsToMatch = references.fullNameTimeDimensions;
822-
}
749+
const dimsToMatch = references.fullNameDimensions;
750+
const timeDimsToMatch = references.fullNameTimeDimensions;
823751

824752
const dimensionsMatch = (dimensions, doBackAlias) => {
825753
const target = doBackAlias ? backAlias(dimsToMatch) : dimsToMatch;
@@ -1035,7 +963,7 @@ export class PreAggregations {
1035963
}
1036964

1037965
// TODO check multiplication factor didn't change
1038-
private buildRollupJoin(preAggObj: PreAggregationForQuery, preAggObjsToJoin: PreAggregationForQuery[]): RollupJoin {
966+
private buildRollupJoin(preAggObj: PreAggregationForQuery, preAggObjsToJoin: PreAggregationForQuery[]): BuildRollupJoinResult {
1039967
return this.query.cacheValue(
1040968
['buildRollupJoin', JSON.stringify(preAggObj), JSON.stringify(preAggObjsToJoin)],
1041969
() => {
@@ -1055,15 +983,22 @@ export class PreAggregations {
1055983

1056984
return hints;
1057985
}).flat();
1058-
const targetJoins = this.resolveJoinMembers(
1059-
this.query.joinGraph.buildJoin(
1060-
preAggJoinsJoinHints.concat(this.cubesFromPreAggregation(preAggObj))
1061-
)
986+
987+
const builtJoinTree = this.query.joinGraph.buildJoin(
988+
preAggJoinsJoinHints.concat(this.cubesFromPreAggregation(preAggObj))
1062989
);
1063-
const existingJoins = R.unnest(preAggObjsToJoin.map(
1064-
// TODO join hints?
1065-
p => this.resolveJoinMembers(this.query.joinGraph.buildJoin(this.cubesFromPreAggregation(p))!)
1066-
));
990+
991+
if (!builtJoinTree) {
992+
throw new UserError(`Can't build join tree for pre-aggregation ${preAggObj.cube}.${preAggObj.preAggregationName}`);
993+
}
994+
995+
const targetJoins = this.resolveJoinMembers(builtJoinTree);
996+
997+
// TODO join hints?
998+
const existingJoins = preAggObjsToJoin
999+
.map(p => this.resolveJoinMembers(this.query.joinGraph.buildJoin(this.cubesFromPreAggregation(p))!))
1000+
.flat();
1001+
10671002
const nonExistingJoins = targetJoins.filter(target => !existingJoins.find(
10681003
existing => existing.originalFrom === target.originalFrom &&
10691004
existing.originalTo === target.originalTo &&
@@ -1073,7 +1008,7 @@ export class PreAggregations {
10731008
if (!nonExistingJoins.length) {
10741009
throw new UserError(`Nothing to join in rollup join. Target joins ${JSON.stringify(targetJoins)} are included in existing rollup joins ${JSON.stringify(existingJoins)}`);
10751010
}
1076-
return nonExistingJoins.map(join => {
1011+
const rollupJoin = nonExistingJoins.map(join => {
10771012
const fromPreAggObj = this.preAggObjForJoin(preAggObjsToJoin, join.fromMembers, join);
10781013
const toPreAggObj = this.preAggObjForJoin(preAggObjsToJoin, join.toMembers, join);
10791014
return {
@@ -1082,6 +1017,11 @@ export class PreAggregations {
10821017
toPreAggObj
10831018
};
10841019
});
1020+
1021+
return {
1022+
rollupJoin,
1023+
existingJoins,
1024+
};
10851025
}
10861026
);
10871027
}
@@ -1142,8 +1082,8 @@ export class PreAggregations {
11421082
preAggregationName,
11431083
preAggregation,
11441084
cube,
1145-
// For rollupJoin and rollupLambda we need to pass references of the underlying rollups
1146-
// to canUsePreAggregation fn, which are collected later;
1085+
// For rollupJoin and rollupLambda we need to enrich references with data
1086+
// from the underlying rollups which are collected later;
11471087
canUsePreAggregation: preAggregation.type === 'rollup' ? canUsePreAggregation(references) : false,
11481088
references,
11491089
preAggregationId: `${cube}.${preAggregationName}`
@@ -1165,12 +1105,53 @@ export class PreAggregations {
11651105
preAggregationsToJoin.forEach(preAgg => {
11661106
references.rollupsReferences.push(preAgg.references);
11671107
});
1168-
const canUsePreAggregationResult = canUsePreAggregation(references);
1108+
const { rollupJoin, existingJoins } = this.buildRollupJoin(preAggObj, preAggregationsToJoin);
1109+
1110+
const joinsMap: Record<string, string> = {};
1111+
for (const j of rollupJoin) {
1112+
joinsMap[j.to] = j.from;
1113+
}
1114+
for (const j of existingJoins) {
1115+
joinsMap[j.to] = j.from;
1116+
}
1117+
1118+
const buildPath = (cubeName: string): string[] => {
1119+
const path = [cubeName];
1120+
const parentMap = joinsMap;
1121+
while (parentMap[cubeName]) {
1122+
cubeName = parentMap[cubeName];
1123+
path.push(cubeName);
1124+
}
1125+
return path.reverse();
1126+
};
1127+
1128+
references.fullNameDimensions = references.dimensions.map(d => {
1129+
const [cubeName, ...restPath] = d.split('.');
1130+
const path = buildPath(cubeName);
1131+
1132+
return `${path.join('.')}.${restPath.join('.')}`;
1133+
});
1134+
references.fullNameMeasures = references.measures.map(m => {
1135+
const [cubeName, ...restPath] = m.split('.');
1136+
const path = buildPath(cubeName);
1137+
1138+
return `${path.join('.')}.${restPath.join('.')}`;
1139+
});
1140+
references.fullNameTimeDimensions = references.timeDimensions.map(td => {
1141+
const [cubeName, ...restPath] = td.dimension.split('.');
1142+
const path = buildPath(cubeName);
1143+
1144+
return {
1145+
...td,
1146+
dimension: `${path.join('.')}.${restPath.join('.')}`,
1147+
};
1148+
});
1149+
11691150
return {
11701151
...preAggObj,
1171-
canUsePreAggregation: canUsePreAggregationResult,
1152+
canUsePreAggregation: canUsePreAggregation(references),
11721153
preAggregationsToJoin,
1173-
rollupJoin: canUsePreAggregationResult ? this.buildRollupJoin(preAggObj, preAggregationsToJoin) : null,
1154+
rollupJoin,
11741155
};
11751156
} else if (preAggregation.type === 'rollupLambda') {
11761157
// TODO evaluation optimizations. Should be cached or moved to compile time.
@@ -1217,6 +1198,9 @@ export class PreAggregations {
12171198
});
12181199
referencedPreAggregations.forEach(preAgg => {
12191200
references.rollupsReferences.push(preAgg.references);
1201+
references.fullNameDimensions.push(...preAgg.references.fullNameDimensions);
1202+
references.fullNameMeasures.push(...preAgg.references.fullNameMeasures);
1203+
references.fullNameTimeDimensions.push(...preAgg.references.fullNameTimeDimensions);
12201204
});
12211205
return {
12221206
...preAggObj,

0 commit comments

Comments
 (0)