Skip to content

Commit ef3a04b

Browse files
authored
fix(schema-compiler): Fix view queries with timeShift members (#9556)
* fix(schema-compiler): Fix view queries with timeShift members * add tests
1 parent 20dd8ad commit ef3a04b

File tree

2 files changed

+108
-5
lines changed

2 files changed

+108
-5
lines changed

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

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,14 +1310,14 @@ export class BaseQuery {
13101310
// TODO calculate based on remove_filter in future
13111311
const wouldNodeApplyFilters = !memberChildren[member];
13121312
let memberFrom = memberChildren[member]
1313-
?.map(child => this.multiStageWithQueries(child, this.childrenMultiStageContext(member, queryContext, wouldNodeApplyFilters), memberChildren, withQueries));
1313+
?.map(child => this.multiStageWithQueries(child, this.childrenMultiStageContext(member, queryContext), memberChildren, withQueries));
13141314
const unionFromDimensions = memberFrom ? R.uniq(R.flatten(memberFrom.map(f => f.dimensions))) : queryContext.dimensions;
13151315
const unionDimensionsContext = { ...queryContext, dimensions: unionFromDimensions.filter(d => !this.newDimension(d).isMultiStage()) };
13161316
// TODO is calling multiStageWithQueries twice optimal?
13171317
memberFrom = memberChildren[member] &&
13181318
R.uniqBy(
13191319
f => f.alias,
1320-
memberChildren[member].map(child => this.multiStageWithQueries(child, this.childrenMultiStageContext(member, unionDimensionsContext, wouldNodeApplyFilters), memberChildren, withQueries))
1320+
memberChildren[member].map(child => this.multiStageWithQueries(child, this.childrenMultiStageContext(member, unionDimensionsContext), memberChildren, withQueries))
13211321
);
13221322
const selfContext = this.selfMultiStageContext(member, queryContext, wouldNodeApplyFilters);
13231323
const subQuery = {
@@ -1344,7 +1344,7 @@ export class BaseQuery {
13441344
member.memberFrom?.forEach(m => this.collectUsedWithQueries(usedQueries, m));
13451345
}
13461346

1347-
childrenMultiStageContext(memberPath, queryContext, wouldNodeApplyFilters) {
1347+
childrenMultiStageContext(memberPath, queryContext) {
13481348
let member;
13491349
if (this.cubeEvaluator.isMeasure(memberPath)) {
13501350
member = this.newMeasure(memberPath);
@@ -1371,10 +1371,14 @@ export class BaseQuery {
13711371
};
13721372
};
13731373
} else {
1374+
const allBackAliasMembers = this.allBackAliasTimeDimensions();
13741375
mapFn = (td) => {
1375-
const timeShift = memberDef.timeShiftReferences.find(r => r.timeDimension === td.dimension);
1376+
const timeShift = memberDef.timeShiftReferences.find(r => r.timeDimension === td.dimension || td.dimension === allBackAliasMembers[r.timeDimension]);
13761377
if (timeShift) {
1377-
if (td.shiftInterval) {
1378+
// We need to ignore aliased td, because it will match and insert shiftInterval on first
1379+
// occurrence, but later during recursion it will hit the original td but shiftInterval will be
1380+
// present and simple check for td.shiftInterval will always result in error.
1381+
if (td.shiftInterval && !td.dimension === allBackAliasMembers[timeShift.timeDimension]) {
13781382
throw new UserError(`Hierarchical time shift is not supported but was provided for '${td.dimension}'. Parent time shift is '${td.shiftInterval}' and current is '${timeShift.interval}'`);
13791383
}
13801384
return {
@@ -4409,10 +4413,24 @@ export class BaseQuery {
44094413
);
44104414
}
44114415

4416+
/**
4417+
* @returns {Record<string, string>}
4418+
*/
4419+
allBackAliasTimeDimensions() {
4420+
const members = R.flatten(this.timeDimensions.map(m => m.getMembers()));
4421+
return this.backAliasMembers(members);
4422+
}
4423+
4424+
/**
4425+
* @returns {Record<string, string>}
4426+
*/
44124427
allBackAliasMembersExceptSegments() {
44134428
return this.backAliasMembers(this.flattenAllMembers(true));
44144429
}
44154430

4431+
/**
4432+
* @returns {Record<string, string>}
4433+
*/
44164434
allBackAliasMembers() {
44174435
return this.backAliasMembers(this.flattenAllMembers());
44184436
}

packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,34 @@ describe('SQL Generation', () => {
1717
sql: new Function('visitor_revenue', 'visitor_count', 'return visitor_revenue + "/" + visitor_count')
1818
}
1919
20+
cube(\`visitors_create_dates\`, {
21+
sql: \`
22+
select id AS create_date_id, created_at from visitors WHERE \${SECURITY_CONTEXT.source.filter('source')} AND
23+
\${SECURITY_CONTEXT.sourceArray.filter(sourceArray => \`source in (\${sourceArray.join(',')})\`)}
24+
\`,
25+
26+
rewriteQueries: true,
27+
28+
dimensions: {
29+
create_date_id: {
30+
type: 'number',
31+
sql: 'id',
32+
primaryKey: true
33+
},
34+
create_date_created_at: {
35+
type: 'time',
36+
sql: 'created_at',
37+
granularities: {
38+
three_days: {
39+
interval: '3 days',
40+
title: '3 days',
41+
origin: '2017-01-01'
42+
}
43+
}
44+
}
45+
}
46+
})
47+
2048
cube(\`visitors\`, {
2149
sql: \`
2250
select * from visitors WHERE \${SECURITY_CONTEXT.source.filter('source')} AND
@@ -33,6 +61,10 @@ describe('SQL Generation', () => {
3361
visitor_checkins: {
3462
relationship: 'hasMany',
3563
sql: \`\${CUBE}.id = \${visitor_checkins}.visitor_id\`
64+
},
65+
visitors_create_dates: {
66+
relationship: 'one_to_one',
67+
sql: \`\${CUBE}.id = \${visitors_create_dates}.create_date_id\`
3668
}
3769
},
3870
@@ -138,6 +170,16 @@ describe('SQL Generation', () => {
138170
type: 'prior',
139171
}]
140172
},
173+
revenue_day_ago_via_join: {
174+
multi_stage: true,
175+
type: 'sum',
176+
sql: \`\${revenue}\`,
177+
time_shift: [{
178+
time_dimension: visitors_create_dates.create_date_created_at,
179+
interval: '1 day',
180+
type: 'prior',
181+
}]
182+
},
141183
cagr_day: {
142184
multi_stage: true,
143185
sql: \`ROUND(100 * \${revenue} / NULLIF(\${revenue_day_ago}, 0))\`,
@@ -339,6 +381,10 @@ describe('SQL Generation', () => {
339381
cubes: [{
340382
join_path: 'visitors',
341383
includes: '*'
384+
},
385+
{
386+
join_path: 'visitors.visitors_create_dates',
387+
includes: '*'
342388
}]
343389
})
344390
@@ -1380,6 +1426,45 @@ SELECT 1 AS revenue, cast('2024-01-01' AS timestamp) as time UNION ALL
13801426
{ visitors__created_at_day: '2017-01-06T00:00:00.000Z', visitors__cagr_day: '300', visitors__revenue: '900', visitors__revenue_day_ago_no_td: '300' }
13811427
]));
13821428

1429+
it('CAGR via view (td from main cube)', async () => runQueryTest({
1430+
measures: [
1431+
'visitors_multi_stage.revenue',
1432+
'visitors_multi_stage.revenue_day_ago',
1433+
'visitors_multi_stage.cagr_day'
1434+
],
1435+
timeDimensions: [{
1436+
dimension: 'visitors_multi_stage.created_at',
1437+
granularity: 'day',
1438+
dateRange: ['2016-12-01', '2017-01-31']
1439+
}],
1440+
order: [{
1441+
id: 'visitors_multi_stage.created_at'
1442+
}],
1443+
timezone: 'America/Los_Angeles'
1444+
}, [
1445+
{ visitors_multi_stage__created_at_day: '2017-01-05T00:00:00.000Z', visitors_multi_stage__cagr_day: '150', visitors_multi_stage__revenue: '300', visitors_multi_stage__revenue_day_ago: '200' },
1446+
{ visitors_multi_stage__created_at_day: '2017-01-06T00:00:00.000Z', visitors_multi_stage__cagr_day: '300', visitors_multi_stage__revenue: '900', visitors_multi_stage__revenue_day_ago: '300' }
1447+
]));
1448+
1449+
it('CAGR via view (td from joined cube)', async () => runQueryTest({
1450+
measures: [
1451+
'visitors_multi_stage.revenue',
1452+
'visitors_multi_stage.revenue_day_ago_via_join',
1453+
],
1454+
timeDimensions: [{
1455+
dimension: 'visitors_multi_stage.create_date_created_at',
1456+
granularity: 'day',
1457+
dateRange: ['2016-12-01', '2017-01-31']
1458+
}],
1459+
order: [{
1460+
id: 'visitors_multi_stage.create_date_created_at'
1461+
}],
1462+
timezone: 'America/Los_Angeles'
1463+
}, [
1464+
{ visitors_multi_stage__create_date_created_at_day: '2017-01-05T00:00:00.000Z', visitors_multi_stage__revenue: '300', visitors_multi_stage__revenue_day_ago_via_join: '200' },
1465+
{ visitors_multi_stage__create_date_created_at_day: '2017-01-06T00:00:00.000Z', visitors_multi_stage__revenue: '900', visitors_multi_stage__revenue_day_ago_via_join: '300' }
1466+
]));
1467+
13831468
it('sql utils', async () => runQueryTest({
13841469
measures: [
13851470
'visitors.visitor_count'

0 commit comments

Comments
 (0)