Skip to content

Commit 9360059

Browse files
committed
optimization
1 parent 8c9a5ae commit 9360059

File tree

19 files changed

+338
-120
lines changed

19 files changed

+338
-120
lines changed

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4214,7 +4214,14 @@ export class BaseQuery {
42144214
'{{ min_expr }} as {{ quoted_min_name }}\n' +
42154215
'FROM {{ from_prepared }}\n' +
42164216
'{% if filter %}WHERE {{ filter }}{% endif %}',
4217-
calc_groups_join: 'SELECT "{{ original_cube }}".*, "{{ groups | map(attribute=\'name\') | join(\'", "\') }}"\n' +
4217+
calc_groups_join: 'SELECT "{{ original_cube }}".*, ' +
4218+
'{%for single_value in single_values %}' +
4219+
'\'{{ single_value.value }}\' as "{{ single_value.name }}"{% if not loop.last %}, {% endif %}' +
4220+
'{% endfor %}' +
4221+
'{% if single_values and groups %}, {% endif %}' +
4222+
'{%for group in groups %}' +
4223+
'"{{ group.name }}"{% if not loop.last %}, {% endif %}' +
4224+
'{% endfor %}' +
42184225
'FROM {{ original_cube_sql }} {{ original_cube }}\n' +
42194226
'{% for group in groups %}' +
42204227
'CROSS JOIN\n' +

packages/cubejs-schema-compiler/test/integration/postgres/calc-groups.test.ts

Lines changed: 81 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -442,48 +442,52 @@ views:
442442
],
443443
{ joinGraph, cubeEvaluator, compiler }));
444444

445-
it('basic cross join with measure with filters', async () => dbRunner.runQueryTest({
446-
dimensions: ['orders.strategy'],
447-
measures: ['orders.revenue'],
448-
timeDimensions: [
445+
it('basic cross join with filters', async () => {
446+
const sqlAndParams = await dbRunner.runQueryTest({
447+
dimensions: ['orders.strategy'],
448+
measures: ['orders.revenue'],
449+
timeDimensions: [
450+
{
451+
dimension: 'orders.date',
452+
granularity: 'year'
453+
}
454+
],
455+
filters: [
456+
{ dimension: 'orders.strategy', operator: 'equals', values: ['B'] }
457+
],
458+
timezone: 'UTC',
459+
order: [{
460+
id: 'orders.date'
461+
}, {
462+
id: 'orders.currency'
463+
},
464+
],
465+
}, [
449466
{
450-
dimension: 'orders.date',
451-
granularity: 'year'
467+
orders__date_year: '2022-01-01T00:00:00.000Z',
468+
orders__strategy: 'B',
469+
orders__revenue: '5',
470+
},
471+
{
472+
orders__date_year: '2023-01-01T00:00:00.000Z',
473+
orders__strategy: 'B',
474+
orders__revenue: '15',
475+
},
476+
{
477+
orders__date_year: '2024-01-01T00:00:00.000Z',
478+
orders__strategy: 'B',
479+
orders__revenue: '30',
480+
},
481+
{
482+
orders__date_year: '2025-01-01T00:00:00.000Z',
483+
orders__strategy: 'B',
484+
orders__revenue: '5',
452485
}
453486
],
454-
filters: [
455-
{ dimension: 'orders.strategy', operator: 'equals', values: ['B'] }
456-
],
457-
timezone: 'UTC',
458-
order: [{
459-
id: 'orders.date'
460-
}, {
461-
id: 'orders.currency'
462-
},
463-
],
464-
}, [
465-
{
466-
orders__date_year: '2022-01-01T00:00:00.000Z',
467-
orders__strategy: 'B',
468-
orders__revenue: '5',
469-
},
470-
{
471-
orders__date_year: '2023-01-01T00:00:00.000Z',
472-
orders__strategy: 'B',
473-
orders__revenue: '15',
474-
},
475-
{
476-
orders__date_year: '2024-01-01T00:00:00.000Z',
477-
orders__strategy: 'B',
478-
orders__revenue: '30',
479-
},
480-
{
481-
orders__date_year: '2025-01-01T00:00:00.000Z',
482-
orders__strategy: 'B',
483-
orders__revenue: '5',
484-
}
485-
],
486-
{ joinGraph, cubeEvaluator, compiler }));
487+
{ joinGraph, cubeEvaluator, compiler });
488+
489+
expect(sqlAndParams[0]).not.toMatch(/CROSS.+JOIN/);
490+
});
487491

488492
it('dimension switch expression simple', async () => dbRunner.runQueryTest({
489493
dimensions: ['orders.currency', 'orders.currency_full_name'],
@@ -599,41 +603,46 @@ views:
599603
],
600604
{ joinGraph, cubeEvaluator, compiler }));
601605

602-
it('measure switch with filter', async () => dbRunner.runQueryTest({
603-
dimensions: ['orders.currency'],
604-
measures: ['orders.amount_usd', 'orders.amount_in_currency'],
605-
timeDimensions: [
606+
it('measure switch with filter', async () => {
607+
const sqlAndParams = await dbRunner.runQueryTest({
608+
dimensions: ['orders.currency'],
609+
measures: ['orders.amount_usd', 'orders.amount_in_currency'],
610+
timeDimensions: [
611+
{
612+
dimension: 'orders.date',
613+
granularity: 'year',
614+
dateRange: ['2024-01-01', '2026-01-01']
615+
}
616+
],
617+
filters: [
618+
{ dimension: 'orders.currency', operator: 'equals', values: ['EUR'] }
619+
],
620+
timezone: 'UTC',
621+
order: [{
622+
id: 'orders.date'
623+
}, {
624+
id: 'orders.currency'
625+
},
626+
],
627+
}, [
606628
{
607-
dimension: 'orders.date',
608-
granularity: 'year',
609-
dateRange: ['2024-01-01', '2026-01-01']
610-
}
611-
],
612-
filters: [
613-
{ dimension: 'orders.currency', operator: 'equals', values: ['EUR'] }
614-
],
615-
timezone: 'UTC',
616-
order: [{
617-
id: 'orders.date'
618-
}, {
619-
id: 'orders.currency'
620-
},
629+
orders__currency: 'EUR',
630+
orders__date_year: '2024-01-01T00:00:00.000Z',
631+
orders__amount_usd: '1030.0',
632+
orders__amount_in_currency: '1002'
633+
},
634+
{
635+
orders__currency: 'EUR',
636+
orders__date_year: '2025-01-01T00:00:00.000Z',
637+
orders__amount_usd: '40.0',
638+
orders__amount_in_currency: '38'
639+
},
621640
],
622-
}, [
623-
{
624-
orders__currency: 'EUR',
625-
orders__date_year: '2024-01-01T00:00:00.000Z',
626-
orders__amount_usd: '1030.0',
627-
orders__amount_in_currency: '1002'
628-
},
629-
{
630-
orders__currency: 'EUR',
631-
orders__date_year: '2025-01-01T00:00:00.000Z',
632-
orders__amount_usd: '40.0',
633-
orders__amount_in_currency: '38'
634-
},
635-
],
636-
{ joinGraph, cubeEvaluator, compiler }));
641+
{ joinGraph, cubeEvaluator, compiler });
642+
643+
expect(sqlAndParams[0]).not.toMatch(/CASE/);
644+
expect(sqlAndParams[0]).not.toMatch(/CROSS.+JOIN/);
645+
});
637646
} else {
638647
// This test is working only in tesseract
639648
test.skip('calc groups testst', () => { expect(1).toBe(1); });

packages/cubejs-schema-compiler/test/integration/utils/BaseDbRunner.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,15 @@ export class BaseDbRunner {
2626
const query = this.newTestQuery({ joinGraph, cubeEvaluator, compiler }, q);
2727

2828
console.log(query.buildSqlAndParams());
29+
const sqlAndParams = query.buildSqlAndParams();
2930

30-
const res = await this.testQuery(query.buildSqlAndParams());
31+
const res = await this.testQuery(sqlAndParams);
3132
console.log(JSON.stringify(res));
32-
console.log('!!! res: ', res);
3333

3434
expect(res).toEqual(
3535
expectedResult
3636
);
37+
return sqlAndParams;
3738
}
3839

3940
public async testQueries(queries, fixture: any = null) {

rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/case_item.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use super::string_or_sql::StringOrSql;
21
use super::member_sql::{MemberSql, NativeMemberSql};
2+
use super::string_or_sql::StringOrSql;
33
use cubenativeutils::wrappers::serializer::{
44
NativeDeserialize, NativeDeserializer, NativeSerialize,
55
};

rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/case_switch_definition.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use crate::cube_bridge::member_sql::{MemberSql, NativeMemberSql};
22

33
use super::case_switch_else_item::{CaseSwitchElseItem, NativeCaseSwitchElseItem};
44
use super::case_switch_item::{CaseSwitchItem, NativeCaseSwitchItem};
5-
use super::string_or_sql::StringOrSql;
65
use cubenativeutils::wrappers::serializer::{
76
NativeDeserialize, NativeDeserializer, NativeSerialize,
87
};

rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/case_variant.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::cube_bridge::case_switch_definition::{
33
CaseSwitchDefinition, NativeCaseSwitchDefinition,
44
};
55

6-
use super::struct_with_sql_member::{NativeStructWithSqlMember, StructWithSqlMember};
76
use cubenativeutils::wrappers::inner_types::InnerTypes;
87
use cubenativeutils::wrappers::serializer::NativeDeserialize;
98
use cubenativeutils::wrappers::NativeObjectHandle;

rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/string_or_sql.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@ impl<IT: InnerTypes> NativeDeserialize<IT> for StringOrSql {
2222
},
2323
}
2424
}
25-
}
25+
}

rust/cubesqlplanner/cubesqlplanner/src/plan/filter.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,60 @@ impl FilterItem {
104104
FilterItem::Segment(item) => result.push(item.member_evaluator().clone()),
105105
}
106106
}
107+
pub fn find_single_value_restriction(&self, symbol: &Rc<MemberSymbol>) -> Option<String> {
108+
match self {
109+
FilterItem::Item(item) => {
110+
if &item.member_evaluator() == symbol {
111+
item.get_single_value_restriction()
112+
} else {
113+
None
114+
}
115+
}
116+
117+
FilterItem::Group(group) => match group.operator {
118+
FilterGroupOperator::Or => {
119+
// Для OR: если хоть одна ветка не ограничивает -> нет единого ограничения
120+
// Если все ограничивают и все одинаковые -> то это значение
121+
let mut candidate: Option<String> = None;
122+
123+
for child in &group.items {
124+
match child.find_single_value_restriction(symbol) {
125+
None => return None, // хотя бы одна альтернатива без фиксации => OR не фиксирует
126+
Some(v) => {
127+
if let Some(prev) = &candidate {
128+
if prev != &v {
129+
return None;
130+
}
131+
} else {
132+
candidate = Some(v);
133+
}
134+
}
135+
}
136+
}
137+
138+
candidate
139+
}
140+
141+
FilterGroupOperator::And => {
142+
let mut candidate: Option<String> = None;
143+
144+
for child in &group.items {
145+
if let Some(v) = child.find_single_value_restriction(symbol) {
146+
if let Some(prev) = &candidate {
147+
if prev != &v {
148+
return None;
149+
}
150+
}
151+
candidate = Some(v);
152+
}
153+
}
154+
155+
candidate
156+
}
157+
},
158+
FilterItem::Segment(_) => None,
159+
}
160+
}
107161
}
108162

109163
impl Filter {

rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,14 @@ impl BaseFilter {
112112
self.values.len() == 1 && self.filter_operator == FilterOperator::Equal
113113
}
114114

115+
pub fn get_single_value_restriction(&self) -> Option<String> {
116+
if self.is_single_value_equal() {
117+
self.values[0].clone()
118+
} else {
119+
None
120+
}
121+
}
122+
115123
pub fn to_sql(
116124
&self,
117125
context: Rc<VisitorContext>,

rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::plan::{Filter, FilterItem};
1616
use crate::planner::sql_evaluator::collectors::{
1717
collect_multiplied_measures, has_multi_stage_members,
1818
};
19+
use crate::planner::sql_evaluator::symbols::apply_static_filter_to_vec;
1920
use cubenativeutils::CubeError;
2021
use itertools::Itertools;
2122
use std::collections::HashSet;
@@ -416,7 +417,7 @@ impl QueryProperties {
416417
let pre_aggregation_query = options.static_data().pre_aggregation_query.unwrap_or(false);
417418
let total_query = options.static_data().total_query.unwrap_or(false);
418419

419-
Ok(Rc::new(Self {
420+
let mut res = Self {
420421
measures,
421422
dimensions,
422423
segments,
@@ -434,7 +435,9 @@ impl QueryProperties {
434435
pre_aggregation_query,
435436
total_query,
436437
query_join_hints,
437-
}))
438+
};
439+
res.apply_static_filter();
440+
Ok(Rc::new(res))
438441
}
439442

440443
pub fn try_new_from_precompiled(
@@ -473,7 +476,7 @@ impl QueryProperties {
473476
&segments,
474477
)?;
475478

476-
Ok(Rc::new(Self {
479+
let mut res = Self {
477480
measures,
478481
dimensions,
479482
time_dimensions,
@@ -491,7 +494,14 @@ impl QueryProperties {
491494
pre_aggregation_query,
492495
total_query,
493496
query_join_hints,
494-
}))
497+
};
498+
res.apply_static_filter();
499+
Ok(Rc::new(res))
500+
}
501+
502+
fn apply_static_filter(&mut self) {
503+
apply_static_filter_to_vec(&mut self.measures, &self.dimensions_filters);
504+
apply_static_filter_to_vec(&mut self.dimensions, &self.dimensions_filters);
495505
}
496506

497507
pub fn compute_join_multi_fact_groups_with_measures(

0 commit comments

Comments
 (0)