Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/cubejs-schema-compiler/src/adapter/BaseQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -3665,7 +3665,6 @@ export class BaseQuery {
* @param {import('./Granularity').Granularity} granularity
* @return {string}
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
dimensionTimeGroupedColumn(dimension, granularity) {
let dtDate;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,13 @@ describe('SQL Generation', () => {
granularity: 'week'
}
},
countRollingThreeDaysToDate: {
type: 'count',
rollingWindow: {
type: 'to_date',
granularity: 'three_days'
}
},
revenue_qtd: {
type: 'sum',
sql: 'amount',
Expand Down Expand Up @@ -1210,6 +1217,137 @@ SELECT 1 AS revenue, cast('2024-01-01' AS timestamp) as time UNION ALL
}
]));

it('custom granularity rolling window to_date with one time dimension with regular granularity', async () => runQueryTest({
measures: [
'visitors.countRollingThreeDaysToDate'
],
timeDimensions: [
{
dimension: 'visitors.created_at',
granularity: 'day',
dateRange: ['2017-01-01', '2017-01-10']
}
],
order: [{
id: 'visitors.created_at'
}],
timezone: 'America/Los_Angeles'
}, [
{
visitors__count_rolling_three_days_to_date: null,
visitors__created_at_day: '2017-01-01T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: '1',
visitors__created_at_day: '2017-01-02T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: '1',
visitors__created_at_day: '2017-01-03T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: '1',
visitors__created_at_day: '2017-01-04T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: '2',
visitors__created_at_day: '2017-01-05T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: '4',
visitors__created_at_day: '2017-01-06T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: null,
visitors__created_at_day: '2017-01-07T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: null,
visitors__created_at_day: '2017-01-08T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: null,
visitors__created_at_day: '2017-01-09T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: null,
visitors__created_at_day: '2017-01-10T00:00:00.000Z',
},
]));

it('custom granularity rolling window to_date with two time dimension granularities one custom one regular', async () => runQueryTest({
measures: [
'visitors.countRollingThreeDaysToDate'
],
timeDimensions: [
{
dimension: 'visitors.created_at',
granularity: 'three_days',
dateRange: ['2017-01-01', '2017-01-10']
},
{
dimension: 'visitors.created_at',
granularity: 'day',
dateRange: ['2017-01-01', '2017-01-10']
}
],
order: [{
id: 'visitors.created_at'
}],
timezone: 'America/Los_Angeles'
}, [
{
visitors__count_rolling_three_days_to_date: null,
visitors__created_at_day: '2017-01-01T00:00:00.000Z',
visitors__created_at_three_days: '2017-01-01T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: '1',
visitors__created_at_day: '2017-01-02T00:00:00.000Z',
visitors__created_at_three_days: '2017-01-01T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: '1',
visitors__created_at_day: '2017-01-03T00:00:00.000Z',
visitors__created_at_three_days: '2017-01-01T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: '1',
visitors__created_at_day: '2017-01-04T00:00:00.000Z',
visitors__created_at_three_days: '2017-01-04T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: '2',
visitors__created_at_day: '2017-01-05T00:00:00.000Z',
visitors__created_at_three_days: '2017-01-04T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: '4',
visitors__created_at_day: '2017-01-06T00:00:00.000Z',
visitors__created_at_three_days: '2017-01-04T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: null,
visitors__created_at_day: '2017-01-07T00:00:00.000Z',
visitors__created_at_three_days: '2017-01-07T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: null,
visitors__created_at_day: '2017-01-08T00:00:00.000Z',
visitors__created_at_three_days: '2017-01-07T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: null,
visitors__created_at_day: '2017-01-09T00:00:00.000Z',
visitors__created_at_three_days: '2017-01-07T00:00:00.000Z',
},
{
visitors__count_rolling_three_days_to_date: null,
visitors__created_at_day: '2017-01-10T00:00:00.000Z',
visitors__created_at_three_days: '2017-01-10T00:00:00.000Z',
},
]));

it('rolling window with same td with and without granularity', async () => runQueryTest({
measures: [
'visitors.countRollingWeekToDate'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::logical_plan::*;
use crate::planner::query_properties::OrderByItem;
use crate::planner::sql_evaluator::MemberSymbol;
use crate::planner::Granularity;
use std::rc::Rc;

pub struct MultiStageRegularRollingWindow {
Expand All @@ -24,14 +25,17 @@ impl PrettyPrint for MultiStageRegularRollingWindow {
}

pub struct MultiStageToDateRollingWindow {
pub granularity: String,
pub granularity_obj: Rc<Granularity>,
}

impl PrettyPrint for MultiStageToDateRollingWindow {
fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) {
result.println("ToDate Rolling Window", state);
let state = state.new_level();
result.println(&format!("granularity: {}", self.granularity), &state);
result.println(
&format!("granularity: {}", self.granularity_obj.granularity()),
&state,
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1061,7 +1061,7 @@ impl PhysicalPlanBuilder {
MultiStageRollingWindowType::ToDate(to_date_rolling_window) => {
JoinCondition::new_to_date_rolling_join(
root_alias.clone(),
to_date_rolling_window.granularity.clone(),
to_date_rolling_window.granularity_obj.clone(),
Expr::Reference(QualifiedColumnName::new(
Some(measure_input_alias.clone()),
base_time_dimension_alias,
Expand Down Expand Up @@ -1092,7 +1092,7 @@ impl PhysicalPlanBuilder {
let mut render_references = HashMap::new();
let mut select_builder = SelectBuilder::new(from.clone());

//We insert render reference for main time dimension (with the some granularity as in time series to avoid unnecessary date_tranc)
//We insert render reference for main time dimension (with some granularity as in time series to avoid unnecessary date_tranc)
render_references.insert(
time_dimension.full_name(),
QualifiedColumnName::new(Some(root_alias.clone()), format!("date_from")),
Expand Down
10 changes: 5 additions & 5 deletions rust/cubesqlplanner/cubesqlplanner/src/plan/join.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::{Expr, SingleAliasedSource};
use crate::planner::query_tools::QueryTools;
use crate::planner::sql_templates::PlanSqlTemplates;
use crate::planner::{BaseJoinCondition, VisitorContext};
use crate::planner::{BaseJoinCondition, Granularity, VisitorContext};
use cubenativeutils::CubeError;
use lazy_static::lazy_static;

Expand Down Expand Up @@ -118,15 +118,15 @@ impl RollingTotalJoinCondition {
}
pub struct ToDateRollingWindowJoinCondition {
time_series_source: String,
granularity: String,
granularity: Rc<Granularity>,
time_dimension: Expr,
_query_tools: Rc<QueryTools>,
}

impl ToDateRollingWindowJoinCondition {
pub fn new(
time_series_source: String,
granularity: String,
granularity: Rc<Granularity>,
time_dimension: Expr,
query_tools: Rc<QueryTools>,
) -> Self {
Expand All @@ -151,7 +151,7 @@ impl ToDateRollingWindowJoinCondition {
templates.column_reference(&Some(self.time_series_source.clone()), "date_from")?;
let date_from = templates.rolling_window_expr_timestamp_cast(&date_from)?;
let date_to = templates.rolling_window_expr_timestamp_cast(&date_to)?;
let grouped_from = templates.time_grouped_column(self.granularity.clone(), date_from)?;
let grouped_from = self.granularity.apply_to_input_sql(templates, date_from)?;
let result = format!("{date_column} >= {grouped_from} and {date_column} <= {date_to}");
Ok(result)
}
Expand Down Expand Up @@ -243,7 +243,7 @@ impl JoinCondition {

pub fn new_to_date_rolling_join(
time_series_source: String,
granularity: String,
granularity: Rc<Granularity>,
time_dimension: Expr,
query_tools: Rc<QueryTools>,
) -> Self {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use crate::planner::query_tools::QueryTools;
use crate::planner::sql_evaluator::MemberSymbol;
use crate::planner::sql_templates::PlanSqlTemplates;
use crate::planner::sql_templates::TemplateProjectionColumn;
use crate::planner::QueryDateTimeHelper;
use crate::planner::{evaluate_with_context, FiltersContext, VisitorContext};
use crate::planner::{Granularity, GranularityHelper, QueryDateTimeHelper};
use cubenativeutils::CubeError;
use std::rc::Rc;

Expand Down Expand Up @@ -188,13 +188,47 @@ impl BaseFilter {
filters_context,
&member_type,
)?,
FilterOperator::ToDateRollingWindowDateRange => self
.to_date_rolling_window_date_range(
FilterOperator::ToDateRollingWindowDateRange => {
let query_granularity = if self.values.len() >= 3 {
if let Some(granularity) = &self.values[2] {
granularity
} else {
return Err(CubeError::user(
"Granularity required for to_date rolling window".to_string(),
));
}
} else {
return Err(CubeError::user(
"Granularity required for to_date rolling window".to_string(),
));
};
let evaluator_compiler_cell = self.query_tools.evaluator_compiler().clone();
let mut evaluator_compiler = evaluator_compiler_cell.borrow_mut();

let Some(granularity_obj) = GranularityHelper::make_granularity_obj(
self.query_tools.cube_evaluator().clone(),
&mut evaluator_compiler,
self.query_tools.timezone().clone(),
&symbol.cube_name(),
&symbol.name(),
Some(query_granularity.clone()),
)?
else {
return Err(CubeError::internal(format!(
"Rolling window granularity '{}' is not found in time dimension '{}'",
query_granularity,
symbol.name()
)));
};

self.to_date_rolling_window_date_range(
&member_sql,
plan_templates,
filters_context,
&member_type,
)?,
granularity_obj,
)?
}
FilterOperator::In => {
self.in_where(&member_sql, plan_templates, filters_context, &member_type)?
}
Expand Down Expand Up @@ -539,22 +573,11 @@ impl BaseFilter {
plan_templates: &PlanSqlTemplates,
_filters_context: &FiltersContext,
_member_type: &Option<String>,
granularity_obj: Granularity,
) -> Result<String, CubeError> {
let (from, to) = self.date_range_from_time_series(plan_templates)?;

let from = if self.values.len() >= 3 {
if let Some(granularity) = &self.values[2] {
plan_templates.time_grouped_column(granularity.clone(), from)?
} else {
return Err(CubeError::user(format!(
"Granularity required for to_date rolling window"
)));
}
} else {
return Err(CubeError::user(format!(
"Granularity required for to_date rolling window"
)));
};
let from = granularity_obj.apply_to_input_sql(plan_templates, from.clone())?;

let date_field = plan_templates.convert_tz(member_sql.to_string())?;
plan_templates.time_range_filter(date_field, from, to)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use crate::logical_plan::*;
use crate::planner::planners::{multi_stage::RollingWindowType, QueryPlanner, SimpleQueryPlanner};
use crate::planner::query_tools::QueryTools;
use crate::planner::sql_evaluator::MemberSymbol;
use crate::planner::{BaseDimension, BaseMeasure, BaseMember, BaseMemberHelper, BaseTimeDimension};
use crate::planner::{
BaseDimension, BaseMeasure, BaseMember, BaseMemberHelper, BaseTimeDimension, GranularityHelper,
};
use crate::planner::{OrderByItem, QueryProperties};

use cubenativeutils::CubeError;
Expand Down Expand Up @@ -126,8 +128,30 @@ impl MultiStageMemberQueryPlanner {
})
}
RollingWindowType::ToDate(to_date_rolling_window) => {
let time_dimension = &rolling_window_desc.time_dimension;
let query_granularity = to_date_rolling_window.granularity.clone();

let evaluator_compiler_cell = self.query_tools.evaluator_compiler().clone();
let mut evaluator_compiler = evaluator_compiler_cell.borrow_mut();

let Some(granularity_obj) = GranularityHelper::make_granularity_obj(
self.query_tools.cube_evaluator().clone(),
&mut evaluator_compiler,
self.query_tools.timezone().clone(),
time_dimension.cube_name(),
time_dimension.name(),
Some(query_granularity.clone()),
)?
else {
return Err(CubeError::internal(format!(
"Rolling window granularity '{}' is not found in time dimension '{}'",
query_granularity,
time_dimension.name()
)));
};

MultiStageRollingWindowType::ToDate(MultiStageToDateRollingWindow {
granularity: to_date_rolling_window.granularity.clone(),
granularity_obj: Rc::new(granularity_obj),
})
}
RollingWindowType::RunningTotal => MultiStageRollingWindowType::RunningTotal,
Expand Down
Loading
Loading