You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Is your feature request related to a problem or challenge?
When implementing our InfuxQL frontend for DataFusion, we found certain functions we would like to express as window functions (like a specialized interpolation function for example)
We can write user defined aggregates:
select
x, my_aggregate(x, time)
from t;
But those do not produce the same number of rows that go in (they reduce cardinality)
We would like to use our own window functions like
select
x, my_interpolation_function(x, time) OVER (PARTITION BY y, ORDER BYtime)
from t;
Describe the solution you'd like
I would like the ability to define, and register user defined window functions , like we have for user defined aggregate functions:
FYI @mustafasrepo and @ozankabak I think @stuartcarnie is contemplating what implementing user defined window functions might entail. I am not sure if you have any thoughts on this matter you would like to share
I never really understood why DataFusion makes a distinction between built in aggregate functions and user defined aggregate functions (it would be really nice if all aggregate functions had the same interface)
/// Window function#[derive(Clone,PartialEq,Eq,Hash)]pubstructWindowFunction{/// Name of the functionpubfun: window_function::WindowFunction,/// List of expressions to feed to the functions as argumentspubargs:Vec<Expr>,/// List of partition by expressionspubpartition_by:Vec<Expr>,/// List of order by expressionspuborder_by:Vec<Expr>,/// Window framepubwindow_frame: window_frame::WindowFrame,}
Note that window_function::WindowFunction already handles user defined functions (AggregateUDF), so I propose to add an addutionl user defined window function variant there:
/// WindowFunction (in `window_function`):#[derive(Debug,Clone,PartialEq,Eq,Hash)]pubenumWindowFunction{/// A built in aggregate function that leverages an aggregate functionAggregateFunction(AggregateFunction),/// A a built-in window functionBuiltInWindowFunction(BuiltInWindowFunction),/// A user defined aggregate functionAggregateUDF(Arc<AggregateUDF>),/// A user defined aggregate function <---- This is NEWWindowUDF(Arc<WindowUDF>),}
WindowUDF
WindowUDF will be a structure that looks very similar to AggregateUDF:
I am a little unclear on certain parts of the PartitionEvaluator AP (specifically, the stateful / stateless partition evaluation) and if we can make it straight forward to implement this for datafusion
Looking at how this code is used in the physical_plan module, what is needed is something that implements the BuiltInWindowFunctionExpr. I am not sure yet if it makes sense to have WindowUDF do so directly of if another structure would be better
Great stuff, @alamb! I did have similar ideas in my own test branch, but I wasn't sure how to replicate BuiltInWindowFunctionExpr and BuiltinWindowState. Renaming them makes a lot of sense 👍🏻
This would mean making those traits pub and part of a WindowUDF and
Pros
This is the same pattern used by AggregateUDF that exposes the Accumulator trait directly to implementors
Allow user defined window functions to access all the features and performance as built in window functions (both now and in the future)
Cons
BuiltInWindowFunctionExpr and PartitionEvaluator are are fairly complicated (as they support many features) which could make implementing user defined window functions harder
Also, BuiltInWindowFunctionExpr and PartitionEvaluator would likely need some changes such as:
renamed so they weren't called "BuiltIn*"
Moving the trait definitions into datafusion_expr
Update the state management to be extendable (e.g. the enum for BuiltinWindowState would have to be updated to to allow user defined state somehow)
Make new APIs and traits
What this would mean:
In this case, we would make new traits that are subsets of what is in BuiltInWindowFunctionExpr and PartitionEvaluator that users would
implement. Internally we would implement BuiltInWindowFunctionExpr
and PartitionEvaluator in terms of these new traits.
Pros
We can tailor the API to precisely the needs of user defined window function writers
Would allow changing the internal APIs without having to change the WindowUDF API
Cons
A new API would mean introducing another api layer that needs to be tested and kept up to date.
The new API also might not expose all the functionality of the built in window functions if such functionality was added at a later date
Discussion
I am going to try and bash out a technical proof of concept of the first approach (exposing the existing APIs) and we can see how it would look. On the balance that is my preferred approach due to the power of the API and the similarities with AggregateUDFs
Activity
alamb commentedon Apr 25, 2023
FYI @mustafasrepo and @ozankabak I think @stuartcarnie is contemplating what implementing user defined window functions might entail. I am not sure if you have any thoughts on this matter you would like to share
ozankabak commentedon Apr 25, 2023
Yes, we will be using UDWF too. Is there a design doc we can read and comment on?
doki23 commentedon Apr 26, 2023
How we distinguish UDWF from UDAF since window function already supports AggregateUDF, I mean UDWF is a super set of udaf in window function.
alamb commentedon Apr 26, 2023
There is no document that I know of -- it is probably time to start one
alamb commentedon Apr 26, 2023
@doki23 I am not sure
The current code has this:
https://docs.rs/datafusion-expr/23.0.0/datafusion_expr/window_function/enum.WindowFunction.html
I never really understood why DataFusion makes a distinction between built in aggregate functions and user defined aggregate functions (it would be really nice if all aggregate functions had the same interface)
alamb commentedon Jun 6, 2023
Unless someone beats me to it I plan to start working on a proposed design for this feature in the next few days
alamb commentedon Jun 7, 2023
Here is a proposed design.
LogicalPlan / Expr
The current (
Expr) representation of window functions:Which is defined like:
Note that
window_function::WindowFunctionalready handles user defined functions (AggregateUDF), so I propose to add an addutionl user defined window function variant there:WindowUDF
WindowUDFwill be a structure that looks very similar toAggregateUDF:https://github.com/apache/arrow-datafusion/blob/1d3860dc813725b5a987aae4c3a8f4a7b2bfdb2d/datafusion/expr/src/udaf.rs#L30-L40
And similarly to the way that an
AggregateUDFinstantiates anAccumulator(viaAccumulatorFunctionImplementation) the WindowUDF will need to provide aPartitionEvaluatorinstance for each partition of the data.I am a little unclear on certain parts of the
PartitionEvaluatorAP (specifically, the stateful / stateless partition evaluation) and if we can make it straight forward to implement this for datafusionLooking at how this code is used in the
physical_planmodule, what is needed is something that implements theBuiltInWindowFunctionExpr. I am not sure yet if it makes sense to haveWindowUDFdo so directly of if another structure would be betterHere are the next steps I plan:
So steps:
PartitionEvaluatorwhich will both improve the code, and allow me to sort out some of my questions: Minor: Add additional docstrings to Window function implementations #6592BuiltInWindowFunctionExprtoWindowFunctionExprto reflect they will be used for more than BuiltInBuiltinWindowStatetoWindowState(in the same theory that it is not related to built ins)stuartcarnie commentedon Jun 7, 2023
Great stuff, @alamb! I did have similar ideas in my own test branch, but I wasn't sure how to replicate
BuiltInWindowFunctionExprandBuiltinWindowState. Renaming them makes a lot of sense 👍🏻ozankabak commentedon Jun 8, 2023
Looks reasonable to me, thanks @alamb
alamb commentedon Jun 8, 2023
While working on #6592 I think I can now articulate a key design question about
WindowUDF. Specifically:Use existing API / Traits
What this would mean:
This would mean exposing (at least)
BuiltInWindowFunctionExprandPartitionEvaluatordirectly for people to implement aWindowUDFThis would mean making those traits
puband part of a WindowUDF andPros
AggregateUDFthat exposes theAccumulatortrait directly to implementorsCons
BuiltInWindowFunctionExprandPartitionEvaluatorare are fairly complicated (as they support many features) which could make implementing user defined window functions harderAlso,
BuiltInWindowFunctionExprandPartitionEvaluatorwould likely need some changes such as:datafusion_exprBuiltinWindowStatewould have to be updated to to allow user defined state somehow)Make new APIs and traits
What this would mean:
In this case, we would make new traits that are subsets of what is in
BuiltInWindowFunctionExprandPartitionEvaluatorthat users wouldimplement. Internally we would implement
BuiltInWindowFunctionExprand
PartitionEvaluatorin terms of these new traits.Pros
Cons
A new API would mean introducing another api layer that needs to be tested and kept up to date.
The new API also might not expose all the functionality of the built in window functions if such functionality was added at a later date
Discussion
I am going to try and bash out a technical proof of concept of the first approach (exposing the existing APIs) and we can see how it would look. On the balance that is my preferred approach due to the power of the API and the similarities with
AggregateUDFsalamb commentedon Jun 9, 2023
Here is a proposed API #6617 showing how a User Defined Window function would work. Any feedback on the approach would be most appreciated.
While waiting for feedback on #6617 I plan to try and simplify some of the window function implementation as some small PRs