Skip to main content

datafusion_functions_aggregate/
nth_value.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Defines NTH_VALUE aggregate expression which may specify ordering requirement
19//! that can evaluated at runtime during query execution
20
21use std::collections::VecDeque;
22use std::mem::{size_of, size_of_val};
23use std::sync::Arc;
24
25use arrow::array::{ArrayRef, AsArray, StructArray, new_empty_array};
26use arrow::datatypes::{DataType, Field, FieldRef, Fields};
27
28use datafusion_common::utils::{SingleRowListArrayBuilder, get_row_at_idx};
29use datafusion_common::{
30    Result, ScalarValue, assert_or_internal_err, exec_err, not_impl_err,
31};
32use datafusion_expr::function::{AccumulatorArgs, StateFieldsArgs};
33use datafusion_expr::utils::format_state_name;
34use datafusion_expr::{
35    Accumulator, AggregateUDFImpl, Documentation, ExprFunctionExt, ReversedUDAF,
36    Signature, SortExpr, Volatility, lit,
37};
38use datafusion_functions_aggregate_common::merge_arrays::merge_ordered_arrays;
39use datafusion_functions_aggregate_common::utils::ordering_fields;
40use datafusion_macros::user_doc;
41use datafusion_physical_expr::expressions::Literal;
42use datafusion_physical_expr_common::sort_expr::{LexOrdering, PhysicalSortExpr};
43
44create_func!(NthValueAgg, nth_value_udaf);
45
46/// Returns the nth value in a group of values.
47pub fn nth_value(
48    expr: datafusion_expr::Expr,
49    n: i64,
50    order_by: Vec<SortExpr>,
51) -> datafusion_expr::Expr {
52    let args = vec![expr, lit(n)];
53    if !order_by.is_empty() {
54        nth_value_udaf()
55            .call(args)
56            .order_by(order_by)
57            .build()
58            .unwrap()
59    } else {
60        nth_value_udaf().call(args)
61    }
62}
63
64#[user_doc(
65    doc_section(label = "Statistical Functions"),
66    description = "Returns the nth value in a group of values.",
67    syntax_example = "nth_value(expression, n ORDER BY expression)",
68    sql_example = r#"```sql
69> SELECT dept_id, salary, NTH_VALUE(salary, 2) OVER (PARTITION BY dept_id ORDER BY salary ASC) AS second_salary_by_dept
70  FROM employee;
71+---------+--------+-------------------------+
72| dept_id | salary | second_salary_by_dept   |
73+---------+--------+-------------------------+
74| 1       | 30000  | NULL                    |
75| 1       | 40000  | 40000                   |
76| 1       | 50000  | 40000                   |
77| 2       | 35000  | NULL                    |
78| 2       | 45000  | 45000                   |
79+---------+--------+-------------------------+
80```"#,
81    argument(
82        name = "expression",
83        description = "The column or expression to retrieve the nth value from."
84    ),
85    argument(
86        name = "n",
87        description = "The position (nth) of the value to retrieve, based on the ordering."
88    )
89)]
90/// Expression for a `NTH_VALUE(..., ... ORDER BY ...)` aggregation. In a multi
91/// partition setting, partial aggregations are computed for every partition,
92/// and then their results are merged.
93#[derive(Debug, PartialEq, Eq, Hash)]
94pub struct NthValueAgg {
95    signature: Signature,
96}
97
98impl NthValueAgg {
99    /// Create a new `NthValueAgg` aggregate function
100    pub fn new() -> Self {
101        Self {
102            signature: Signature::any(2, Volatility::Immutable),
103        }
104    }
105}
106
107impl Default for NthValueAgg {
108    fn default() -> Self {
109        Self::new()
110    }
111}
112
113impl AggregateUDFImpl for NthValueAgg {
114    fn name(&self) -> &str {
115        "nth_value"
116    }
117
118    fn signature(&self) -> &Signature {
119        &self.signature
120    }
121
122    fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
123        Ok(arg_types[0].clone())
124    }
125
126    fn accumulator(&self, acc_args: AccumulatorArgs) -> Result<Box<dyn Accumulator>> {
127        let n = match acc_args.exprs[1]
128            .downcast_ref::<Literal>()
129            .map(|lit| lit.value())
130        {
131            Some(ScalarValue::Int64(Some(value))) => {
132                if acc_args.is_reversed {
133                    -*value
134                } else {
135                    *value
136                }
137            }
138            _ => {
139                return not_impl_err!(
140                    "{} not supported for n: {}",
141                    self.name(),
142                    &acc_args.exprs[1]
143                );
144            }
145        };
146
147        let Some(ordering) = LexOrdering::new(acc_args.order_bys.to_vec()) else {
148            return TrivialNthValueAccumulator::try_new(
149                n,
150                acc_args.return_field.data_type(),
151            )
152            .map(|acc| Box::new(acc) as _);
153        };
154        let ordering_dtypes = ordering
155            .iter()
156            .map(|e| e.expr.data_type(acc_args.schema))
157            .collect::<Result<Vec<_>>>()?;
158
159        let data_type = acc_args.expr_fields[0].data_type();
160        NthValueAccumulator::try_new(n, data_type, &ordering_dtypes, ordering)
161            .map(|acc| Box::new(acc) as _)
162    }
163
164    fn state_fields(&self, args: StateFieldsArgs) -> Result<Vec<FieldRef>> {
165        let mut fields = vec![Field::new_list(
166            format_state_name(self.name(), "nth_value"),
167            // See COMMENTS.md to understand why nullable is set to true
168            Field::new_list_field(args.input_fields[0].data_type().clone(), true),
169            false,
170        )];
171        let orderings = args.ordering_fields.to_vec();
172        if !orderings.is_empty() {
173            fields.push(Field::new_list(
174                format_state_name(self.name(), "nth_value_orderings"),
175                Field::new_list_field(DataType::Struct(Fields::from(orderings)), true),
176                false,
177            ));
178        }
179        Ok(fields.into_iter().map(Arc::new).collect())
180    }
181
182    fn reverse_expr(&self) -> ReversedUDAF {
183        ReversedUDAF::Reversed(nth_value_udaf())
184    }
185
186    fn documentation(&self) -> Option<&Documentation> {
187        self.doc()
188    }
189}
190
191#[derive(Debug)]
192pub struct TrivialNthValueAccumulator {
193    /// The `N` value.
194    n: i64,
195    /// Stores entries in the `NTH_VALUE` result.
196    values: VecDeque<ScalarValue>,
197    /// Data types of the value.
198    datatype: DataType,
199}
200
201impl TrivialNthValueAccumulator {
202    /// Create a new order-insensitive NTH_VALUE accumulator based on the given
203    /// item data type.
204    pub fn try_new(n: i64, datatype: &DataType) -> Result<Self> {
205        // n cannot be 0
206        assert_or_internal_err!(
207            n != 0,
208            "Nth value indices are 1 based. 0 is invalid index"
209        );
210        Ok(Self {
211            n,
212            values: VecDeque::new(),
213            datatype: datatype.clone(),
214        })
215    }
216
217    /// Updates state, with the `values`. Fetch contains missing number of entries for state to be complete
218    /// None represents all of the new `values` need to be added to the state.
219    fn append_new_data(
220        &mut self,
221        values: &[ArrayRef],
222        fetch: Option<usize>,
223    ) -> Result<()> {
224        let n_row = values[0].len();
225        let n_to_add = if let Some(fetch) = fetch {
226            std::cmp::min(fetch, n_row)
227        } else {
228            n_row
229        };
230        for index in 0..n_to_add {
231            let mut row = get_row_at_idx(values, index)?;
232            self.values.push_back(row.swap_remove(0));
233            // At index 1, we have n index argument, which is constant.
234        }
235        Ok(())
236    }
237}
238
239impl Accumulator for TrivialNthValueAccumulator {
240    /// Updates its state with the `values`. Assumes data in the `values` satisfies the required
241    /// ordering for the accumulator (across consecutive batches, not just batch-wise).
242    fn update_batch(&mut self, values: &[ArrayRef]) -> Result<()> {
243        if !values.is_empty() {
244            let n_required = self.n.unsigned_abs() as usize;
245            let from_start = self.n > 0;
246            if from_start {
247                // direction is from start
248                let n_remaining = n_required.saturating_sub(self.values.len());
249                self.append_new_data(values, Some(n_remaining))?;
250            } else {
251                // direction is from end
252                self.append_new_data(values, None)?;
253                let start_offset = self.values.len().saturating_sub(n_required);
254                if start_offset > 0 {
255                    self.values.drain(0..start_offset);
256                }
257            }
258        }
259        Ok(())
260    }
261
262    fn merge_batch(&mut self, states: &[ArrayRef]) -> Result<()> {
263        if !states.is_empty() {
264            // First entry in the state is the aggregation result.
265            let n_required = self.n.unsigned_abs() as usize;
266            let array_agg_res = ScalarValue::convert_array_to_scalar_vec(&states[0])?;
267            for v in array_agg_res.into_iter().flatten() {
268                self.values.extend(v);
269                if self.values.len() > n_required {
270                    // There is enough data collected, can stop merging:
271                    break;
272                }
273            }
274        }
275        Ok(())
276    }
277
278    fn state(&mut self) -> Result<Vec<ScalarValue>> {
279        let mut values_cloned = self.values.clone();
280        let values_slice = values_cloned.make_contiguous();
281        Ok(vec![ScalarValue::List(ScalarValue::new_list_nullable(
282            values_slice,
283            &self.datatype,
284        ))])
285    }
286
287    fn evaluate(&mut self) -> Result<ScalarValue> {
288        let n_required = self.n.unsigned_abs() as usize;
289        let from_start = self.n > 0;
290        let nth_value_idx = if from_start {
291            // index is from start
292            let forward_idx = n_required - 1;
293            (forward_idx < self.values.len()).then_some(forward_idx)
294        } else {
295            // index is from end
296            self.values.len().checked_sub(n_required)
297        };
298        if let Some(idx) = nth_value_idx {
299            Ok(self.values[idx].clone())
300        } else {
301            ScalarValue::try_from(self.datatype.clone())
302        }
303    }
304
305    fn size(&self) -> usize {
306        size_of_val(self) + ScalarValue::size_of_vec_deque(&self.values)
307            - size_of_val(&self.values)
308            + size_of::<DataType>()
309    }
310}
311
312#[derive(Debug)]
313pub struct NthValueAccumulator {
314    /// The `N` value.
315    n: i64,
316    /// Stores entries in the `NTH_VALUE` result.
317    values: VecDeque<ScalarValue>,
318    /// Stores values of ordering requirement expressions corresponding to each
319    /// entry in `values`. This information is used when merging results from
320    /// different partitions. For detailed information how merging is done, see
321    /// [`merge_ordered_arrays`].
322    ordering_values: VecDeque<Vec<ScalarValue>>,
323    /// Stores datatypes of expressions inside values and ordering requirement
324    /// expressions.
325    datatypes: Vec<DataType>,
326    /// Stores the ordering requirement of the `Accumulator`.
327    ordering_req: LexOrdering,
328}
329
330impl NthValueAccumulator {
331    /// Create a new order-sensitive NTH_VALUE accumulator based on the given
332    /// item data type.
333    pub fn try_new(
334        n: i64,
335        datatype: &DataType,
336        ordering_dtypes: &[DataType],
337        ordering_req: LexOrdering,
338    ) -> Result<Self> {
339        // n cannot be 0
340        assert_or_internal_err!(
341            n != 0,
342            "Nth value indices are 1 based. 0 is invalid index"
343        );
344        let mut datatypes = vec![datatype.clone()];
345        datatypes.extend(ordering_dtypes.iter().cloned());
346        Ok(Self {
347            n,
348            values: VecDeque::new(),
349            ordering_values: VecDeque::new(),
350            datatypes,
351            ordering_req,
352        })
353    }
354
355    fn evaluate_orderings(&self) -> Result<ScalarValue> {
356        let fields = ordering_fields(&self.ordering_req, &self.datatypes[1..]);
357
358        let mut column_wise_ordering_values = vec![];
359        let num_columns = fields.len();
360        for i in 0..num_columns {
361            let column_values = self
362                .ordering_values
363                .iter()
364                .map(|x| x[i].clone())
365                .collect::<Vec<_>>();
366            let array = if column_values.is_empty() {
367                new_empty_array(fields[i].data_type())
368            } else {
369                ScalarValue::iter_to_array(column_values)?
370            };
371            column_wise_ordering_values.push(array);
372        }
373
374        let struct_field = Fields::from(fields);
375        let ordering_array =
376            StructArray::try_new(struct_field, column_wise_ordering_values, None)?;
377
378        Ok(SingleRowListArrayBuilder::new(Arc::new(ordering_array)).build_list_scalar())
379    }
380
381    fn evaluate_values(&self) -> ScalarValue {
382        let mut values_cloned = self.values.clone();
383        let values_slice = values_cloned.make_contiguous();
384        ScalarValue::List(ScalarValue::new_list_nullable(
385            values_slice,
386            &self.datatypes[0],
387        ))
388    }
389
390    /// Updates state, with the `values`. Fetch contains missing number of entries for state to be complete
391    /// None represents all of the new `values` need to be added to the state.
392    fn append_new_data(
393        &mut self,
394        values: &[ArrayRef],
395        fetch: Option<usize>,
396    ) -> Result<()> {
397        let n_row = values[0].len();
398        let n_to_add = if let Some(fetch) = fetch {
399            std::cmp::min(fetch, n_row)
400        } else {
401            n_row
402        };
403        for index in 0..n_to_add {
404            let row = get_row_at_idx(values, index)?;
405            self.values.push_back(row[0].clone());
406            // At index 1, we have n index argument.
407            // Ordering values cover starting from 2nd index to end
408            self.ordering_values.push_back(row[2..].to_vec());
409        }
410        Ok(())
411    }
412}
413
414impl Accumulator for NthValueAccumulator {
415    /// Updates its state with the `values`. Assumes data in the `values` satisfies the required
416    /// ordering for the accumulator (across consecutive batches, not just batch-wise).
417    fn update_batch(&mut self, values: &[ArrayRef]) -> Result<()> {
418        if values.is_empty() {
419            return Ok(());
420        }
421
422        let n_required = self.n.unsigned_abs() as usize;
423        let from_start = self.n > 0;
424        if from_start {
425            // direction is from start
426            let n_remaining = n_required.saturating_sub(self.values.len());
427            self.append_new_data(values, Some(n_remaining))?;
428        } else {
429            // direction is from end
430            self.append_new_data(values, None)?;
431            let start_offset = self.values.len().saturating_sub(n_required);
432            if start_offset > 0 {
433                self.values.drain(0..start_offset);
434                self.ordering_values.drain(0..start_offset);
435            }
436        }
437
438        Ok(())
439    }
440
441    fn merge_batch(&mut self, states: &[ArrayRef]) -> Result<()> {
442        if states.is_empty() {
443            return Ok(());
444        }
445        // Second entry stores values received for ordering requirement columns
446        // for each aggregation value inside NTH_VALUE list. For each `StructArray`
447        // inside this list, we will receive an `Array` that stores values received
448        // from its ordering requirement expression. This information is necessary
449        // during merging.
450        let Some(agg_orderings) = states[1].as_list_opt::<i32>() else {
451            return exec_err!("Expects to receive a list array");
452        };
453
454        // Stores NTH_VALUE results coming from each partition
455        let mut partition_values = vec![self.values.clone()];
456        // First entry in the state is the aggregation result.
457        let array_agg_res = ScalarValue::convert_array_to_scalar_vec(&states[0])?;
458        for v in array_agg_res.into_iter().flatten() {
459            partition_values.push(v.into());
460        }
461        // Stores ordering requirement expression results coming from each partition:
462        let mut partition_ordering_values = vec![self.ordering_values.clone()];
463        let orderings = ScalarValue::convert_array_to_scalar_vec(agg_orderings)?;
464        // Extract value from struct to ordering_rows for each group/partition:
465        for partition_ordering_rows in orderings.into_iter().flatten() {
466            let ordering_values = partition_ordering_rows.into_iter().map(|ordering_row| {
467                let ScalarValue::Struct(s_array) = ordering_row else {
468                    return exec_err!(
469                        "Expects to receive ScalarValue::Struct(Some(..), _) but got: {:?}",
470                        ordering_row.data_type()
471                    );
472                };
473                s_array
474                    .columns()
475                    .iter()
476                    .map(|column| ScalarValue::try_from_array(column, 0))
477                    .collect()
478            }).collect::<Result<VecDeque<_>>>()?;
479            partition_ordering_values.push(ordering_values);
480        }
481
482        let sort_options = self
483            .ordering_req
484            .iter()
485            .map(|sort_expr| sort_expr.options)
486            .collect::<Vec<_>>();
487        let (new_values, new_orderings) = merge_ordered_arrays(
488            &mut partition_values,
489            &mut partition_ordering_values,
490            &sort_options,
491        )?;
492        self.values = new_values.into();
493        self.ordering_values = new_orderings.into();
494        Ok(())
495    }
496
497    fn state(&mut self) -> Result<Vec<ScalarValue>> {
498        Ok(vec![self.evaluate_values(), self.evaluate_orderings()?])
499    }
500
501    fn evaluate(&mut self) -> Result<ScalarValue> {
502        let n_required = self.n.unsigned_abs() as usize;
503        let from_start = self.n > 0;
504        let nth_value_idx = if from_start {
505            // index is from start
506            let forward_idx = n_required - 1;
507            (forward_idx < self.values.len()).then_some(forward_idx)
508        } else {
509            // index is from end
510            self.values.len().checked_sub(n_required)
511        };
512        if let Some(idx) = nth_value_idx {
513            Ok(self.values[idx].clone())
514        } else {
515            ScalarValue::try_from(self.datatypes[0].clone())
516        }
517    }
518
519    fn size(&self) -> usize {
520        let mut total = size_of_val(self) + ScalarValue::size_of_vec_deque(&self.values)
521            - size_of_val(&self.values);
522
523        // Add size of the `self.ordering_values`
524        total += size_of::<Vec<ScalarValue>>() * self.ordering_values.capacity();
525        for row in &self.ordering_values {
526            total += ScalarValue::size_of_vec(row) - size_of_val(row);
527        }
528
529        // Add size of the `self.datatypes`
530        total += size_of::<DataType>() * self.datatypes.capacity();
531        for dtype in &self.datatypes {
532            total += dtype.size() - size_of_val(dtype);
533        }
534
535        // Add size of the `self.ordering_req`
536        total += size_of::<PhysicalSortExpr>() * self.ordering_req.capacity();
537        // TODO: Calculate size of each `PhysicalSortExpr` more accurately.
538        total
539    }
540}