1use arrow::{
21 array::{Array, BooleanArray, UInt64Array, UInt64Builder},
22 compute::take,
23 datatypes::{DataType, FieldRef},
24};
25use datafusion_common::{Result, exec_err, plan_err};
26use datafusion_expr::{
27 ColumnarValue, Documentation, HigherOrderFunctionArgs, HigherOrderReturnFieldArgs,
28 HigherOrderSignature, HigherOrderUDFImpl, LambdaParametersProgress, ValueOrLambda,
29 Volatility,
30};
31use datafusion_macros::user_doc;
32use std::sync::Arc;
33
34use crate::lambda_utils::{
35 EvaluatedListLambda, SingleListLambdaResult, coerce_single_list_arg,
36 evaluate_single_list_predicate, single_list_lambda_parameters, value_lambda_pair,
37};
38
39make_higher_order_function_expr_and_func!(
40 ArrayFirst,
41 array_first,
42 array lambda,
43 "returns the first element of an array that satisfies the predicate",
44 array_first_higher_order_function
45);
46
47#[user_doc(
48 doc_section(label = "Array Functions"),
49 description = "Returns the first element of an array that satisfies the given predicate. Returns null if the array is empty or no element matches. A predicate that returns null for an element is treated as not matching.",
50 syntax_example = "array_first(array, predicate)",
51 sql_example = r#"```sql
52> select array_first([1, 2, 3, 4], x -> x > 2);
53+----------------------------------------+
54| array_first([1,2,3,4],x -> x > 2) |
55+----------------------------------------+
56| 3 |
57+----------------------------------------+
58```"#,
59 argument(
60 name = "array",
61 description = "Array expression. Can be a constant, column, or function, and any combination of array operators."
62 ),
63 argument(
64 name = "predicate",
65 description = "Lambda predicate that returns a boolean. The first element for which it returns true is returned."
66 )
67)]
68#[derive(Debug, PartialEq, Eq, Hash)]
69pub struct ArrayFirst {
70 signature: HigherOrderSignature,
71 aliases: Vec<String>,
72}
73
74impl Default for ArrayFirst {
75 fn default() -> Self {
76 Self::new()
77 }
78}
79
80impl ArrayFirst {
81 pub fn new() -> Self {
82 Self {
83 signature: HigherOrderSignature::exact(
84 vec![ValueOrLambda::Value(()), ValueOrLambda::Lambda(())],
85 Volatility::Immutable,
86 ),
87 aliases: vec![String::from("list_first")],
88 }
89 }
90}
91
92impl HigherOrderUDFImpl for ArrayFirst {
93 fn name(&self) -> &str {
94 "array_first"
95 }
96
97 fn aliases(&self) -> &[String] {
98 &self.aliases
99 }
100
101 fn signature(&self) -> &HigherOrderSignature {
102 &self.signature
103 }
104
105 fn coerce_value_types(&self, arg_types: &[DataType]) -> Result<Vec<DataType>> {
106 coerce_single_list_arg(self.name(), arg_types)
107 }
108
109 fn lambda_parameters(
110 &self,
111 _step: usize,
112 fields: &[ValueOrLambda<FieldRef, Option<FieldRef>>],
113 ) -> Result<LambdaParametersProgress> {
114 single_list_lambda_parameters(self.name(), fields)
115 }
116
117 fn return_field_from_args(
118 &self,
119 args: HigherOrderReturnFieldArgs,
120 ) -> Result<FieldRef> {
121 let (list, _lambda) = value_lambda_pair(self.name(), args.arg_fields)?;
122
123 let element_field = match list.data_type() {
124 DataType::List(field) | DataType::LargeList(field) => field,
125 other => {
126 return plan_err!(
127 "{} expected a list as first argument, got {other}",
128 self.name()
129 );
130 }
131 };
132
133 Ok(Arc::new(
136 element_field
137 .as_ref()
138 .clone()
139 .with_name("")
140 .with_nullable(true),
141 ))
142 }
143
144 fn invoke_with_args(&self, args: HigherOrderFunctionArgs) -> Result<ColumnarValue> {
145 let evaluated = match evaluate_single_list_predicate(self.name(), &args)? {
146 SingleListLambdaResult::EarlyReturn(v) => return Ok(v),
147 SingleListLambdaResult::Ready(v) => v,
148 };
149
150 let predicate = evaluated.boolean_predicate(self.name())?;
151 let indices = match evaluated.original_list.data_type() {
152 DataType::List(_) | DataType::LargeList(_) => {
153 first_match_indices(&evaluated, &predicate)
154 }
155 other => return exec_err!("expected list, got {other}"),
156 };
157
158 let result = take(evaluated.flattened_values.as_ref(), &indices, None)?;
159 Ok(ColumnarValue::Array(result))
160 }
161
162 fn documentation(&self) -> Option<&Documentation> {
163 self.doc()
164 }
165}
166
167fn first_match_indices(
174 evaluated: &EvaluatedListLambda,
175 predicate: &BooleanArray,
176) -> UInt64Array {
177 let mut builder = UInt64Builder::with_capacity(evaluated.len());
178
179 for i in 0..evaluated.len() {
180 let (start, end) = evaluated.row_range(i);
181
182 match (start..end).find(|&j| predicate.is_valid(j) && predicate.value(j)) {
183 Some(j) => builder.append_value(j as u64),
184 None => builder.append_null(),
185 }
186 }
187
188 builder.finish()
189}
190
191#[cfg(test)]
192mod tests {
193 use arrow::{
194 array::{Array, AsArray, Int32Array, StringArray},
195 buffer::{NullBuffer, OffsetBuffer},
196 datatypes::Int32Type,
197 };
198
199 use crate::array_first::array_first_higher_order_function;
200 use crate::lambda_utils::test_utils::{
201 create_i32_large_list, create_i32_list, eval_hof_on_i32_list,
202 eval_hof_on_i32_list_with_outer, v,
203 };
204 use datafusion_common::Result;
205 use datafusion_expr::{col, lit};
206
207 fn first_greater_than_two(
208 list: impl Array + Clone + 'static,
209 ) -> Result<arrow::array::ArrayRef> {
210 eval_hof_on_i32_list(array_first_higher_order_function(), list, v().gt(lit(2i32)))
211 }
212
213 fn first_where_hundred_div_gt_five(
215 list: impl Array + Clone + 'static,
216 ) -> Result<arrow::array::ArrayRef> {
217 eval_hof_on_i32_list(
218 array_first_higher_order_function(),
219 list,
220 (lit(100i32) / v()).gt(lit(5i32)),
221 )
222 }
223
224 #[test]
225 fn test_first_basic() -> Result<()> {
226 let list = create_i32_list(
227 vec![1, 2, 3, 4, 5],
228 OffsetBuffer::<i32>::from_lengths(vec![5]),
229 None,
230 );
231 let res = first_greater_than_two(list)?;
232 assert_eq!(
233 res.as_primitive::<Int32Type>(),
234 &Int32Array::from(vec![Some(3)])
235 );
236 Ok(())
237 }
238
239 #[test]
240 fn test_first_no_match_is_null() -> Result<()> {
241 let list =
242 create_i32_list(vec![1, 2], OffsetBuffer::<i32>::from_lengths(vec![2]), None);
243 let res = first_greater_than_two(list)?;
244 assert_eq!(
245 res.as_primitive::<Int32Type>(),
246 &Int32Array::from(vec![None])
247 );
248 Ok(())
249 }
250
251 #[test]
252 fn test_first_empty_array_is_null() -> Result<()> {
253 let list = create_i32_list(
254 Vec::<i32>::new(),
255 OffsetBuffer::<i32>::from_lengths(vec![0]),
256 None,
257 );
258 let res = first_greater_than_two(list)?;
259 assert_eq!(
260 res.as_primitive::<Int32Type>(),
261 &Int32Array::from(vec![None])
262 );
263 Ok(())
264 }
265
266 #[test]
267 fn test_first_multiple_sublists() -> Result<()> {
268 let list = create_i32_list(
270 vec![1, 5, 2, 4, 3, 1, 2],
271 OffsetBuffer::<i32>::from_lengths(vec![2, 3, 2]),
272 None,
273 );
274 let res = first_greater_than_two(list)?;
275 assert_eq!(
276 res.as_primitive::<Int32Type>(),
277 &Int32Array::from(vec![Some(5), Some(4), None])
278 );
279 Ok(())
280 }
281
282 #[test]
283 fn test_first_null_predicate_element_is_skipped() -> Result<()> {
284 let list = create_i32_list(
287 Int32Array::from(vec![Some(1), None, Some(4)]),
288 OffsetBuffer::<i32>::from_lengths(vec![3]),
289 None,
290 );
291 let res = first_greater_than_two(list)?;
292 assert_eq!(
293 res.as_primitive::<Int32Type>(),
294 &Int32Array::from(vec![Some(4)])
295 );
296 Ok(())
297 }
298
299 #[test]
300 fn test_first_matched_null_element_is_returned() -> Result<()> {
301 let list = create_i32_list(
304 Int32Array::from(vec![Some(1), None, Some(3)]),
305 OffsetBuffer::<i32>::from_lengths(vec![3]),
306 None,
307 );
308 let res = eval_hof_on_i32_list(
309 array_first_higher_order_function(),
310 list,
311 v().is_null(),
312 )?;
313 assert_eq!(
314 res.as_primitive::<Int32Type>(),
315 &Int32Array::from(vec![None])
316 );
317 Ok(())
318 }
319
320 #[test]
323 fn test_first_does_not_evaluate_predicate_on_null_row_values() -> Result<()> {
324 let list = create_i32_list(
325 vec![1, 2, 0, 4, 5],
326 OffsetBuffer::<i32>::from_lengths(vec![3, 2]),
327 Some(NullBuffer::from(vec![false, true])),
328 );
329 let res = first_where_hundred_div_gt_five(list)?;
330 assert_eq!(
331 res.as_primitive::<Int32Type>(),
332 &Int32Array::from(vec![None, Some(4)])
333 );
334 Ok(())
335 }
336
337 #[test]
339 fn test_first_does_not_evaluate_predicate_on_unreachable_values() -> Result<()> {
340 let list = create_i32_list(
342 vec![0, 4, 5, 50, 100],
343 OffsetBuffer::<i32>::from_lengths(vec![1, 2, 2]),
344 None,
345 )
346 .slice(1, 2);
347 let res = first_where_hundred_div_gt_five(list)?;
348 assert_eq!(
350 res.as_primitive::<Int32Type>(),
351 &Int32Array::from(vec![Some(4), None])
352 );
353 Ok(())
354 }
355
356 #[test]
357 fn test_first_eagerly_evaluates_predicate_after_match() {
358 let list =
361 create_i32_list(vec![4, 0], OffsetBuffer::<i32>::from_lengths(vec![2]), None);
362
363 let err = first_where_hundred_div_gt_five(list).unwrap_err();
364 assert!(
365 err.to_string().contains("Divide by zero"),
366 "unexpected error: {err}"
367 );
368 }
369
370 #[test]
371 fn test_first_large_list_parity() -> Result<()> {
372 let list = create_i32_large_list(
373 vec![1, 2, 3, 4, 5],
374 OffsetBuffer::<i64>::from_lengths(vec![5]),
375 None,
376 );
377 let res = first_greater_than_two(list)?;
378 assert_eq!(
379 res.as_primitive::<Int32Type>(),
380 &Int32Array::from(vec![Some(3)])
381 );
382 Ok(())
383 }
384
385 #[test]
386 fn test_first_captured_outer_column() -> Result<()> {
387 let list = create_i32_list(
388 vec![1, 50, 4, 50, 7, 50],
389 OffsetBuffer::<i32>::from_lengths(vec![2, 2, 2]),
390 None,
391 );
392 let number = Int32Array::from(vec![10, 40, 60]);
393 let res = eval_hof_on_i32_list_with_outer(
394 array_first_higher_order_function(),
395 list,
396 number,
397 v().gt(col("number")),
398 )?;
399 assert_eq!(
400 res.as_primitive::<Int32Type>(),
401 &Int32Array::from(vec![Some(50), Some(50), None])
402 );
403 Ok(())
404 }
405
406 #[test]
407 fn test_first_string_elements() -> Result<()> {
408 use arrow::array::ListArray;
409 use arrow::datatypes::{DataType, Field};
410 use datafusion_expr::Expr;
411 use datafusion_expr::expr::LambdaVariable;
412 use std::sync::Arc;
413
414 let values = StringArray::from(vec!["a", "bb", "ccc"]);
416 let list = ListArray::new(
417 Arc::new(Field::new_list_field(DataType::Utf8, true)),
418 OffsetBuffer::<i32>::from_lengths(vec![3]),
419 Arc::new(values),
420 None,
421 );
422
423 let x = Expr::LambdaVariable(LambdaVariable::new(
424 "v".to_string(),
425 Some(Arc::new(Field::new("v", DataType::Utf8, true))),
426 ));
427 let body = x.gt(lit("a"));
428
429 let res = eval_hof_on_i32_list(array_first_higher_order_function(), list, body)?;
430 assert_eq!(res.as_string::<i32>(), &StringArray::from(vec![Some("bb")]));
431 Ok(())
432 }
433}