Skip to content

Commit 6aec8a7

Browse files
KSDaemonigorlukanin
authored andcommitted
fix(schema-compiler): Fix pre-agg matching for 'rollupJoin' / 'rollupLambda' pre-aggregations (#10017)
* expose joinGraph from transformQueryToCanUseForm() * get rid of ramda in favor of simple js * preparing dimensionsMatch() * fix(schema-compiler): Fix pre-agg matching for 'rollupJoin' / 'rollupLambda' pre-aggregations * add tests # Conflicts: # packages/cubejs-schema-compiler/test/integration/postgres/pre-aggregations.test.ts * add test for 3-cube rollupJoin pre-agg * use rollupsReferences for 'rollupJoin' / 'rollupLambda' pre-agg matching * fix old tests with new required fields * more tests * implement pre-agg matching using pre-agg join subgraphs * fix incorrect cache for pre-aggs * fix canUsePreAggregationNotAdditive * skip test for tesseract * export type * more types * build fullNames for rollupJoin/Lambda in the evaluatedPreAggregationObj() * add memberShortNameFromPath() to Evaluator * fix type * get rid of references.fullName* in favor of fullpath-members * some refactoring to avoid copy/paste * add 'rollupJoin pre-aggregation matching with transitive joins' test * fix buildRollupJoin * fix resolveJoinMembers() * implement sortMembersByJoinTree() * fix joint hints collection for transitive joins * remove obsolete * revert back obsolete * fix joint hints collection for transitive joins * fix joint hints collection for transitive joins * update joinTreeForHints() with skipQueryJoinMap flag * fix buildRollupJoin() * fix datamodel for rollupJoin tests * simplify buildRollupJoin() and evaluatedPreAggregationObj() * fix tests * remove obsolete * add test for pre-agg with not-full paths * more types * improve error message in preAggObjForJoin()
1 parent 2594421 commit 6aec8a7

File tree

8 files changed

+996
-110
lines changed

8 files changed

+996
-110
lines changed

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

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -387,16 +387,16 @@ export class BaseQuery {
387387
}
388388

389389
/**
390-
* Is used by native
391390
* This function follows the same logic as in this.collectJoinHints()
392-
* @private
391+
* skipQueryJoinMap is used by PreAggregations to build join tree without user's query all members map
392+
* @public
393393
* @param {Array<(Array<string> | string)>} hints
394+
* @param { boolean } skipQueryJoinMap
394395
* @return {import('../compiler/JoinGraph').FinishedJoinTree}
395396
*/
396-
joinTreeForHints(hints) {
397-
const explicitJoinHintMembers = new Set(hints.filter(j => Array.isArray(j)).flat());
398-
const queryJoinMaps = this.queryJoinMap();
399-
const newCollectedHints = [];
397+
joinTreeForHints(hints, skipQueryJoinMap = false) {
398+
const queryJoinMaps = skipQueryJoinMap ? {} : this.queryJoinMap();
399+
let newCollectedHints = [];
400400

401401
const constructJH = () => R.uniq(this.enrichHintsWithJoinMap([
402402
...newCollectedHints,
@@ -421,16 +421,20 @@ export class BaseQuery {
421421
const iterationCollectedHints = joinMembersJoinHints.filter(j => !allJoinHintsFlatten.has(j));
422422
newJoinHintsCollectedCnt = iterationCollectedHints.length;
423423
cnt++;
424-
if (newJoin) {
425-
newCollectedHints.push(...joinMembersJoinHints.filter(j => !explicitJoinHintMembers.has(j)));
424+
if (newJoin && newJoin.joins.length > 0) {
425+
// Even if there is no join tree changes, we still
426+
// push correctly ordered join hints, collected from the resolving of members of join tree
427+
// upfront the all existing query members. This ensures the correct cube join order
428+
// with transitive joins even if they are already presented among query members.
429+
newCollectedHints = this.enrichedJoinHintsFromJoinTree(newJoin, joinMembersJoinHints);
426430
}
427431
} while (newJoin?.joins.length > 0 && !this.isJoinTreesEqual(prevJoin, newJoin) && cnt < 10000 && newJoinHintsCollectedCnt > 0);
428432

429433
if (cnt >= 10000) {
430434
throw new UserError('Can not construct joins for the query, potential loop detected');
431435
}
432436

433-
return newJoin;
437+
return this.joinGraph.buildJoin(constructJH());
434438
}
435439

436440
cacheValue(key, fn, { contextPropNames, inputProps, cache } = {}) {
@@ -505,6 +509,34 @@ export class BaseQuery {
505509
return joinMaps;
506510
}
507511

512+
/**
513+
* @private
514+
* @param { import('../compiler/JoinGraph').FinishedJoinTree } joinTree
515+
* @param { string[] } joinHints
516+
* @return { string[][] }
517+
*/
518+
enrichedJoinHintsFromJoinTree(joinTree, joinHints) {
519+
const joinsMap = {};
520+
521+
for (const j of joinTree.joins) {
522+
joinsMap[j.to] = j.from;
523+
}
524+
525+
return joinHints.map(jh => {
526+
let cubeName = jh;
527+
const path = [cubeName];
528+
while (joinsMap[cubeName]) {
529+
cubeName = joinsMap[cubeName];
530+
path.push(cubeName);
531+
}
532+
533+
if (path.length === 1) {
534+
return path[0];
535+
}
536+
return path.reverse();
537+
});
538+
}
539+
508540
/**
509541
* @private
510542
* @param { (string|string[])[] } hints
@@ -2666,10 +2698,9 @@ export class BaseQuery {
26662698
*/
26672699
collectJoinHints(excludeTimeDimensions = false) {
26682700
const allMembersJoinHints = this.collectJoinHintsFromMembers(this.allMembersConcat(excludeTimeDimensions));
2669-
const explicitJoinHintMembers = new Set(allMembersJoinHints.filter(j => Array.isArray(j)).flat());
26702701
const queryJoinMaps = this.queryJoinMap();
26712702
const customSubQueryJoinHints = this.collectJoinHintsFromMembers(this.joinMembersFromCustomSubQuery());
2672-
const newCollectedHints = [];
2703+
let newCollectedHints = [];
26732704

26742705
// One cube may join the other cube via transitive joined cubes,
26752706
// members from which are referenced in the join `on` clauses.
@@ -2703,8 +2734,12 @@ export class BaseQuery {
27032734
const iterationCollectedHints = joinMembersJoinHints.filter(j => !allJoinHintsFlatten.has(j));
27042735
newJoinHintsCollectedCnt = iterationCollectedHints.length;
27052736
cnt++;
2706-
if (newJoin) {
2707-
newCollectedHints.push(...joinMembersJoinHints.filter(j => !explicitJoinHintMembers.has(j)));
2737+
if (newJoin && newJoin.joins.length > 0) {
2738+
// Even if there is no join tree changes, we still
2739+
// push correctly ordered join hints, collected from the resolving of members of join tree
2740+
// upfront the all existing query members. This ensures the correct cube join order
2741+
// with transitive joins even if they are already presented among query members.
2742+
newCollectedHints = this.enrichedJoinHintsFromJoinTree(newJoin, joinMembersJoinHints);
27082743
}
27092744
} while (newJoin?.joins.length > 0 && !this.isJoinTreesEqual(prevJoin, newJoin) && cnt < 10000 && newJoinHintsCollectedCnt > 0);
27102745

0 commit comments

Comments
 (0)