1use self::WindownFunction::*;
2use super::{Entity, query::QueryExt};
3use zino_core::model::Query;
4
5#[derive(Debug, Clone, Copy)]
7#[non_exhaustive]
8enum WindownFunction<E: Entity> {
9 Count(E::Column),
11 Sum(E::Column),
13 Avg(E::Column),
15 Min(E::Column),
17 Max(E::Column),
19 RowNumber,
21 Rank,
23 DenseRank,
25 PercentRank,
27 CumeDist,
29 Ntile(usize),
31 Lag(E::Column, usize),
33 Lead(E::Column, usize),
35 FirstValue(E::Column),
37 LastValue(E::Column),
39 NthValue(E::Column, usize),
41}
42
43#[derive(Debug, Clone, Copy)]
61pub struct Window<E: Entity> {
62 function: WindownFunction<E>,
64 partition: E::Column,
66 order: Option<(E::Column, bool)>,
68}
69
70impl<E: Entity> Window<E> {
71 #[inline]
73 pub fn count(col: E::Column, partition: E::Column) -> Self {
74 Self {
75 function: Count(col),
76 partition,
77 order: None,
78 }
79 }
80
81 #[inline]
83 pub fn sum(col: E::Column, partition: E::Column) -> Self {
84 Self {
85 function: Sum(col),
86 partition,
87 order: None,
88 }
89 }
90
91 #[inline]
93 pub fn avg(col: E::Column, partition: E::Column) -> Self {
94 Self {
95 function: Avg(col),
96 partition,
97 order: None,
98 }
99 }
100
101 #[inline]
103 pub fn min(col: E::Column, partition: E::Column) -> Self {
104 Self {
105 function: Min(col),
106 partition,
107 order: None,
108 }
109 }
110
111 #[inline]
113 pub fn max(col: E::Column, partition: E::Column) -> Self {
114 Self {
115 function: Max(col),
116 partition,
117 order: None,
118 }
119 }
120
121 #[inline]
123 pub fn row_number(partition: E::Column) -> Self {
124 Self {
125 function: RowNumber,
126 partition,
127 order: None,
128 }
129 }
130
131 #[inline]
133 pub fn rank(partition: E::Column) -> Self {
134 Self {
135 function: Rank,
136 partition,
137 order: None,
138 }
139 }
140
141 #[inline]
143 pub fn dense_rank(partition: E::Column) -> Self {
144 Self {
145 function: DenseRank,
146 partition,
147 order: None,
148 }
149 }
150
151 #[inline]
153 pub fn percent_rank(partition: E::Column) -> Self {
154 Self {
155 function: PercentRank,
156 partition,
157 order: None,
158 }
159 }
160
161 #[inline]
163 pub fn cume_dist(partition: E::Column) -> Self {
164 Self {
165 function: CumeDist,
166 partition,
167 order: None,
168 }
169 }
170
171 #[inline]
173 pub fn ntile(num_buckets: usize, partition: E::Column) -> Self {
174 Self {
175 function: Ntile(num_buckets),
176 partition,
177 order: None,
178 }
179 }
180
181 #[inline]
183 pub fn lag(col: E::Column, offset: usize, partition: E::Column) -> Self {
184 Self {
185 function: Lag(col, offset),
186 partition,
187 order: None,
188 }
189 }
190
191 #[inline]
193 pub fn lead(col: E::Column, offset: usize, partition: E::Column) -> Self {
194 Self {
195 function: Lead(col, offset),
196 partition,
197 order: None,
198 }
199 }
200
201 #[inline]
203 pub fn first_value(col: E::Column, partition: E::Column) -> Self {
204 Self {
205 function: FirstValue(col),
206 partition,
207 order: None,
208 }
209 }
210
211 #[inline]
213 pub fn last_value(col: E::Column, partition: E::Column) -> Self {
214 Self {
215 function: LastValue(col),
216 partition,
217 order: None,
218 }
219 }
220
221 #[inline]
223 pub fn nth_value(col: E::Column, n: usize, partition: E::Column) -> Self {
224 Self {
225 function: NthValue(col, n),
226 partition,
227 order: None,
228 }
229 }
230
231 #[inline]
233 pub fn order_by(mut self, col: E::Column, descending: bool) -> Self {
234 self.order = Some((col, descending));
235 self
236 }
237
238 #[inline]
240 pub fn order_asc(mut self, col: E::Column) -> Self {
241 self.order = Some((col, false));
242 self
243 }
244
245 #[inline]
247 pub fn order_desc(mut self, col: E::Column) -> Self {
248 self.order = Some((col, true));
249 self
250 }
251
252 pub(super) fn expr(&self) -> String {
254 let partition_col_name = E::format_column(&self.partition);
255 let partition = Query::format_field(&partition_col_name);
256 let sort = self
257 .order
258 .as_ref()
259 .map(|(col, descending)| {
260 let col_name = E::format_column(col);
261 let sort_field = Query::format_field(&col_name);
262 if *descending {
263 format!(" ORDER BY {sort_field} DESC")
264 } else {
265 format!(" ORDER BY {sort_field} ASC")
266 }
267 })
268 .unwrap_or_default();
269 match &self.function {
270 Count(col) => {
271 let col_name = E::format_column(col);
272 let field = Query::format_field(&col_name);
273 format!("count({field}) OVER (PARTITION BY {partition}{sort})")
274 }
275 Sum(col) => {
276 let col_name = E::format_column(col);
277 let field = Query::format_field(&col_name);
278 format!("sum({field}) OVER (PARTITION BY {partition}{sort})")
279 }
280 Avg(col) => {
281 let col_name = E::format_column(col);
282 let field = Query::format_field(&col_name);
283 format!("avg({field}) OVER (PARTITION BY {partition}{sort})")
284 }
285 Min(col) => {
286 let col_name = E::format_column(col);
287 let field = Query::format_field(&col_name);
288 format!("min({field}) OVER (PARTITION BY {partition}{sort})")
289 }
290 Max(col) => {
291 let col_name = E::format_column(col);
292 let field = Query::format_field(&col_name);
293 format!("max({field}) OVER (PARTITION BY {partition}{sort})")
294 }
295 RowNumber => {
296 format!("row_number() OVER (PARTITION BY {partition}{sort})")
297 }
298 Rank => {
299 format!("rank() OVER (PARTITION BY {partition}{sort})")
300 }
301 DenseRank => {
302 format!("dense_rank() OVER (PARTITION BY {partition}{sort})")
303 }
304 PercentRank => {
305 format!("percent_rank() OVER (PARTITION BY {partition}{sort})")
306 }
307 CumeDist => {
308 format!("cume_dist() OVER (PARTITION BY {partition}{sort})")
309 }
310 Ntile(n) => {
311 format!("ntile({n}) OVER (PARTITION BY {partition}{sort})")
312 }
313 Lag(col, offset) => {
314 let col_name = E::format_column(col);
315 let field = Query::format_field(&col_name);
316 format!("lag({field}, {offset}) OVER (PARTITION BY {partition}{sort})")
317 }
318 Lead(col, offset) => {
319 let col_name = E::format_column(col);
320 let field = Query::format_field(&col_name);
321 format!("lead({field}, {offset}) OVER (PARTITION BY {partition}{sort})")
322 }
323 FirstValue(col) => {
324 let col_name = E::format_column(col);
325 let field = Query::format_field(&col_name);
326 format!("first_value({field}) OVER (PARTITION BY {partition}{sort})")
327 }
328 LastValue(col) => {
329 let col_name = E::format_column(col);
330 let field = Query::format_field(&col_name);
331 format!("last_value({field}) OVER (PARTITION BY {partition}{sort})")
332 }
333 NthValue(col, n) => {
334 let col_name = E::format_column(col);
335 let field = Query::format_field(&col_name);
336 format!("nth_value({field}, {n}) OVER (PARTITION BY {partition}{sort})")
337 }
338 }
339 }
340}