Skip to content

Commit

Permalink
fix: Allow using sub query dimensions in join conditions (#2419)
Browse files Browse the repository at this point in the history
* fix: Allow using sub query dimensions in join conditions

* chore: Allow using sub query dimensions in join conditions: lint and TODO
  • Loading branch information
paveltiunov committed Mar 23, 2021
1 parent 9212fb9 commit 496a075
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 5 deletions.
23 changes: 18 additions & 5 deletions packages/cubejs-schema-compiler/src/adapter/BaseQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -840,19 +840,25 @@ export class BaseQuery {
}

joinQuery(join, subQueryDimensions) {
const subQueryDimensionsByCube = R.groupBy(d => this.cubeEvaluator.cubeNameFromPath(d), subQueryDimensions);
const joins = join.joins.map(
j => {
const [cubeSql, cubeAlias, conditions] = this.rewriteInlineCubeSql(j.originalTo, true);
return {
return [{
sql: cubeSql,
alias: cubeAlias,
on: `${this.evaluateSql(j.originalFrom, j.join.sql)}${conditions ? ` AND (${conditions})` : ''}`
};
// TODO handle the case when sub query referenced by a foreign cube on other side of a join
}].concat((subQueryDimensionsByCube[j.originalTo] || []).map(d => this.subQueryJoin(d)));
}
).concat(subQueryDimensions.map(d => this.subQueryJoin(d)));
).reduce((a, b) => a.concat(b), []);

const [cubeSql, cubeAlias] = this.rewriteInlineCubeSql(join.root);
return this.joinSql([{ sql: cubeSql, alias: cubeAlias }, ...joins]);
return this.joinSql([
{ sql: cubeSql, alias: cubeAlias },
...(subQueryDimensionsByCube[join.root] || []).map(d => this.subQueryJoin(d)),
...joins
]);
}

joinSql(toJoin) {
Expand Down Expand Up @@ -1122,7 +1128,14 @@ export class BaseQuery {
.concat(this.segments)
.concat(this.filters)
.concat(this.measureFilters)
.concat(excludeTimeDimensions ? [] : this.timeDimensions);
.concat(excludeTimeDimensions ? [] : this.timeDimensions)
.concat(this.join ? this.join.joins.map(j => ({
getMembers: () => [{
path: () => null,
cube: () => this.cubeEvaluator.cubeFromPath(j.originalFrom),
definition: () => j.join,
}]
})) : []);
return this.collectFrom(membersToCollectFrom, fn, methodName);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { PostgresQuery } from '../../../src/adapter/PostgresQuery';
import { prepareCompiler } from '../../unit/PrepareCompiler';
import { dbRunner } from './PostgresDBRunner';

describe('Sub Query Dimensions', () => {
jest.setTimeout(200000);

const { compiler, joinGraph, cubeEvaluator } = prepareCompiler(`
cube(\`A\`, {
sql: \`
SELECT 79 AS id, 1 AS foo_id UNION ALL
SELECT 80 AS id, 2 AS foo_id UNION ALL
SELECT 81 AS id, 3 AS foo_id UNION ALL
SELECT 82 AS id, 4 AS foo_id UNION ALL
SELECT 83 AS id, 5 AS foo_id UNION ALL
SELECT 84 AS id, 6 AS foo_id
\`,
measures: {
maxFooId: {
sql: \`foo_id\`,
type: 'max'
}
},
dimensions: {
id: {
sql: \`id\`,
type: \`number\`,
primaryKey: true,
},
fooId: {
sql: \`\${CUBE}.foo_id\`,
type: \`number\`,
},
},
});
cube(\`B\`, {
sql: \`
SELECT 100 AS id, 1 AS foo_id, 450 AS bar_id UNION ALL
SELECT 101 AS id, 2 AS foo_id, 450 AS bar_id UNION ALL
SELECT 102 AS id, 3 AS foo_id, 452 AS bar_id UNION ALL
SELECT 103 AS id, 4 AS foo_id, 452 AS bar_id UNION ALL
SELECT 104 AS id, 5 AS foo_id, 478 AS bar_id
\`,
joins: {
A: {
relationship: \`hasOne\`,
sql: \`\${A}.foo_id = \${B}.foo_id\`,
},
C: {
relationship: \`hasMany\`,
sql: \`\${B}.bar_id = \${C}.bar_id AND \${B.fooId} > 3\`,
},
},
measures: {
count: {
type: \`count\`
},
},
dimensions: {
id: {
sql: \`id\`,
type: \`number\`,
primaryKey: true,
shown: true
},
fooId: {
sql: \`\${A.maxFooId}\`,
type: \`number\`,
subQuery: true
}
}
});
cube(\`C\`, {
sql: \`
SELECT 789 AS id, 450 AS bar_id, 0.2 AS important_value UNION ALL
SELECT 790 AS id, 450 AS bar_id, 0.3 AS important_value UNION ALL
SELECT 791 AS id, 452 AS bar_id, 5.6 AS important_value UNION ALL
SELECT 792 AS id, 452 AS bar_id, 5.6 AS important_value UNION ALL
SELECT 793 AS id, 478 AS bar_id, 38.0 AS important_value UNION ALL
SELECT 794 AS id, 478 AS bar_id, 43.5 AS important_value
\`,
measures: {
importantValue: {
sql: \`important_value\`,
type: \`sum\`,
},
},
dimensions: {
id: {
sql: \`id\`,
type: \`number\`,
primaryKey: true,
},
barId: {
sql: \`\${CUBE}.bar_id\`,
type: \`number\`,
},
},
});
`);

async function runQueryTest(q, expectedResult) {
await compiler.compile();
const query = new PostgresQuery({ joinGraph, cubeEvaluator, compiler }, q);

console.log(query.buildSqlAndParams());

const res = await dbRunner.testQuery(query.buildSqlAndParams());
console.log(JSON.stringify(res));

expect(res).toEqual(
expectedResult
);
}

it('inserted at the right place of a join', async () => runQueryTest({
measures: ['C.importantValue'],
dimensions: [
'B.id'
],
order: [{ id: 'B.id' }]
}, [{
b__id: 100,
c__important_value: null,
}, {
b__id: 101,
c__important_value: null,
}, {
b__id: 102,
c__important_value: null,
}, {
b__id: 103,
c__important_value: '11.2',
}, {
b__id: 104,
c__important_value: '81.5',
}]));
});

0 comments on commit 496a075

Please sign in to comment.