1- use super :: { time_seria , Schema , SingleAliasedSource } ;
1+ use super :: { time_series , Schema , SingleAliasedSource } ;
22use crate :: planner:: sql_templates:: PlanSqlTemplates ;
33use crate :: planner:: { BaseJoinCondition , BaseMember , VisitorContext } ;
44use cubenativeutils:: CubeError ;
55
66use std:: rc:: Rc ;
77
88pub struct RollingWindowJoinCondition {
9- tailing_interval : Option < String > ,
9+ data_source : String ,
10+ time_series_source : String ,
11+ trailing_interval : Option < String > ,
1012 leading_interval : Option < String > ,
1113 offset : String ,
12- is_from_start_to_end : bool ,
13- time_dimension : Vec < Rc < BaseMember > > ,
14+ time_dimension : Rc < dyn BaseMember > ,
1415}
1516
1617impl RollingWindowJoinCondition {
1718 pub fn new (
19+ data_source : String ,
20+ time_series_source : String ,
1821 trailing_interval : Option < String > ,
1922 leading_interval : Option < String > ,
2023 offset : String ,
21- is_from_start_to_end : bool ,
22- dimensions : Vec < Rc < BaseMember > > ,
24+ time_dimension : Rc < dyn BaseMember > ,
2325 ) -> Self {
2426 Self {
25- tailing_interval,
27+ data_source,
28+ time_series_source,
29+ trailing_interval,
2630 leading_interval,
2731 offset,
28- is_from_start_to_end,
2932 time_dimension,
3033 }
3134 }
3235
36+ /*
37+ *
38+ offset = offset || 'end';
39+ return this.timeDimensions.map(
40+ d => [d, (dateFrom, dateTo, dateField, dimensionDateFrom, dimensionDateTo, isFromStartToEnd) => {
41+ // dateFrom based window
42+ const conditions = [];
43+ if (trailingInterval !== 'unbounded') {
44+ const startDate = isFromStartToEnd || offset === 'start' ? dateFrom : dateTo;
45+ const trailingStart = trailingInterval ? this.subtractInterval(startDate, trailingInterval) : startDate;
46+ const sign = offset === 'start' ? '>=' : '>';
47+ conditions.push(`${dateField} ${sign} ${trailingStart}`);
48+ }
49+ if (leadingInterval !== 'unbounded') {
50+ const endDate = isFromStartToEnd || offset === 'end' ? dateTo : dateFrom;
51+ const leadingEnd = leadingInterval ? this.addInterval(endDate, leadingInterval) : endDate;
52+ const sign = offset === 'end' ? '<=' : '<';
53+ conditions.push(`${dateField} ${sign} ${leadingEnd}`);
54+ }
55+ return conditions.length ? conditions.join(' AND ') : '1 = 1';
56+ }]
57+ );
58+ */
3359 pub fn to_sql (
3460 & self ,
3561 templates : & PlanSqlTemplates ,
3662 context : Rc < VisitorContext > ,
3763 schema : Rc < Schema > ,
3864 ) -> Result < String , CubeError > {
39- let result = if self . dimensions . is_empty ( ) {
40- format ! ( "1 = 1" )
65+ let mut conditions = vec ! [ ] ;
66+ /* let date_column_alias = if let Some(column) = schema.find_column_for_member(&self.time_dimension.full_name(), &None) {
67+ templates.column_reference(&source, &column.alias.clone())
4168 } else {
42- let conditions = vec ! [ ] ;
43- self . dimensions
44- . iter ( )
45- . map ( |dim| -> Result < String , CubeError > {
46- if let Some ( trailing_interval) = self . trailing_interval {
47- if tailing_interval == "unbounded" {
48- let seria_column = "date_from" ,
49- }
50- }
69+ dimension.to_sql(context.clone(), schema.clone())
70+ } */
71+ let date_column_alias =
72+ self . resolve_time_column_alias ( templates, context. clone ( ) , schema. clone ( ) ) ?;
73+ if let Some ( trailing_interval) = & self . trailing_interval {
74+ if trailing_interval != "unbounded" {
75+ let start_date = if self . offset == "start" {
76+ templates
77+ . column_reference ( & Some ( self . time_series_source . clone ( ) ) , "date_from" ) ?
78+ } else {
79+ templates. column_reference ( & Some ( self . time_series_source . clone ( ) ) , "date_to" ) ?
80+ } ;
5181
82+ let trailing_start = if let Some ( trailing_interval) = & self . trailing_interval {
83+ format ! ( "{start_date} - interval '{trailing_interval}'" )
84+ } else {
85+ start_date
86+ } ;
5287
53- } )
54- . collect :: < Result < Vec < _ > , _ > > ( ) ?
55- . join ( " AND " )
88+ let sign = if self . offset == "start" { ">=" } else { ">" } ;
89+
90+ conditions. push ( format ! ( "{date_column_alias} {sign} {trailing_start}" ) ) ;
91+ }
92+ }
93+
94+ if let Some ( leading_interval) = & self . trailing_interval {
95+ if leading_interval != "unbounded" {
96+ let end_date = if self . offset == "end" {
97+ templates. column_reference ( & Some ( self . time_series_source . clone ( ) ) , "date_to" ) ?
98+ } else {
99+ templates
100+ . column_reference ( & Some ( self . time_series_source . clone ( ) ) , "date_from" ) ?
101+ } ;
102+
103+ let leading_end = if let Some ( leading_interval) = & self . leading_interval {
104+ format ! ( "{end_date} + interval '{leading_interval}'" )
105+ } else {
106+ end_date
107+ } ;
108+
109+ let sign = if self . offset == "end" { "<=" } else { "<" } ;
110+
111+ conditions. push ( format ! ( "{date_column_alias} {sign} {leading_end}" ) ) ;
112+ }
113+ }
114+
115+ let result = if conditions. is_empty ( ) {
116+ templates. always_true ( ) ?
117+ } else {
118+ conditions. join ( " AND " )
56119 } ;
57120 Ok ( result)
58121 }
59122
60- fn resolve_member_alias (
123+ fn resolve_time_column_alias (
61124 & self ,
62125 templates : & PlanSqlTemplates ,
63126 context : Rc < VisitorContext > ,
64- source : & String ,
65- dimension : & Rc < dyn BaseMember > ,
66127 schema : Rc < Schema > ,
67128 ) -> Result < String , CubeError > {
68- let schema = schema. extract_source_schema ( source) ;
69- let source = Some ( source. clone ( ) ) ;
70- if let Some ( column) = schema. find_column_for_member ( & dimension. full_name ( ) , & source) {
129+ let schema = schema. extract_source_schema ( & self . data_source ) ;
130+ let source = Some ( self . data_source . clone ( ) ) ;
131+ if let Some ( column) =
132+ schema. find_column_for_member ( & self . time_dimension . full_name ( ) , & source)
133+ {
71134 templates. column_reference ( & source, & column. alias . clone ( ) )
72135 } else {
73- dimension . to_sql ( context. clone ( ) , schema. clone ( ) )
136+ self . time_dimension . to_sql ( context. clone ( ) , schema. clone ( ) )
74137 }
75138 }
76139}
@@ -162,6 +225,7 @@ impl DimensionJoinCondition {
162225pub enum JoinCondition {
163226 DimensionJoinCondition ( DimensionJoinCondition ) ,
164227 BaseJoinCondition ( Rc < dyn BaseJoinCondition > ) ,
228+ RollingWindowJoinCondition ( RollingWindowJoinCondition ) ,
165229}
166230
167231impl JoinCondition {
@@ -179,6 +243,24 @@ impl JoinCondition {
179243 ) )
180244 }
181245
246+ pub fn new_rolling_join (
247+ data_source : String ,
248+ time_series_source : String ,
249+ trailing_interval : Option < String > ,
250+ leading_interval : Option < String > ,
251+ offset : String ,
252+ time_dimension : Rc < dyn BaseMember > ,
253+ ) -> Self {
254+ Self :: RollingWindowJoinCondition ( RollingWindowJoinCondition :: new (
255+ data_source,
256+ time_series_source,
257+ trailing_interval,
258+ leading_interval,
259+ offset,
260+ time_dimension,
261+ ) )
262+ }
263+
182264 pub fn new_base_join ( base : Rc < dyn BaseJoinCondition > ) -> Self {
183265 Self :: BaseJoinCondition ( base)
184266 }
@@ -192,6 +274,9 @@ impl JoinCondition {
192274 match & self {
193275 JoinCondition :: DimensionJoinCondition ( cond) => cond. to_sql ( templates, context, schema) ,
194276 JoinCondition :: BaseJoinCondition ( cond) => cond. to_sql ( context, schema) ,
277+ JoinCondition :: RollingWindowJoinCondition ( cond) => {
278+ cond. to_sql ( templates, context, schema)
279+ }
195280 }
196281 }
197282}
0 commit comments