Skip to content

Commit f17f49a

Browse files
authored
fix(tesseract): MultiStage group_by and reduce_by now work with views (#9710)
1 parent e2ee0cb commit f17f49a

File tree

3 files changed

+209
-15
lines changed

3 files changed

+209
-15
lines changed

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

Lines changed: 192 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,18 @@ describe('SQL Generation', () => {
271271
type: 'sum',
272272
group_by: []
273273
},
274+
visitors_revenue_per_source: {
275+
multi_stage: true,
276+
sql: \`\${revenue}\`,
277+
type: 'sum',
278+
group_by: [visitors.source]
279+
},
280+
visitors_revenue_without_date: {
281+
multi_stage: true,
282+
sql: \`\${revenue}\`,
283+
type: 'sum',
284+
reduce_by: [visitors.created_at]
285+
},
274286
percentage_of_total: {
275287
multi_stage: true,
276288
sql: \`(100 * \${revenue} / NULLIF(\${visitors_revenue_total}, 0))::int\`,
@@ -887,7 +899,6 @@ SELECT 1 AS revenue, cast('2024-01-01' AS timestamp) as time UNION ALL
887899
async function runQueryTest(q, expectedResult) {
888900
await compiler.compile();
889901
const query = new PostgresQuery({ joinGraph, cubeEvaluator, compiler }, q);
890-
891902
const res = await dbRunner.testQuery(query.buildSqlAndParams());
892903
console.log(JSON.stringify(res));
893904

@@ -3759,6 +3770,186 @@ SELECT 1 AS revenue, cast('2024-01-01' AS timestamp) as time UNION ALL
37593770
}]
37603771
));
37613772

3773+
it('multi stage sum with group by', async () => runQueryTest(
3774+
{
3775+
measures: ['visitors.visitors_revenue_per_source', 'visitors.revenue'],
3776+
dimensions: ['visitors.source', 'visitors.created_at'],
3777+
order: [{
3778+
id: 'visitors.source'
3779+
}, {
3780+
id: 'visitors.created_at'
3781+
}],
3782+
},
3783+
[{
3784+
visitors__source: 'google',
3785+
visitors__created_at: '2017-01-06T00:00:00.000Z',
3786+
visitors__visitors_revenue_per_source: '300',
3787+
visitors__revenue: '300'
3788+
},
3789+
{
3790+
visitors__source: 'some',
3791+
visitors__created_at: '2017-01-03T00:00:00.000Z',
3792+
visitors__visitors_revenue_per_source: '300',
3793+
visitors__revenue: '100'
3794+
},
3795+
{
3796+
visitors__source: 'some',
3797+
visitors__created_at: '2017-01-05T00:00:00.000Z',
3798+
visitors__visitors_revenue_per_source: '300',
3799+
visitors__revenue: '200'
3800+
},
3801+
{
3802+
visitors__source: null,
3803+
visitors__created_at: '2016-09-07T00:00:00.000Z',
3804+
visitors__visitors_revenue_per_source: '1400',
3805+
visitors__revenue: '500'
3806+
},
3807+
{
3808+
visitors__source: null,
3809+
visitors__created_at: '2017-01-07T00:00:00.000Z',
3810+
visitors__visitors_revenue_per_source: '1400',
3811+
visitors__revenue: '900'
3812+
}]
3813+
));
3814+
3815+
if (getEnv('nativeSqlPlanner')) {
3816+
it('multi stage sum with group by over view', async () => runQueryTest(
3817+
{
3818+
measures: ['visitors_multi_stage.visitors_revenue_per_source', 'visitors_multi_stage.revenue'],
3819+
dimensions: ['visitors_multi_stage.source', 'visitors_multi_stage.created_at'],
3820+
order: [{
3821+
id: 'visitors_multi_stage.source'
3822+
}, {
3823+
id: 'visitors_multi_stage.created_at'
3824+
}],
3825+
},
3826+
[{
3827+
visitors_multi_stage__source: 'google',
3828+
visitors_multi_stage__created_at: '2017-01-06T00:00:00.000Z',
3829+
visitors_multi_stage__visitors_revenue_per_source: '300',
3830+
visitors_multi_stage__revenue: '300'
3831+
},
3832+
{
3833+
visitors_multi_stage__source: 'some',
3834+
visitors_multi_stage__created_at: '2017-01-03T00:00:00.000Z',
3835+
visitors_multi_stage__visitors_revenue_per_source: '300',
3836+
visitors_multi_stage__revenue: '100'
3837+
},
3838+
{
3839+
visitors_multi_stage__source: 'some',
3840+
visitors_multi_stage__created_at: '2017-01-05T00:00:00.000Z',
3841+
visitors_multi_stage__visitors_revenue_per_source: '300',
3842+
visitors_multi_stage__revenue: '200'
3843+
},
3844+
{
3845+
visitors_multi_stage__source: null,
3846+
visitors_multi_stage__created_at: '2016-09-07T00:00:00.000Z',
3847+
visitors_multi_stage__visitors_revenue_per_source: '1400',
3848+
visitors_multi_stage__revenue: '500'
3849+
},
3850+
{
3851+
visitors_multi_stage__source: null,
3852+
visitors_multi_stage__created_at: '2017-01-07T00:00:00.000Z',
3853+
visitors_multi_stage__visitors_revenue_per_source: '1400',
3854+
visitors_multi_stage__revenue: '900'
3855+
}]
3856+
));
3857+
} else {
3858+
it.skip('multi stage sum with reduce by over view', async () => {
3859+
// Works only in Tesseract
3860+
});
3861+
}
3862+
3863+
it('multi stage sum with reduce by', async () => runQueryTest(
3864+
{
3865+
measures: ['visitors.visitors_revenue_without_date', 'visitors.revenue'],
3866+
dimensions: ['visitors.source', 'visitors.created_at'],
3867+
order: [{
3868+
id: 'visitors.source'
3869+
}, {
3870+
id: 'visitors.created_at'
3871+
}],
3872+
},
3873+
[{
3874+
visitors__source: 'google',
3875+
visitors__created_at: '2017-01-06T00:00:00.000Z',
3876+
visitors__visitors_revenue_without_date: '300',
3877+
visitors__revenue: '300'
3878+
},
3879+
{
3880+
visitors__source: 'some',
3881+
visitors__created_at: '2017-01-03T00:00:00.000Z',
3882+
visitors__visitors_revenue_without_date: '300',
3883+
visitors__revenue: '100'
3884+
},
3885+
{
3886+
visitors__source: 'some',
3887+
visitors__created_at: '2017-01-05T00:00:00.000Z',
3888+
visitors__visitors_revenue_without_date: '300',
3889+
visitors__revenue: '200'
3890+
},
3891+
{
3892+
visitors__source: null,
3893+
visitors__created_at: '2016-09-07T00:00:00.000Z',
3894+
visitors__visitors_revenue_without_date: '1400',
3895+
visitors__revenue: '500'
3896+
},
3897+
{
3898+
visitors__source: null,
3899+
visitors__created_at: '2017-01-07T00:00:00.000Z',
3900+
visitors__visitors_revenue_without_date: '1400',
3901+
visitors__revenue: '900'
3902+
}]
3903+
));
3904+
3905+
if (getEnv('nativeSqlPlanner')) {
3906+
it('multi stage sum with reduce by over view', async () => runQueryTest(
3907+
{
3908+
measures: ['visitors_multi_stage.visitors_revenue_without_date', 'visitors_multi_stage.revenue'],
3909+
dimensions: ['visitors_multi_stage.source', 'visitors_multi_stage.created_at'],
3910+
order: [{
3911+
id: 'visitors_multi_stage.source'
3912+
}, {
3913+
id: 'visitors_multi_stage.created_at'
3914+
}],
3915+
},
3916+
[{
3917+
visitors_multi_stage__source: 'google',
3918+
visitors_multi_stage__created_at: '2017-01-06T00:00:00.000Z',
3919+
visitors_multi_stage__visitors_revenue_without_date: '300',
3920+
visitors_multi_stage__revenue: '300'
3921+
},
3922+
{
3923+
visitors_multi_stage__source: 'some',
3924+
visitors_multi_stage__created_at: '2017-01-03T00:00:00.000Z',
3925+
visitors_multi_stage__visitors_revenue_without_date: '300',
3926+
visitors_multi_stage__revenue: '100'
3927+
},
3928+
{
3929+
visitors_multi_stage__source: 'some',
3930+
visitors_multi_stage__created_at: '2017-01-05T00:00:00.000Z',
3931+
visitors_multi_stage__visitors_revenue_without_date: '300',
3932+
visitors_multi_stage__revenue: '200'
3933+
},
3934+
{
3935+
visitors_multi_stage__source: null,
3936+
visitors_multi_stage__created_at: '2016-09-07T00:00:00.000Z',
3937+
visitors_multi_stage__visitors_revenue_without_date: '1400',
3938+
visitors_multi_stage__revenue: '500'
3939+
},
3940+
{
3941+
visitors_multi_stage__source: null,
3942+
visitors_multi_stage__created_at: '2017-01-07T00:00:00.000Z',
3943+
visitors_multi_stage__visitors_revenue_without_date: '1400',
3944+
visitors_multi_stage__revenue: '900'
3945+
}]
3946+
));
3947+
} else {
3948+
it.skip('multi stage sum with reduce by over view', async () => {
3949+
// Works only in Tesseract
3950+
});
3951+
}
3952+
37623953
it('multiplied sum and count no dimensions through view', async () => runQueryTest(
37633954
{
37643955
measures: ['visitors_visitors_checkins_view.revenue', 'visitors_visitors_checkins_view.visitor_checkins_count'],

rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -341,27 +341,15 @@ impl MultiStageMemberQueryPlanner {
341341
let dimensions = if !reduce_by.is_empty() {
342342
dimensions
343343
.into_iter()
344-
.filter(|d| {
345-
if reduce_by.iter().any(|m| d.full_name() == m.full_name()) {
346-
false
347-
} else {
348-
true
349-
}
350-
})
344+
.filter(|d| !reduce_by.iter().any(|m| d.has_member_in_reference_chain(m)))
351345
.collect_vec()
352346
} else {
353347
dimensions
354348
};
355349
let dimensions = if let Some(group_by) = group_by {
356350
dimensions
357351
.into_iter()
358-
.filter(|d| {
359-
if group_by.iter().any(|m| d.full_name() == m.full_name()) {
360-
true
361-
} else {
362-
false
363-
}
364-
})
352+
.filter(|d| group_by.iter().any(|m| d.has_member_in_reference_chain(m)))
365353
.collect_vec()
366354
} else {
367355
dimensions

rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,21 @@ impl MemberSymbol {
163163
current
164164
}
165165

166+
pub fn has_member_in_reference_chain(&self, member: &Rc<MemberSymbol>) -> bool {
167+
if self.full_name() == member.full_name() {
168+
return true;
169+
}
170+
171+
let mut current = self.reference_member();
172+
while let Some(reference) = current {
173+
if reference.full_name() == member.full_name() {
174+
return true;
175+
}
176+
current = reference.reference_member();
177+
}
178+
false
179+
}
180+
166181
pub fn owned_by_cube(&self) -> bool {
167182
match self {
168183
Self::Dimension(d) => d.owned_by_cube(),

0 commit comments

Comments
 (0)