Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
789a185
prepare test for non-match because of join tree difference
KSDaemon May 20, 2025
052405a
remove unused
KSDaemon May 20, 2025
2d5091c
fix some types
KSDaemon May 20, 2025
b1e5c2f
fix duplicates in join hint collection
KSDaemon May 21, 2025
2cdb003
Don't even try to match pre-agg if there are customSubQueryJoins
KSDaemon May 21, 2025
72965c4
Don't try to match pre-aggs if there are MemberExpressions
KSDaemon May 21, 2025
3fd739e
add joinTree evaluation for pre-agg
KSDaemon May 22, 2025
fe96c7b
more types and ts fixes
KSDaemon May 27, 2025
c6c1647
implement join tree comparison
KSDaemon May 27, 2025
7e4d52e
fix tests
KSDaemon May 27, 2025
f7bbeaa
fix for 'rollupJoin' pre-aggs
KSDaemon May 27, 2025
cc47643
remove comments
KSDaemon May 28, 2025
cd7b2fd
fix tests
KSDaemon Jun 12, 2025
2078122
fix aliasMember set for segments
KSDaemon Jun 12, 2025
2cbd158
fix expressionPath()
KSDaemon Jun 12, 2025
324e441
fix test
KSDaemon Jun 12, 2025
8caf00b
add resolveFullMemberPathFn() in BaseQuery
KSDaemon Jun 12, 2025
699c408
make real full join name path member comparison for pre-agg matching
KSDaemon Jun 12, 2025
540189f
fix tests
KSDaemon Jun 12, 2025
79759f1
fix tests
KSDaemon Jun 12, 2025
f2de033
add comments
KSDaemon Jun 13, 2025
1414e94
Add more tests
KSDaemon Jun 13, 2025
5c858ac
fix tesseract skip tests
KSDaemon Jun 26, 2025
0509abf
mark some tests as skipped for tesseract
KSDaemon Jun 27, 2025
ce2c107
sync tests
KSDaemon Aug 22, 2025
1b23158
remove obsolete types
KSDaemon Aug 22, 2025
3c5de16
remove comment
KSDaemon Aug 22, 2025
39c2711
fix tests
KSDaemon Aug 22, 2025
09097d0
fix tests
KSDaemon Aug 22, 2025
1234579
skip tests for tesseract
KSDaemon Aug 22, 2025
abcb683
useful comment
KSDaemon Aug 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ export class BaseMeasure {

public expressionPath(): string {
if (this.expression) {
return `expr:${this.expression.expressionName}`;
return `expr:${this.expressionName}`;
}

const path = this.path();
Expand Down
118 changes: 108 additions & 10 deletions packages/cubejs-schema-compiler/src/adapter/BaseQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,7 @@ export class BaseQuery {
/** @type {import('./BaseTimeDimension').BaseTimeDimension[]} */
timeDimensions;

/**
* @type {import('../compiler/JoinGraph').FinishedJoinTree}
*/
/** @type {import('../compiler/JoinGraph').FinishedJoinTree} */
join;

/**
Expand Down Expand Up @@ -332,6 +330,9 @@ export class BaseQuery {
}).filter(R.identity).map(this.newTimeDimension.bind(this));
this.allFilters = this.timeDimensions.concat(this.segments).concat(this.filters);
/**
* For now this might come only from SQL API, it might be some queries that uses measures and filters to
* get the dimensions that are then used as join conditions to get the final results.
* As consequence - if there are such sub query joins - pre-aggregations can't be used.
* @type {Array<{sql: string, on: {expression: Function}, joinType: 'LEFT' | 'INNER', alias: string}>}
*/
this.customSubQueryJoins = this.options.subqueryJoins ?? [];
Expand Down Expand Up @@ -365,6 +366,17 @@ export class BaseQuery {
try {
// TODO allJoinHints should contain join hints form pre-agg
this.join = this.joinGraph.buildJoin(this.allJoinHints);
/**
* @type {Record<string, string[]>}
*/
const queryJoinGraph = {};
for (const { originalFrom, originalTo } of (this.join?.joins || [])) {
if (!queryJoinGraph[originalFrom]) {
queryJoinGraph[originalFrom] = [];
}
queryJoinGraph[originalFrom].push(originalTo);
}
this.joinGraphPaths = queryJoinGraph || {};
} catch (e) {
if (this.useNativeSqlPlanner) {
// Tesseract doesn't require join to be prebuilt and there's a case where single join can't be built for multi-fact query
Expand Down Expand Up @@ -420,7 +432,7 @@ export class BaseQuery {

/**
*
* @returns {Array<Array<string>>}
* @returns {Array<string | Array<string>>}
*/
get allJoinHints() {
if (!this.collectedJoinHints) {
Expand Down Expand Up @@ -708,7 +720,9 @@ export class BaseQuery {
if (this.from) {
return this.simpleQuery();
}
if (!this.options.preAggregationQuery) {
const hasMemberExpressions = this.allMembersConcat(false).some(m => m.isMemberExpression);

if (!this.options.preAggregationQuery && !this.customSubQueryJoins.length && !hasMemberExpressions) {
preAggForQuery =
this.preAggregations.findPreAggregationForQuery();
if (this.options.disableExternalPreAggregations && preAggForQuery?.preAggregation.external) {
Expand All @@ -720,8 +734,6 @@ export class BaseQuery {
multipliedMeasures,
regularMeasures,
cumulativeMeasures,
withQueries,
multiStageMembers,
} = this.fullKeyQueryAggregateMeasures();

if (cumulativeMeasures.length === 0) {
Expand Down Expand Up @@ -785,7 +797,7 @@ export class BaseQuery {
externalPreAggregationQuery() {
if (!this.options.preAggregationQuery && !this.options.disableExternalPreAggregations && this.externalQueryClass) {
const preAggregationForQuery = this.preAggregations.findPreAggregationForQuery();
if (preAggregationForQuery && preAggregationForQuery.preAggregation.external) {
if (preAggregationForQuery?.preAggregation.external) {
return true;
}
const preAggregationsDescription = this.preAggregations.preAggregationsDescription();
Expand Down Expand Up @@ -3810,7 +3822,7 @@ export class BaseQuery {

/**
*
* @param options
* @param {unknown} options
* @returns {this}
*/
newSubQuery(options) {
Expand Down Expand Up @@ -4964,7 +4976,10 @@ export class BaseQuery {
*/
backAliasMembers(members) {
const query = this;
return Object.fromEntries(members.flatMap(

const buildJoinPath = this.buildJoinPathFn();

const aliases = Object.fromEntries(members.flatMap(
member => {
const collectedMembers = query.evaluateSymbolSqlWithContext(
() => query.collectFrom([member], query.collectMemberNamesFor.bind(query), 'collectMemberNamesFor'),
Expand All @@ -4982,5 +4997,88 @@ export class BaseQuery {
.map(d => [query.cubeEvaluator.byPathAnyType(d).aliasMember, memberPath]);
}
));

/**
* @type {Record<string, string>}
*/
const res = {};
for (const [original, alias] of Object.entries(aliases)) {
const [cube, field] = original.split('.');
const path = buildJoinPath(cube);

const [aliasCube, aliasField] = alias.split('.');
const aliasPath = aliasCube !== cube ? buildJoinPath(aliasCube) : path;

if (path) {
res[`${path}.${field}`] = aliasPath ? `${aliasPath}.${aliasField}` : alias;
}

// Aliases might come from proxied members, in such cases
// we need to map them to originals too
if (aliasPath) {
res[original] = `${aliasPath}.${aliasField}`;
}
}

return res;
}

buildJoinPathFn() {
const query = this;
const { root } = this.join || {};

return (target) => {
const visited = new Set();
const path = [];

/**
* @param {string} node
* @returns {boolean}
*/
function dfs(node) {
if (node === target) {
path.push(node);
return true;
}

if (visited.has(node)) return false;
visited.add(node);

const neighbors = query.joinGraphPaths[node] || [];
for (const neighbor of neighbors) {
if (dfs(neighbor)) {
path.unshift(node);
return true;
}
}

return false;
}

return dfs(root) ? path.join('.') : null;
};
}

/**
* Returns a function that constructs the full member path
* based on the query's join structure.
* @returns {(function(member: string): (string))}
*/
resolveFullMemberPathFn() {
const { root: queryJoinRoot } = this.join || {};

const buildJoinPath = this.buildJoinPathFn();

return (member) => {
const [cube, field] = member.split('.');
if (!cube || !field) return member;

if (cube === queryJoinRoot.root) {
return member;
}

const path = buildJoinPath(cube);
return path ? `${path}.${field}` : member;
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class BaseSegment {

public expressionPath(): string {
if (this.expression) {
return `expr:${this.expression.expressionName}`;
return `expr:${this.expressionName}`;
}

const path = this.path();
Expand Down
Loading
Loading