Skip to content

Commit 1764481

Browse files
committed
fix(schema-compiler): Fix rendered references for preaggregations with join paths
1 parent 0012076 commit 1764481

File tree

2 files changed

+161
-53
lines changed

2 files changed

+161
-53
lines changed

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

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,21 +1393,24 @@ export class PreAggregations {
13931393
* @returns {Record<string, string>}
13941394
*/
13951395
measuresRenderedReference(preAggregationForQuery) {
1396-
return R.pipe(
1397-
R.map(path => {
1396+
const measures = this.rollupMeasures(preAggregationForQuery);
1397+
1398+
return Object.fromEntries(measures
1399+
.flatMap(path => {
13981400
const measure = this.query.newMeasure(path);
1399-
return [
1401+
const memberPath = this.query.cubeEvaluator.pathFromArray(measure.path());
1402+
const column = this.query.ungrouped ? measure.aliasName() : (this.query.aggregateOnGroupedColumn(
1403+
measure.measureDefinition(),
1404+
measure.aliasName(),
1405+
!this.query.safeEvaluateSymbolContext().overTimeSeriesAggregate,
14001406
path,
1401-
this.query.ungrouped ? measure.aliasName() : (this.query.aggregateOnGroupedColumn(
1402-
measure.measureDefinition(),
1403-
measure.aliasName(),
1404-
!this.query.safeEvaluateSymbolContext().overTimeSeriesAggregate,
1405-
path,
1406-
) || `sum(${measure.aliasName()})`),
1407+
) || `sum(${measure.aliasName()})`);
1408+
// Return both full join path and measure path
1409+
return [
1410+
[path, column],
1411+
[memberPath, column],
14071412
];
1408-
}),
1409-
R.fromPairs,
1410-
)(this.rollupMeasures(preAggregationForQuery));
1413+
}));
14111414
}
14121415

14131416
/**
@@ -1416,16 +1419,19 @@ export class PreAggregations {
14161419
* @returns {Record<string, string>}
14171420
*/
14181421
measureAliasesRenderedReference(preAggregationForQuery) {
1419-
return R.pipe(
1420-
R.map(path => {
1422+
const measures = this.rollupMeasures(preAggregationForQuery);
1423+
1424+
return Object.fromEntries(measures
1425+
.flatMap(path => {
14211426
const measure = this.query.newMeasure(path);
1427+
const memberPath = this.query.cubeEvaluator.pathFromArray(measure.path());
1428+
const alias = measure.aliasName();
1429+
// Return both full join path and measure path
14221430
return [
1423-
path,
1424-
measure.aliasName(),
1431+
[path, alias],
1432+
[memberPath, alias],
14251433
];
1426-
}),
1427-
R.fromPairs,
1428-
)(this.rollupMeasures(preAggregationForQuery));
1434+
}));
14291435
}
14301436

14311437
/**
@@ -1434,16 +1440,19 @@ export class PreAggregations {
14341440
* @returns {Record<string, string>}
14351441
*/
14361442
dimensionsRenderedReference(preAggregationForQuery) {
1437-
return R.pipe(
1438-
R.map(path => {
1443+
const dimensions = this.rollupDimensions(preAggregationForQuery);
1444+
1445+
return Object.fromEntries(dimensions
1446+
.flatMap(path => {
14391447
const dimension = this.query.newDimension(path);
1448+
const column = this.query.escapeColumnName(dimension.unescapedAliasName());
1449+
const memberPath = this.query.cubeEvaluator.pathFromArray(dimension.path());
1450+
// Return both full join path and dimension path
14401451
return [
1441-
path,
1442-
this.query.escapeColumnName(dimension.unescapedAliasName()),
1452+
[path, column],
1453+
[memberPath, column],
14431454
];
1444-
}),
1445-
R.fromPairs,
1446-
)(this.rollupDimensions(preAggregationForQuery));
1455+
}));
14471456
}
14481457

14491458
/**
@@ -1453,16 +1462,19 @@ export class PreAggregations {
14531462
* @returns {Record<string, string>}
14541463
*/
14551464
timeDimensionsRenderedReference(rollupGranularity, preAggregationForQuery) {
1456-
return R.pipe(
1457-
R.map((td) => {
1465+
const timeDimensions = this.rollupTimeDimensions(preAggregationForQuery);
1466+
1467+
return Object.fromEntries(timeDimensions
1468+
.flatMap(td => {
14581469
const timeDimension = this.query.newTimeDimension(td);
1470+
const column = this.query.escapeColumnName(timeDimension.unescapedAliasName(rollupGranularity));
1471+
const memberPath = this.query.cubeEvaluator.pathFromArray(timeDimension.path());
1472+
// Return both full join path and dimension path
14591473
return [
1460-
td.dimension,
1461-
this.query.escapeColumnName(timeDimension.unescapedAliasName(rollupGranularity)),
1474+
[td.dimension, column],
1475+
[memberPath, column],
14621476
];
1463-
}),
1464-
R.fromPairs,
1465-
)(this.rollupTimeDimensions(preAggregationForQuery));
1477+
}));
14661478
}
14671479

14681480
/**

packages/cubejs-schema-compiler/test/integration/postgres/multiple-join-paths.test.ts

Lines changed: 116 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { prepareJsCompiler } from '../../unit/PrepareCompiler';
33
import { DataSchemaCompiler } from '../../../src/compiler/DataSchemaCompiler';
44
import { JoinGraph } from '../../../src/compiler/JoinGraph';
55
import { CubeEvaluator } from '../../../src/compiler/CubeEvaluator';
6+
import { testWithPreAggregation } from './pre-aggregation-utils';
67

78
describe('Multiple join paths', () => {
89
jest.setTimeout(200000);
@@ -20,7 +21,8 @@ describe('Multiple join paths', () => {
2021
// └-->F-------┘
2122
// View, pre-aggregations and all interesting parts should use ADEX path
2223
// It should NOT be the shortest one from A to X (that's AFX), nor first in join edges declaration (that's ABCX)
23-
// All join conditions would be essentially `FALSE`, but with different syntax, to be able to test SQL generation
24+
// All join conditions would be essentially `TRUE` for ADEX joins and `FALSE` for everything else
25+
// But they would use different syntax, to be able to test SQL generation
2426
// Also, there should be only one way to cover cubes A and D with joins: A->D join
2527

2628
// TODO in this model queries like [A.a_id, X.x_id] become ambiguous, probably we want to handle this better
@@ -37,7 +39,7 @@ describe('Multiple join paths', () => {
3739
},
3840
D: {
3941
relationship: 'many_to_one',
40-
sql: "'A' = 'D'",
42+
sql: "'A' = 'D' OR TRUE",
4143
},
4244
F: {
4345
relationship: 'many_to_one',
@@ -78,7 +80,6 @@ describe('Multiple join paths', () => {
7880
a_id,
7981
A.D.d_id,
8082
A.D.d_name_for_join_paths,
81-
// D.d_id,
8283
A.D.E.X.x_id,
8384
],
8485
measures: [
@@ -93,6 +94,19 @@ describe('Multiple join paths', () => {
9394
granularity: 'day',
9495
},
9596
97+
adex_cumulative_with_join_paths: {
98+
type: 'rollup',
99+
dimensions: [
100+
a_id,
101+
A.D.E.X.x_id,
102+
],
103+
measures: [
104+
A.D.E.X.x_cumulative_sum,
105+
],
106+
timeDimension: A.D.E.X.x_time,
107+
granularity: 'day',
108+
},
109+
96110
ad_without_join_paths: {
97111
type: 'rollup',
98112
dimensions: [
@@ -183,7 +197,7 @@ describe('Multiple join paths', () => {
183197
joins: {
184198
E: {
185199
relationship: 'many_to_one',
186-
sql: "'D' = 'E'",
200+
sql: "'D' = 'E' OR TRUE",
187201
},
188202
},
189203
@@ -228,7 +242,7 @@ describe('Multiple join paths', () => {
228242
joins: {
229243
X: {
230244
relationship: 'many_to_one',
231-
sql: "'E' = 'X'",
245+
sql: "'E' = 'X' OR TRUE",
232246
},
233247
},
234248
@@ -319,6 +333,13 @@ describe('Multiple join paths', () => {
319333
sql: 'x_value',
320334
type: 'sum',
321335
},
336+
x_cumulative_sum: {
337+
sql: 'x_value',
338+
type: 'sum',
339+
rolling_window: {
340+
trailing: 'unbounded',
341+
},
342+
},
322343
},
323344
324345
segments: {
@@ -515,33 +536,108 @@ describe('Multiple join paths', () => {
515536
expect(preAggregation).toBeDefined();
516537
});
517538

518-
const preAggregationIds = [
519-
'A.adex_with_join_paths',
520-
'A.ad_without_join_paths',
539+
function makeReferenceQueryFor(preAggregationId: string, withDateRange: boolean = false): PostgresQuery {
540+
const preAggregations = cubeEvaluator.preAggregations({
541+
preAggregationIds: [preAggregationId]
542+
});
543+
544+
expect(preAggregations.length).toBe(1);
545+
const preAggregation = preAggregations[0];
546+
547+
if (withDateRange) {
548+
preAggregation.references.timeDimensions = preAggregation.references.timeDimensions.map(td => ({
549+
...td,
550+
dateRange: ['1970-01-01', '1970-01-02'],
551+
}));
552+
}
553+
554+
return new PostgresQuery({ joinGraph, cubeEvaluator, compiler }, {
555+
...preAggregation.references,
556+
preAggregationId: preAggregation.id,
557+
preAggregationsSchema: '',
558+
timezone: 'UTC',
559+
});
560+
}
561+
562+
const preAggregationTests = [
563+
{
564+
preAggregationId: 'A.adex_with_join_paths',
565+
addTimeRange: false,
566+
expectedData: [
567+
{
568+
a__a_id: 1,
569+
a__a_seg: false,
570+
a__a_sum: '100',
571+
d__d_id: 1,
572+
d__d_name_for_join_paths: 'foo',
573+
d__d_seg: false,
574+
x__x_id: 1,
575+
x__x_seg: false,
576+
x__x_time_day: '1970-01-01T00:00:00.000Z',
577+
},
578+
],
579+
},
580+
{
581+
preAggregationId: 'A.adex_cumulative_with_join_paths',
582+
addTimeRange: true,
583+
expectedData: [
584+
{
585+
a__a_id: 1,
586+
x__x_cumulative_sum: '100',
587+
x__x_id: 1,
588+
x__x_time_day: '1970-01-01T00:00:00.000Z',
589+
},
590+
{
591+
a__a_id: 1,
592+
x__x_cumulative_sum: '100',
593+
x__x_id: 1,
594+
x__x_time_day: '1970-01-02T00:00:00.000Z',
595+
},
596+
],
597+
},
598+
{
599+
preAggregationId: 'A.ad_without_join_paths',
600+
addTimeRange: false,
601+
expectedData: [
602+
{
603+
a__a_id: 1,
604+
a__a_seg: false,
605+
a__a_sum: '100',
606+
d__d_id: 1,
607+
d__d_name_for_no_join_paths: 'foo',
608+
d__d_seg: false,
609+
d__d_time_day: '1970-01-01T00:00:00.000Z',
610+
},
611+
],
612+
},
521613
];
522-
for (const preAggregationId of preAggregationIds) {
614+
for (const { preAggregationId, addTimeRange, expectedData } of preAggregationTests) {
523615
// eslint-disable-next-line no-loop-func
524616
it(`pre-aggregation ${preAggregationId} should match its own references`, async () => {
525-
const preAggregations = cubeEvaluator.preAggregations({});
617+
// Always not using range, because reference query would have no range to start from
618+
// but should match pre-aggregation anyway
619+
const query = makeReferenceQueryFor(preAggregationId);
526620

527-
const preAggregation = preAggregations
528-
.find(p => p.id === preAggregationId);
529-
if (preAggregation === undefined) {
530-
throw expect(preAggregation).toBeDefined();
621+
const preAggregationsDescription: any = query.preAggregations?.preAggregationsDescription();
622+
const preAggregationFromQuery = preAggregationsDescription.find(p => p.preAggregationId === preAggregationId);
623+
if (preAggregationFromQuery === undefined) {
624+
throw expect(preAggregationFromQuery).toBeDefined();
531625
}
626+
});
532627

533-
const query = new PostgresQuery({ joinGraph, cubeEvaluator, compiler }, {
534-
...preAggregation.references,
535-
preAggregationId: preAggregation.id,
536-
});
628+
// eslint-disable-next-line no-loop-func
629+
it(`pre-aggregation ${preAggregationId} reference query should be executable`, async () => {
630+
// Adding date range for rolling window measure
631+
const query = makeReferenceQueryFor(preAggregationId, addTimeRange);
537632

538633
const preAggregationsDescription: any = query.preAggregations?.preAggregationsDescription();
539-
const preAggregationFromQuery = preAggregationsDescription.find(p => p.preAggregationId === preAggregation.id);
634+
const preAggregationFromQuery = preAggregationsDescription.find(p => p.preAggregationId === preAggregationId);
540635
if (preAggregationFromQuery === undefined) {
541636
throw expect(preAggregationFromQuery).toBeDefined();
542637
}
543638

544-
expect(preAggregationFromQuery.preAggregationId).toBe(preAggregationId);
639+
const res = await testWithPreAggregation(preAggregationFromQuery, query);
640+
expect(res).toEqual(expectedData);
545641
});
546642
}
547643
});

0 commit comments

Comments
 (0)