Skip to content

Commit 90f1d97

Browse files
committed
recursive static filter
1 parent c14aad6 commit 90f1d97

File tree

19 files changed

+422
-86
lines changed

19 files changed

+422
-86
lines changed

packages/cubejs-schema-compiler/src/compiler/CubeEvaluator.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -556,9 +556,6 @@ export class CubeEvaluator extends CubeSymbols {
556556
if (funcArgs.length > 0 && cubeReferencesUsed.length === 0) {
557557
ownedByCube = false;
558558
}
559-
if (member.type && member.type === 'switch') {
560-
ownedByCube = false;
561-
}
562559
// Aliases one to one some another member as in case of views
563560
// Note: Segments do not have type set
564561
if (!ownedByCube && !member.filters && (!member.type || CubeSymbols.isCalculatedMeasureType(member.type)) && pathReferencesUsed.length === 1 && this.pathFromArray(pathReferencesUsed[0]) === evaluatedSql) {

packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,8 @@ const DimensionsSchema = Joi.object().pattern(identifierRegex, Joi.alternatives(
699699
then: SwitchDimension,
700700
otherwise: Joi.alternatives().try(
701701
inherit(BaseDimensionWithoutSubQuery, {
702-
case: CaseVariants
702+
case: CaseVariants,
703+
multiStage: Joi.boolean().strict(),
703704
}),
704705
inherit(BaseDimensionWithoutSubQuery, {
705706
latitude: Joi.object().keys({

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

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -247,19 +247,11 @@ cubes:
247247
- name: count
248248
type: count
249249
250-
- name: source_root
250+
- name: source
251251
sql: >
252252
SELECT 1
253253
public: false
254254
255-
joins:
256-
- name: source_a
257-
sql: "1 = 1"
258-
relationship: one_to_many
259-
- name: source_b
260-
sql: "1 = 1"
261-
relationship: one_to_many
262-
263255
dimensions:
264256
- name: pk
265257
type: number
@@ -276,6 +268,7 @@ cubes:
276268
277269
- name: product_category
278270
type: string
271+
multi_stage: true
279272
case:
280273
switch: "{CUBE.source}"
281274
when:
@@ -411,15 +404,9 @@ cubes:
411404
sql: PRICE_EUR
412405
413406
views:
414-
- name: source
415-
cubes:
416-
- join_path: source_root
417-
includes: "*"
418-
419407
420408
421409
- name: orders_view
422-
423410
cubes:
424411
- join_path: orders
425412
includes:
@@ -1209,6 +1196,29 @@ views:
12091196
{ joinGraph, cubeEvaluator, compiler });
12101197
});
12111198

1199+
it('source switch - source_a + usd + filter by category', async () => {
1200+
await dbRunner.runQueryTest({
1201+
dimensions: ['source.currency', 'source.product_category'],
1202+
measures: ['source.price'],
1203+
order: [{
1204+
id: 'source.product_category'
1205+
},
1206+
],
1207+
filters: [
1208+
{ dimension: 'source.currency', operator: 'equals', values: ['USD'] },
1209+
{ dimension: 'source.source', operator: 'equals', values: ['A'] },
1210+
{ dimension: 'source.product_category', operator: 'equals', values: ['some category'] },
1211+
],
1212+
}, [
1213+
{
1214+
source__currency: 'USD',
1215+
source__product_category: 'some category',
1216+
source__price: '600'
1217+
},
1218+
],
1219+
{ joinGraph, cubeEvaluator, compiler });
1220+
});
1221+
12121222
it('source switch - source_b + eur', async () => {
12131223
await dbRunner.runQueryTest({
12141224
dimensions: ['source.currency', 'source.product_category'],

rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/multi_stage_measure_calculation.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ impl<'a> LogicalNodeProcessor<'a, MultiStageMeasureCalculation>
5050
}
5151

5252
for measure in measure_calculation.schema().measures.iter() {
53+
println!("!!!! ooooo: {}", measure.full_name());
5354
references_builder.resolve_references_for_member(
5455
measure.clone(),
5556
&None,

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub enum FilterType {
1818
Measure,
1919
}
2020

21+
#[derive(Clone)]
2122
pub struct BaseFilter {
2223
query_tools: Rc<QueryTools>,
2324
member_evaluator: Rc<MemberSymbol>,
@@ -83,6 +84,12 @@ impl BaseFilter {
8384
}
8485
}
8586

87+
pub fn with_member_evaluator(&self, member_evaluator: Rc<MemberSymbol>) -> Rc<Self> {
88+
let mut result = self.clone();
89+
result.member_evaluator = member_evaluator;
90+
Rc::new(result)
91+
}
92+
8693
//FIXME Not very good solution, but suitable for check time dimension filters in pre-aggregations
8794
pub fn time_dimension_symbol(&self) -> Option<Rc<MemberSymbol>> {
8895
if self.member_evaluator.as_time_dimension().is_ok() {

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::planner::{evaluate_with_context, VisitorContext};
77
use cubenativeutils::CubeError;
88
use std::rc::Rc;
99

10+
#[derive(Clone)]
1011
pub struct BaseSegment {
1112
full_name: String,
1213
member_evaluator: Rc<MemberSymbol>,
@@ -61,6 +62,12 @@ impl BaseSegment {
6162
self.member_evaluator.clone()
6263
}
6364

65+
pub fn with_member_evaluator(&self, member_evaluator: Rc<MemberSymbol>) -> Rc<Self> {
66+
let mut result = self.clone();
67+
result.member_evaluator = member_evaluator;
68+
Rc::new(result)
69+
}
70+
6471
pub fn cube_name(&self) -> &String {
6572
&self.cube_name
6673
}

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

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -200,10 +200,15 @@ impl MultiStageMemberQueryPlanner {
200200
_ => MultiStageCalculationWindowFunction::None,
201201
};
202202

203+
let measures = if self.description.member().evaluation_node().is_measure() {
204+
vec![self.description.member().evaluation_node().clone()]
205+
} else {
206+
vec![]
207+
};
203208
let schema = LogicalSchema::default()
204209
.set_dimensions(self.description.state().dimensions_symbols())
205210
.set_time_dimensions(self.description.state().time_dimensions_symbols())
206-
.set_measures(vec![self.description.member().evaluation_node().clone()])
211+
.set_measures(measures)
207212
.into_rc();
208213

209214
let calculation_type = match multi_stage_member.inode_type() {
@@ -221,10 +226,12 @@ impl MultiStageMemberQueryPlanner {
221226
.input_cte_aliases()
222227
.into_iter()
223228
.map(|(name, symbols)| {
224-
Rc::new(MultiStageSubqueryRef::builder()
225-
.name(name.clone())
226-
.symbols(symbols.clone())
227-
.build())
229+
Rc::new(
230+
MultiStageSubqueryRef::builder()
231+
.name(name.clone())
232+
.symbols(symbols.clone())
233+
.build(),
234+
)
228235
})
229236
.collect_vec();
230237

@@ -236,11 +243,13 @@ impl MultiStageMemberQueryPlanner {
236243
.partition_by(partition_by)
237244
.window_function_to_use(window_function_to_use)
238245
.order_by(self.query_order_by()?)
239-
.source(Rc::new(FullKeyAggregate::builder()
240-
.schema(full_key_aggregate_schema)
241-
.use_full_join_and_coalesce(true)
242-
.multi_stage_subquery_refs(input_sources)
243-
.build()))
246+
.source(Rc::new(
247+
FullKeyAggregate::builder()
248+
.schema(full_key_aggregate_schema)
249+
.use_full_join_and_coalesce(true)
250+
.multi_stage_subquery_refs(input_sources)
251+
.build(),
252+
))
244253
.build();
245254

246255
let result = LogicalMultiStageMember {

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,12 @@ impl MultiStageQueryPlanner {
9292
let top_level_ctes = top_level_ctes
9393
.iter()
9494
.map(|(alias, symbols)| {
95-
Rc::new(MultiStageSubqueryRef::builder()
96-
.name(alias.clone())
97-
.symbols(symbols.clone())
98-
.build())
95+
Rc::new(
96+
MultiStageSubqueryRef::builder()
97+
.name(alias.clone())
98+
.symbols(symbols.clone())
99+
.build(),
100+
)
99101
})
100102
.collect_vec();
101103

@@ -136,6 +138,7 @@ impl MultiStageQueryPlanner {
136138
is_ungrupped,
137139
)
138140
} else {
141+
println!("!!!! jjjjj");
139142
(
140143
MultiStageInodeMember::new(
141144
MultiStageInodeMemberType::Calculate,
@@ -157,7 +160,7 @@ impl MultiStageQueryPlanner {
157160
descriptions: &mut Vec<Rc<MultiStageQueryDescription>>,
158161
) -> Result<Rc<MultiStageQueryDescription>, CubeError> {
159162
let member = member.resolve_reference_chain();
160-
let member = apply_static_filter_to_symbol(&member, state.dimensions_filters());
163+
let member = apply_static_filter_to_symbol(&member, state.dimensions_filters())?;
161164

162165
let member_name = member.full_name();
163166
if let Some(exists) = descriptions

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

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use super::query_tools::QueryTools;
44
use crate::cube_bridge::join_hints::JoinHintItem;
55
use crate::cube_bridge::member_expression::MemberExpressionExpressionDef;
66
use crate::planner::sql_evaluator::{
7-
MemberExpressionExpression, MemberExpressionSymbol, TimeDimensionSymbol,
7+
apply_static_filter_to_filter_item, apply_static_filter_to_symbol, MemberExpressionExpression,
8+
MemberExpressionSymbol, TimeDimensionSymbol,
89
};
910
use crate::planner::GranularityHelper;
1011

@@ -401,22 +402,10 @@ impl QueryProperties {
401402

402403
let query_join_hints = Rc::new(options.join_hints()?.unwrap_or_default());
403404

404-
let multi_fact_join_groups = Self::compute_join_multi_fact_groups(
405-
query_join_hints.clone(),
406-
query_tools.clone(),
407-
&measures,
408-
&dimensions,
409-
&time_dimensions,
410-
&time_dimensions_filters,
411-
&dimensions_filters,
412-
&measures_filters,
413-
&segments,
414-
)?;
415-
416405
let pre_aggregation_query = options.static_data().pre_aggregation_query.unwrap_or(false);
417406
let total_query = options.static_data().total_query.unwrap_or(false);
418407

419-
let res = Self {
408+
let mut res = Self {
420409
measures,
421410
dimensions,
422411
segments,
@@ -430,11 +419,12 @@ impl QueryProperties {
430419
query_tools,
431420
ignore_cumulative: false,
432421
ungrouped,
433-
multi_fact_join_groups,
422+
multi_fact_join_groups: vec![],
434423
pre_aggregation_query,
435424
total_query,
436425
query_join_hints,
437426
};
427+
res.apply_static_filters()?;
438428
Ok(Rc::new(res))
439429
}
440430

@@ -462,19 +452,7 @@ impl QueryProperties {
462452
order_by
463453
};
464454

465-
let multi_fact_join_groups = Self::compute_join_multi_fact_groups(
466-
query_join_hints.clone(),
467-
query_tools.clone(),
468-
&measures,
469-
&dimensions,
470-
&time_dimensions,
471-
&time_dimensions_filters,
472-
&dimensions_filters,
473-
&measures_filters,
474-
&segments,
475-
)?;
476-
477-
let res = Self {
455+
let mut res = Self {
478456
measures,
479457
dimensions,
480458
time_dimensions,
@@ -488,14 +466,55 @@ impl QueryProperties {
488466
query_tools,
489467
ignore_cumulative,
490468
ungrouped,
491-
multi_fact_join_groups,
469+
multi_fact_join_groups: vec![],
492470
pre_aggregation_query,
493471
total_query,
494472
query_join_hints,
495473
};
474+
res.apply_static_filters()?;
475+
496476
Ok(Rc::new(res))
497477
}
498478

479+
fn apply_static_filters(&mut self) -> Result<(), CubeError> {
480+
let dimensions_filters = self.dimensions_filters.clone();
481+
for dim in self.dimensions.iter_mut() {
482+
*dim = apply_static_filter_to_symbol(dim, &dimensions_filters)?;
483+
}
484+
for meas in self.measures.iter_mut() {
485+
*meas = apply_static_filter_to_symbol(meas, &dimensions_filters)?;
486+
}
487+
for filter_item in self.dimensions_filters.iter_mut() {
488+
*filter_item = apply_static_filter_to_filter_item(filter_item, &dimensions_filters)?;
489+
}
490+
for filter_item in self.measures_filters.iter_mut() {
491+
*filter_item = apply_static_filter_to_filter_item(filter_item, &dimensions_filters)?;
492+
}
493+
for filter_item in self.time_dimensions_filters.iter_mut() {
494+
*filter_item = apply_static_filter_to_filter_item(filter_item, &dimensions_filters)?;
495+
}
496+
for filter_item in self.segments.iter_mut() {
497+
*filter_item = apply_static_filter_to_filter_item(filter_item, &dimensions_filters)?;
498+
}
499+
for order_item in self.order_by.iter_mut() {
500+
order_item.member_evaluator =
501+
apply_static_filter_to_symbol(&order_item.member_evaluator, &dimensions_filters)?;
502+
}
503+
504+
self.multi_fact_join_groups = Self::compute_join_multi_fact_groups(
505+
self.query_join_hints.clone(),
506+
self.query_tools.clone(),
507+
&self.measures,
508+
&self.dimensions,
509+
&self.time_dimensions,
510+
&self.time_dimensions_filters,
511+
&self.dimensions_filters,
512+
&self.measures_filters,
513+
&self.segments,
514+
)?;
515+
Ok(())
516+
}
517+
499518
pub fn compute_join_multi_fact_groups_with_measures(
500519
&self,
501520
measures: &Vec<Rc<MemberSymbol>>,

rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ impl TraversalVisitor for JoinHintsCollector {
2626
path: &Vec<String>,
2727
_: &Self::State,
2828
) -> Result<Option<Self::State>, CubeError> {
29+
if !node.owned_by_cube() {
30+
return Ok(Some(()));
31+
}
32+
println!("!!! node: {}", node.full_name());
2933
match node.as_ref() {
3034
MemberSymbol::Dimension(e) => {
3135
if !e.is_view() {
@@ -79,6 +83,7 @@ pub fn collect_join_hints(node: &Rc<MemberSymbol>) -> Result<Vec<JoinHintItem>,
7983
let mut visitor = JoinHintsCollector::new();
8084
visitor.apply(node, &())?;
8185
let res = visitor.extract_result();
86+
println!("!!! join hints 0: {:#?}", res);
8287
Ok(res)
8388
}
8489

@@ -90,5 +95,6 @@ pub fn collect_join_hints_for_measures(
9095
visitor.apply(&meas, &())?;
9196
}
9297
let res = visitor.extract_result();
98+
println!("!!! join hints: {:#?}", res);
9399
Ok(res)
94100
}

0 commit comments

Comments
 (0)