Skip to content

Commit d5e1335

Browse files
committed
DataFrame supports window function
1 parent e6657f0 commit d5e1335

1 file changed

Lines changed: 48 additions & 5 deletions

File tree

datafusion/src/execution/dataframe_impl.rs

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use crate::arrow::util::pretty;
3535
use crate::physical_plan::{
3636
execute_stream, execute_stream_partitioned, ExecutionPlan, SendableRecordBatchStream,
3737
};
38+
use crate::sql::utils::{find_window_exprs, group_window_expr_by_sort_keys};
3839
use async_trait::async_trait;
3940

4041
/// Implementation of DataFrame API
@@ -75,10 +76,27 @@ impl DataFrame for DataFrameImpl {
7576

7677
/// Create a projection based on arbitrary expressions
7778
fn select(&self, expr_list: Vec<Expr>) -> Result<Arc<dyn DataFrame>> {
78-
let plan = LogicalPlanBuilder::from(self.to_logical_plan())
79-
.project(expr_list)?
80-
.build()?;
81-
Ok(Arc::new(DataFrameImpl::new(self.ctx_state.clone(), &plan)))
79+
let window_func_exprs = find_window_exprs(&expr_list);
80+
let plan = if window_func_exprs.is_empty() {
81+
self.to_logical_plan()
82+
} else {
83+
let mut logical_plan = self.to_logical_plan();
84+
let mut groups = group_window_expr_by_sort_keys(&window_func_exprs)?;
85+
groups.sort_by(|(key1, _), (key2, _)| key1.len().cmp(&key2.len()));
86+
groups.reverse();
87+
for (_, exprs) in groups {
88+
let window_exprs = exprs.into_iter().cloned().collect::<Vec<_>>();
89+
logical_plan = LogicalPlanBuilder::from(logical_plan)
90+
.window(window_exprs)?
91+
.build()?;
92+
}
93+
logical_plan
94+
};
95+
let project_plan = LogicalPlanBuilder::from(plan).project(expr_list)?.build()?;
96+
Ok(Arc::new(DataFrameImpl::new(
97+
self.ctx_state.clone(),
98+
&project_plan,
99+
)))
82100
}
83101

84102
/// Create a filter based on a predicate expression
@@ -233,7 +251,7 @@ mod tests {
233251
use crate::execution::options::CsvReadOptions;
234252
use crate::logical_plan::*;
235253
use crate::physical_plan::functions::Volatility;
236-
use crate::physical_plan::ColumnarValue;
254+
use crate::physical_plan::{window_functions, ColumnarValue};
237255
use crate::{assert_batches_sorted_eq, execution::context::ExecutionContext};
238256
use crate::{physical_plan::functions::ScalarFunctionImplementation, test};
239257
use arrow::datatypes::DataType;
@@ -270,6 +288,31 @@ mod tests {
270288
Ok(())
271289
}
272290

291+
#[tokio::test]
292+
async fn select_with_window_exprs() -> Result<()> {
293+
// build plan using Table API
294+
let t = test_table().await?;
295+
let first_row = Expr::WindowFunction {
296+
fun: window_functions::WindowFunction::BuiltInWindowFunction(
297+
window_functions::BuiltInWindowFunction::FirstValue,
298+
),
299+
args: vec![col("aggregate_test_100.c1")],
300+
partition_by: vec![col("aggregate_test_100.c2")],
301+
order_by: vec![],
302+
window_frame: None,
303+
};
304+
let t2 = t.select(vec![col("c1"), first_row])?;
305+
let plan = t2.to_logical_plan();
306+
307+
let sql_plan = create_plan(
308+
"select c1, first_value(c1) over (partition by c2) from aggregate_test_100",
309+
)
310+
.await?;
311+
312+
assert_same_plan(&plan, &sql_plan);
313+
Ok(())
314+
}
315+
273316
#[tokio::test]
274317
async fn aggregate() -> Result<()> {
275318
// build plan using DataFrame API

0 commit comments

Comments
 (0)