Skip to content

Commit 7160283

Browse files
authored
fix(cube): Issue with rollup pre-aggragations matching for views (cube-js#10474)
1 parent 354942e commit 7160283

2 files changed

Lines changed: 246 additions & 8 deletions

File tree

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -784,9 +784,15 @@ export class PreAggregations {
784784
const canUse = ((
785785
windowGranularityMatches(references)
786786
) && (
787+
(references.rollups.length > 0 &&
787788
R.all(
788789
(m: string) => references.measures.indexOf(m) !== -1,
789-
references.rollups.length > 0 ? transformedQuery.leafMeasures : transformedQuery.leafMeasuresFullPaths,
790+
transformedQuery.leafMeasures,
791+
)
792+
)
793+
|| R.all(
794+
(m: string) => references.measures.indexOf(m) !== -1,
795+
transformedQuery.leafMeasuresFullPaths,
790796
) || (transformedQuery.isAdditive && R.all(
791797
m => backAliasMeasures.indexOf(m) !== -1,
792798
transformedQuery.measures,

packages/cubejs-schema-compiler/test/integration/postgres/pre-aggregations-calculated-measures.test.ts

Lines changed: 239 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
1-
import {
2-
getEnv,
3-
} from '@cubejs-backend/shared';
4-
import R from 'ramda';
5-
import { UserError } from '../../../src/compiler/UserError';
61
import { PostgresQuery } from '../../../src/adapter/PostgresQuery';
7-
import { prepareJsCompiler } from '../../unit/PrepareCompiler';
2+
import { prepareJsCompiler, prepareCompiler } from '../../unit/PrepareCompiler';
83
import { dbRunner } from './PostgresDBRunner';
94

10-
describe('PreAggregationsMultiStage', () => {
5+
describe('PreAggregationsCalulatedMeasures', () => {
116
jest.setTimeout(200000);
127

138
const { compiler, joinGraph, cubeEvaluator } = prepareJsCompiler(`
@@ -136,9 +131,246 @@ describe('PreAggregationsMultiStage', () => {
136131
137132
})
138133
134+
cube('facts', {
135+
sql: 'select * from visitor_checkins',
136+
sqlAlias: 'f',
137+
measures: {
138+
count: { type: 'count' },
139+
total_cost: { sql: 'id', type: 'sum' },
140+
avg_cost: { sql: \`\${CUBE.total_cost} / \${CUBE.count}\`, type: 'number' },
141+
},
142+
dimensions: {
143+
id: { type: 'number', sql: 'id', primaryKey: true },
144+
line_item_id: { type: 'number', sql: 'visitor_id' },
145+
day: { type: 'time', sql: 'created_at' },
146+
},
147+
preAggregations: {
148+
facts_rollup: {
149+
type: 'rollup',
150+
measures: [CUBE.count, CUBE.total_cost, CUBE.avg_cost],
151+
dimensions: [CUBE.line_item_id],
152+
timeDimension: CUBE.day,
153+
granularity: 'day',
154+
}
155+
}
156+
})
157+
158+
cube('line_items', {
159+
sql: 'select * from visitors',
160+
sqlAlias: 'li',
161+
joins: {
162+
facts: {
163+
relationship: 'one_to_many',
164+
sql: \`\${CUBE.id} = \${facts.line_item_id}\`
165+
},
166+
campaigns: {
167+
relationship: 'many_to_one',
168+
sql: \`\${CUBE.id} = \${campaigns.id}\`
169+
}
170+
},
171+
measures: {
172+
count: { type: 'count' }
173+
},
174+
dimensions: {
175+
id: { type: 'number', sql: 'id', primaryKey: true },
176+
name: { type: 'string', sql: 'source' },
177+
},
178+
preAggregations: {
179+
li_rollup: {
180+
type: 'rollup',
181+
dimensions: [CUBE.id, CUBE.name],
182+
},
183+
combined_rollup_join: {
184+
type: 'rollupJoin',
185+
measures: [line_items.facts.count, line_items.facts.total_cost, line_items.facts.avg_cost],
186+
dimensions: [CUBE.name, campaigns.campaign_name],
187+
timeDimension: line_items.facts.day,
188+
granularity: 'day',
189+
rollups: [campaigns.campaigns_rollup, facts.facts_rollup, CUBE.li_rollup],
190+
}
191+
}
192+
})
193+
194+
cube('campaigns', {
195+
sql: "select 1 as id, 'camp1' as campaign_name",
196+
sqlAlias: 'c',
197+
measures: {
198+
count: { type: 'count' }
199+
},
200+
dimensions: {
201+
id: { type: 'number', sql: 'id', primaryKey: true },
202+
campaign_name: { type: 'string', sql: 'campaign_name' },
203+
},
204+
preAggregations: {
205+
campaigns_rollup: {
206+
type: 'rollup',
207+
dimensions: [CUBE.id, CUBE.campaign_name],
208+
}
209+
}
210+
})
211+
212+
view('my_view', {
213+
cubes: [
214+
{ join_path: line_items.facts, includes: '*', prefix: true },
215+
{ join_path: line_items, includes: '*', prefix: true },
216+
{ join_path: line_items.campaigns, includes: '*', prefix: true },
217+
]
218+
})
139219
140220
`);
141221

222+
it('rollupJoin matching with additive measures through view', async () => {
223+
await compiler.compile();
224+
225+
const query = new PostgresQuery(
226+
{ joinGraph, cubeEvaluator, compiler },
227+
{
228+
measures: [
229+
'my_view.facts_count',
230+
'my_view.facts_total_cost',
231+
],
232+
timeDimensions: [{
233+
dimension: 'my_view.facts_day',
234+
granularity: 'day',
235+
}],
236+
timezone: 'America/Los_Angeles',
237+
preAggregationsSchema: '',
238+
}
239+
);
240+
241+
const matchedPreAgg = query.preAggregations?.findPreAggregationForQuery();
242+
243+
const sqlAndParams = query.buildSqlAndParams();
244+
expect(sqlAndParams[0]).toContain('campaigns_rollup');
245+
expect(sqlAndParams[0]).toContain('facts_rollup');
246+
expect(sqlAndParams[0]).toContain('li_rollup');
247+
expect(matchedPreAgg).toBeDefined();
248+
expect(matchedPreAgg?.preAggregationName).toEqual('combined_rollup_join');
249+
return dbRunner.evaluateQueryWithPreAggregations(query).then(res => {
250+
expect(res).toEqual(
251+
252+
[
253+
{
254+
my_view__facts_day_day: '2017-01-02T00:00:00.000Z',
255+
my_view__facts_count: '1',
256+
my_view__facts_total_cost: '1'
257+
},
258+
{
259+
my_view__facts_day_day: '2017-01-03T00:00:00.000Z',
260+
my_view__facts_count: '1',
261+
my_view__facts_total_cost: '2'
262+
},
263+
{
264+
my_view__facts_day_day: '2017-01-04T00:00:00.000Z',
265+
my_view__facts_count: '3',
266+
my_view__facts_total_cost: '12'
267+
},
268+
{
269+
my_view__facts_day_day: '2017-01-05T00:00:00.000Z',
270+
my_view__facts_count: '1',
271+
my_view__facts_total_cost: '6'
272+
},
273+
{
274+
my_view__facts_day_day: null,
275+
my_view__facts_count: null,
276+
my_view__facts_total_cost: null
277+
}
278+
]
279+
280+
);
281+
});
282+
});
283+
284+
it('rollupJoin matching with additive measures', async () => {
285+
await compiler.compile();
286+
287+
const query = new PostgresQuery(
288+
{ joinGraph, cubeEvaluator, compiler },
289+
{
290+
measures: [
291+
'facts.count',
292+
'facts.total_cost',
293+
],
294+
dimensions: ['line_items.name'],
295+
timezone: 'America/Los_Angeles',
296+
preAggregationsSchema: '',
297+
}
298+
);
299+
300+
const matchedPreAgg = query.preAggregations?.findPreAggregationForQuery();
301+
302+
const sqlAndParams = query.buildSqlAndParams();
303+
expect(sqlAndParams[0]).toContain('campaigns_rollup');
304+
expect(sqlAndParams[0]).toContain('facts_rollup');
305+
expect(sqlAndParams[0]).toContain('li_rollup');
306+
expect(matchedPreAgg).toBeDefined();
307+
expect(matchedPreAgg?.preAggregationName).toEqual('combined_rollup_join');
308+
return dbRunner.evaluateQueryWithPreAggregations(query).then(res => {
309+
expect(res).toEqual(
310+
311+
[
312+
{ li__name: null, f__count: null, f__total_cost: null },
313+
{ li__name: 'some', f__count: '5', f__total_cost: '15' },
314+
{ li__name: 'google', f__count: '1', f__total_cost: '6' }
315+
]
316+
317+
);
318+
});
319+
});
320+
321+
it('rollupJoin matching with calculated measures through view', async () => {
322+
await compiler.compile();
323+
324+
const query = new PostgresQuery(
325+
{ joinGraph, cubeEvaluator, compiler },
326+
{
327+
measures: [
328+
'my_view.facts_avg_cost',
329+
],
330+
timeDimensions: [{
331+
dimension: 'my_view.facts_day',
332+
granularity: 'day',
333+
}],
334+
timezone: 'America/Los_Angeles',
335+
preAggregationsSchema: '',
336+
}
337+
);
338+
339+
const matchedPreAgg = query.preAggregations?.findPreAggregationForQuery();
340+
341+
const sqlAndParams = query.buildSqlAndParams();
342+
expect(sqlAndParams[0]).toContain('campaigns_rollup');
343+
expect(sqlAndParams[0]).toContain('facts_rollup');
344+
expect(sqlAndParams[0]).toContain('li_rollup');
345+
expect(matchedPreAgg).toBeDefined();
346+
expect(matchedPreAgg?.preAggregationName).toEqual('combined_rollup_join');
347+
return dbRunner.evaluateQueryWithPreAggregations(query).then(res => {
348+
expect(res).toEqual(
349+
350+
[
351+
{
352+
my_view__facts_day_day: '2017-01-02T00:00:00.000Z',
353+
my_view__facts_avg_cost: '1.00000000000000000000'
354+
},
355+
{
356+
my_view__facts_day_day: '2017-01-03T00:00:00.000Z',
357+
my_view__facts_avg_cost: '2.0000000000000000'
358+
},
359+
{
360+
my_view__facts_day_day: '2017-01-04T00:00:00.000Z',
361+
my_view__facts_avg_cost: '4.0000000000000000'
362+
},
363+
{
364+
my_view__facts_day_day: '2017-01-05T00:00:00.000Z',
365+
my_view__facts_avg_cost: '6.0000000000000000'
366+
},
367+
{ my_view__facts_day_day: null, my_view__facts_avg_cost: null }
368+
]
369+
370+
);
371+
});
372+
});
373+
142374
it('calculated measure pre-aggregation', () => compiler.compile().then(() => {
143375
const query = new PostgresQuery({ joinGraph, cubeEvaluator, compiler }, {
144376
measures: [

0 commit comments

Comments
 (0)