vortex_array/expr/exprs.rs
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! Factory functions for creating [`Expression`]s from scalar function vtables.
5
6use std::sync::Arc;
7use std::sync::LazyLock;
8
9use vortex_error::VortexExpect;
10use vortex_error::vortex_panic;
11use vortex_utils::iter::ReduceBalancedIterExt;
12
13use crate::aggregate_fn::NumericalAggregateOpts;
14use crate::dtype::DType;
15use crate::dtype::FieldName;
16use crate::dtype::FieldNames;
17use crate::dtype::Nullability;
18use crate::expr::Expression;
19use crate::scalar::Scalar;
20use crate::scalar::ScalarValue;
21use crate::scalar_fn::EmptyOptions;
22use crate::scalar_fn::ScalarFnVTableExt;
23use crate::scalar_fn::fns::between::Between;
24use crate::scalar_fn::fns::between::BetweenOptions;
25use crate::scalar_fn::fns::binary::Binary;
26use crate::scalar_fn::fns::byte_length::ByteLength;
27use crate::scalar_fn::fns::case_when::CaseWhen;
28use crate::scalar_fn::fns::case_when::CaseWhenOptions;
29use crate::scalar_fn::fns::cast::Cast;
30use crate::scalar_fn::fns::dynamic::DynamicComparison;
31use crate::scalar_fn::fns::dynamic::DynamicComparisonExpr;
32use crate::scalar_fn::fns::dynamic::Rhs;
33use crate::scalar_fn::fns::ext_storage::ExtStorage;
34use crate::scalar_fn::fns::fill_null::FillNull;
35use crate::scalar_fn::fns::get_item::GetItem;
36use crate::scalar_fn::fns::is_not_null::IsNotNull;
37use crate::scalar_fn::fns::is_null::IsNull;
38use crate::scalar_fn::fns::like::Like;
39use crate::scalar_fn::fns::like::LikeOptions;
40use crate::scalar_fn::fns::list_contains::ListContains;
41use crate::scalar_fn::fns::list_length::ListLength;
42use crate::scalar_fn::fns::list_sum::ListSum;
43use crate::scalar_fn::fns::literal::Literal;
44use crate::scalar_fn::fns::mask::Mask;
45use crate::scalar_fn::fns::merge::DuplicateHandling;
46use crate::scalar_fn::fns::merge::Merge;
47use crate::scalar_fn::fns::not::Not;
48use crate::scalar_fn::fns::operators::CompareOperator;
49use crate::scalar_fn::fns::operators::Operator;
50use crate::scalar_fn::fns::pack::Pack;
51use crate::scalar_fn::fns::pack::PackOptions;
52use crate::scalar_fn::fns::root::Root;
53use crate::scalar_fn::fns::select::FieldSelection;
54use crate::scalar_fn::fns::select::Select;
55use crate::scalar_fn::fns::variant_get::VariantGet;
56use crate::scalar_fn::fns::variant_get::VariantGetOptions;
57use crate::scalar_fn::fns::variant_get::VariantPath;
58use crate::scalar_fn::fns::zip::Zip;
59
60static ROOT: LazyLock<Expression> = LazyLock::new(|| {
61 Root.try_new_expr(EmptyOptions, vec![])
62 .vortex_expect("Creating root() shouldn't fail")
63});
64
65/// Creates an expression that references the root scope.
66///
67/// Returns the entire input array as passed to the expression evaluator.
68/// This is commonly used as the starting point for field access and other operations.
69pub fn root() -> Expression {
70 ROOT.clone()
71}
72
73/// Return whether the expression is a root expression.
74pub fn is_root(expr: &Expression) -> bool {
75 // root doesn't have any children, and scalar_fns have distinct ids
76 // so we should almost always hit this eq check
77 (expr.scalar_fn().id() == ROOT.scalar_fn().id()) || expr.is::<Root>()
78}
79
80// ---- Literal ----
81
82/// Create a new `Literal` expression from a type that coerces to `Scalar`.
83///
84///
85/// ## Example usage
86///
87/// ```
88/// use vortex_array::arrays::PrimitiveArray;
89/// use vortex_array::dtype::Nullability;
90/// use vortex_array::expr::lit;
91/// use vortex_array::scalar_fn::fns::literal::Literal;
92/// use vortex_array::scalar::Scalar;
93///
94/// let number = lit(34i32);
95///
96/// let scalar = number.as_::<Literal>();
97/// assert_eq!(scalar, &Scalar::primitive(34i32, Nullability::NonNullable));
98/// ```
99pub fn lit(value: impl Into<Scalar>) -> Expression {
100 Literal.new_expr(value.into(), [])
101}
102
103// ---- GetItem / Col ----
104
105/// Creates an expression that accesses a field from the root array.
106///
107/// Equivalent to `get_item(field, root())` - extracts a named field from the input array.
108///
109/// ```rust
110/// # use vortex_array::expr::col;
111/// let expr = col("name");
112/// ```
113pub fn col(field: impl Into<FieldName>) -> Expression {
114 GetItem.new_expr(field.into(), vec![root()])
115}
116
117/// Creates an expression that extracts a named field from a struct expression.
118///
119/// Accesses the specified field from the result of the child expression.
120///
121/// ```rust
122/// # use vortex_array::expr::{get_item, root};
123/// let expr = get_item("user_id", root());
124/// ```
125pub fn get_item(field: impl Into<FieldName>, child: Expression) -> Expression {
126 GetItem.new_expr(field.into(), vec![child])
127}
128
129// ---- VariantGet ----
130
131/// Creates an expression that extracts a path from a Variant expression.
132///
133/// Missing paths, traversal mismatches, and failed casts return null. When `dtype` is `None`,
134/// results are nullable Variant values; otherwise results are nullable values of `dtype`.
135pub fn variant_get(
136 child: Expression,
137 path: impl Into<VariantPath>,
138 dtype: Option<DType>,
139) -> Expression {
140 VariantGet.new_expr(VariantGetOptions::new(path.into(), dtype), vec![child])
141}
142
143// ---- CaseWhen ----
144
145/// Creates a CASE WHEN expression with one WHEN/THEN pair and an ELSE value.
146pub fn case_when(
147 condition: Expression,
148 then_value: Expression,
149 else_value: Expression,
150) -> Expression {
151 let options = CaseWhenOptions {
152 num_when_then_pairs: 1,
153 has_else: true,
154 };
155 CaseWhen.new_expr(options, [condition, then_value, else_value])
156}
157
158/// Creates a CASE WHEN expression with one WHEN/THEN pair and no ELSE value.
159pub fn case_when_no_else(condition: Expression, then_value: Expression) -> Expression {
160 let options = CaseWhenOptions {
161 num_when_then_pairs: 1,
162 has_else: false,
163 };
164 CaseWhen.new_expr(options, [condition, then_value])
165}
166
167/// Creates an n-ary CASE WHEN expression from WHEN/THEN pairs and an optional ELSE value.
168pub fn nested_case_when(
169 when_then_pairs: Vec<(Expression, Expression)>,
170 else_value: Option<Expression>,
171) -> Expression {
172 assert!(
173 !when_then_pairs.is_empty(),
174 "nested_case_when requires at least one when/then pair"
175 );
176
177 let has_else = else_value.is_some();
178 let mut children = Vec::with_capacity(when_then_pairs.len() * 2 + usize::from(has_else));
179 for (condition, then_value) in &when_then_pairs {
180 children.push(condition.clone());
181 children.push(then_value.clone());
182 }
183 if let Some(else_expr) = else_value {
184 children.push(else_expr);
185 }
186
187 let Ok(num_when_then_pairs) = u32::try_from(when_then_pairs.len()) else {
188 vortex_panic!("nested_case_when has too many when/then pairs");
189 };
190 let options = CaseWhenOptions {
191 num_when_then_pairs,
192 has_else,
193 };
194 CaseWhen.new_expr(options, children)
195}
196
197// ---- Binary operators ----
198
199/// Create a new [`Binary`] using the [`Eq`](Operator::Eq) operator.
200///
201/// ## Example usage
202///
203/// ```
204/// # use vortex_array::arrays::{BoolArray, PrimitiveArray};
205/// # use vortex_array::arrays::bool::BoolArrayExt;
206/// # use vortex_array::IntoArray;
207/// # use vortex_array::{VortexSessionExecute, array_session};
208/// # use vortex_array::validity::Validity;
209/// # use vortex_buffer::buffer;
210/// # use vortex_array::expr::{eq, root, lit};
211/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
212/// let result = xs.into_array().apply(&eq(root(), lit(3))).unwrap();
213/// let mut ctx = array_session().create_execution_ctx();
214///
215/// assert_eq!(
216/// result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
217/// BoolArray::from_iter(vec![false, false, true]).to_bit_buffer(),
218/// );
219/// ```
220pub fn eq(lhs: Expression, rhs: Expression) -> Expression {
221 Binary
222 .try_new_expr(Operator::Eq, [lhs, rhs])
223 .vortex_expect("Failed to create Eq binary expression")
224}
225
226/// Create a new [`Binary`] using the [`NotEq`](Operator::NotEq) operator.
227///
228/// ## Example usage
229///
230/// ```
231/// # use vortex_array::arrays::{BoolArray, PrimitiveArray};
232/// # use vortex_array::arrays::bool::BoolArrayExt;
233/// # use vortex_array::IntoArray;
234/// # use vortex_array::{VortexSessionExecute, array_session};
235/// # use vortex_array::validity::Validity;
236/// # use vortex_buffer::buffer;
237/// # use vortex_array::expr::{root, lit, not_eq};
238/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
239/// let result = xs.into_array().apply(¬_eq(root(), lit(3))).unwrap();
240/// let mut ctx = array_session().create_execution_ctx();
241///
242/// assert_eq!(
243/// result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
244/// BoolArray::from_iter(vec![true, true, false]).to_bit_buffer(),
245/// );
246/// ```
247pub fn not_eq(lhs: Expression, rhs: Expression) -> Expression {
248 Binary
249 .try_new_expr(Operator::NotEq, [lhs, rhs])
250 .vortex_expect("Failed to create NotEq binary expression")
251}
252
253/// Create a new [`Binary`] using the [`Gte`](Operator::Gte) operator.
254///
255/// ## Example usage
256///
257/// ```
258/// # use vortex_array::arrays::{BoolArray, PrimitiveArray };
259/// # use vortex_array::arrays::bool::BoolArrayExt;
260/// # use vortex_array::IntoArray;
261/// # use vortex_array::{VortexSessionExecute, array_session};
262/// # use vortex_array::validity::Validity;
263/// # use vortex_buffer::buffer;
264/// # use vortex_array::expr::{gt_eq, root, lit};
265/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
266/// let result = xs.into_array().apply(>_eq(root(), lit(3))).unwrap();
267/// let mut ctx = array_session().create_execution_ctx();
268///
269/// assert_eq!(
270/// result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
271/// BoolArray::from_iter(vec![false, false, true]).to_bit_buffer(),
272/// );
273/// ```
274pub fn gt_eq(lhs: Expression, rhs: Expression) -> Expression {
275 Binary
276 .try_new_expr(Operator::Gte, [lhs, rhs])
277 .vortex_expect("Failed to create Gte binary expression")
278}
279
280/// Create a new [`Binary`] using the [`Gt`](Operator::Gt) operator.
281///
282/// ## Example usage
283///
284/// ```
285/// # use vortex_array::arrays::{BoolArray, PrimitiveArray };
286/// # use vortex_array::arrays::bool::BoolArrayExt;
287/// # use vortex_array::IntoArray;
288/// # use vortex_array::{VortexSessionExecute, array_session};
289/// # use vortex_array::validity::Validity;
290/// # use vortex_buffer::buffer;
291/// # use vortex_array::expr::{gt, root, lit};
292/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
293/// let result = xs.into_array().apply(>(root(), lit(2))).unwrap();
294/// let mut ctx = array_session().create_execution_ctx();
295///
296/// assert_eq!(
297/// result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
298/// BoolArray::from_iter(vec![false, false, true]).to_bit_buffer(),
299/// );
300/// ```
301pub fn gt(lhs: Expression, rhs: Expression) -> Expression {
302 Binary
303 .try_new_expr(Operator::Gt, [lhs, rhs])
304 .vortex_expect("Failed to create Gt binary expression")
305}
306
307/// Create a new [`Binary`] using the [`Lte`](Operator::Lte) operator.
308///
309/// ## Example usage
310///
311/// ```
312/// # use vortex_array::arrays::{BoolArray, PrimitiveArray };
313/// # use vortex_array::arrays::bool::BoolArrayExt;
314/// # use vortex_array::IntoArray;
315/// # use vortex_array::{VortexSessionExecute, array_session};
316/// # use vortex_array::validity::Validity;
317/// # use vortex_buffer::buffer;
318/// # use vortex_array::expr::{root, lit, lt_eq};
319/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
320/// let result = xs.into_array().apply(<_eq(root(), lit(2))).unwrap();
321/// let mut ctx = array_session().create_execution_ctx();
322///
323/// assert_eq!(
324/// result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
325/// BoolArray::from_iter(vec![true, true, false]).to_bit_buffer(),
326/// );
327/// ```
328pub fn lt_eq(lhs: Expression, rhs: Expression) -> Expression {
329 Binary
330 .try_new_expr(Operator::Lte, [lhs, rhs])
331 .vortex_expect("Failed to create Lte binary expression")
332}
333
334/// Create a new [`Binary`] using the [`Lt`](Operator::Lt) operator.
335///
336/// ## Example usage
337///
338/// ```
339/// # use vortex_array::arrays::{BoolArray, PrimitiveArray };
340/// # use vortex_array::arrays::bool::BoolArrayExt;
341/// # use vortex_array::IntoArray;
342/// # use vortex_array::{VortexSessionExecute, array_session};
343/// # use vortex_array::validity::Validity;
344/// # use vortex_buffer::buffer;
345/// # use vortex_array::expr::{root, lit, lt};
346/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
347/// let result = xs.into_array().apply(<(root(), lit(3))).unwrap();
348/// let mut ctx = array_session().create_execution_ctx();
349///
350/// assert_eq!(
351/// result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
352/// BoolArray::from_iter(vec![true, true, false]).to_bit_buffer(),
353/// );
354/// ```
355pub fn lt(lhs: Expression, rhs: Expression) -> Expression {
356 Binary
357 .try_new_expr(Operator::Lt, [lhs, rhs])
358 .vortex_expect("Failed to create Lt binary expression")
359}
360
361/// Create a new [`Binary`] using the [`Or`](Operator::Or) operator.
362///
363/// ## Example usage
364///
365/// ```
366/// # use vortex_array::arrays::BoolArray;
367/// # use vortex_array::arrays::bool::BoolArrayExt;
368/// # use vortex_array::IntoArray;
369/// # use vortex_array::{VortexSessionExecute, array_session};
370/// # use vortex_array::expr::{root, lit, or};
371/// let xs = BoolArray::from_iter(vec![true, false, true]);
372/// let result = xs.into_array().apply(&or(root(), lit(false))).unwrap();
373/// let mut ctx = array_session().create_execution_ctx();
374///
375/// assert_eq!(
376/// result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
377/// BoolArray::from_iter(vec![true, false, true]).to_bit_buffer(),
378/// );
379/// ```
380pub fn or(lhs: Expression, rhs: Expression) -> Expression {
381 Binary
382 .try_new_expr(Operator::Or, [lhs, rhs])
383 .vortex_expect("Failed to create Or binary expression")
384}
385
386/// Collects a list of `or`ed values into a single expression using a balanced tree.
387///
388/// This creates a balanced binary tree to avoid deep nesting that could cause
389/// stack overflow during drop or evaluation.
390///
391/// [a, b, c, d] => or(or(a, b), or(c, d))
392pub fn or_collect<I>(iter: I) -> Option<Expression>
393where
394 I: IntoIterator<Item = Expression>,
395{
396 iter.into_iter().reduce_balanced(or)
397}
398
399/// Create a new [`Binary`] using the [`And`](Operator::And) operator.
400///
401/// ## Example usage
402///
403/// ```
404/// # use vortex_array::arrays::BoolArray;
405/// # use vortex_array::arrays::bool::BoolArrayExt;
406/// # use vortex_array::IntoArray;
407/// # use vortex_array::{VortexSessionExecute, array_session};
408/// # use vortex_array::expr::{and, root, lit};
409/// let xs = BoolArray::from_iter(vec![true, false, true]).into_array();
410/// let result = xs.apply(&and(root(), lit(true))).unwrap();
411/// let mut ctx = array_session().create_execution_ctx();
412///
413/// assert_eq!(
414/// result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
415/// BoolArray::from_iter(vec![true, false, true]).to_bit_buffer(),
416/// );
417/// ```
418pub fn and(lhs: Expression, rhs: Expression) -> Expression {
419 Binary
420 .try_new_expr(Operator::And, [lhs, rhs])
421 .vortex_expect("Failed to create And binary expression")
422}
423
424/// Collects a list of `and`ed values into a single expression using a balanced tree.
425///
426/// This creates a balanced binary tree to avoid deep nesting that could cause
427/// stack overflow during drop or evaluation.
428///
429/// [a, b, c, d] => and(and(a, b), and(c, d))
430pub fn and_collect<I>(iter: I) -> Option<Expression>
431where
432 I: IntoIterator<Item = Expression>,
433{
434 iter.into_iter().reduce_balanced(and)
435}
436
437/// Create a new [`Binary`] using the [`Add`](Operator::Add) operator.
438///
439/// ## Example usage
440///
441/// ```
442/// # use vortex_array::IntoArray;
443/// # use vortex_array::arrays::PrimitiveArray;
444/// # use vortex_array::builtins::ArrayBuiltins;
445/// # use vortex_array::{VortexSessionExecute, array_session};
446/// # use vortex_buffer::buffer;
447/// # use vortex_array::expr::{checked_add, lit, root};
448/// let xs = buffer![1, 2, 3].into_array();
449/// let result = xs.apply(&checked_add(root(), lit(5))).unwrap();
450///
451/// let mut ctx = array_session().create_execution_ctx();
452/// let result = result.execute::<PrimitiveArray>(&mut ctx).unwrap();
453/// assert_eq!(result.as_slice::<i32>(), [6, 7, 8]);
454/// ```
455pub fn checked_add(lhs: Expression, rhs: Expression) -> Expression {
456 Binary
457 .try_new_expr(Operator::Add, [lhs, rhs])
458 .vortex_expect("Failed to create Add binary expression")
459}
460
461// ---- Not ----
462
463/// Creates an expression that logically inverts boolean values.
464///
465/// Returns the logical negation of the input boolean expression.
466///
467/// ```rust
468/// # use vortex_array::expr::{not, root};
469/// let expr = not(root());
470/// ```
471pub fn not(operand: Expression) -> Expression {
472 Not.new_expr(EmptyOptions, vec![operand])
473}
474
475// ---- Between ----
476
477/// Creates an expression that checks if values are between two bounds.
478///
479/// Returns a boolean array indicating which values fall within the specified range.
480/// The comparison strictness is controlled by the options parameter.
481///
482/// ```rust
483/// # use vortex_array::scalar_fn::fns::between::BetweenOptions;
484/// # use vortex_array::scalar_fn::fns::between::StrictComparison;
485/// # use vortex_array::expr::{between, lit, root};
486/// let opts = BetweenOptions {
487/// lower_strict: StrictComparison::NonStrict,
488/// upper_strict: StrictComparison::NonStrict,
489/// };
490/// let expr = between(root(), lit(10), lit(20), opts);
491/// ```
492pub fn between(
493 arr: Expression,
494 lower: Expression,
495 upper: Expression,
496 options: BetweenOptions,
497) -> Expression {
498 Between
499 .try_new_expr(options, [arr, lower, upper])
500 .vortex_expect("Failed to create Between expression")
501}
502
503// ---- Select ----
504
505/// Creates an expression that selects (includes) specific fields from an array.
506///
507/// Projects only the specified fields from the child expression, which must be of DType struct.
508/// ```rust
509/// # use vortex_array::expr::{select, root};
510/// let expr = select(["name", "age"], root());
511/// ```
512pub fn select(field_names: impl Into<FieldNames>, child: Expression) -> Expression {
513 Select
514 .try_new_expr(FieldSelection::Include(field_names.into()), [child])
515 .vortex_expect("Failed to create Select expression")
516}
517
518/// Creates an expression that excludes specific fields from an array.
519///
520/// Projects all fields except the specified ones from the input struct expression.
521///
522/// ```rust
523/// # use vortex_array::expr::{select_exclude, root};
524/// let expr = select_exclude(["internal_id", "metadata"], root());
525/// ```
526pub fn select_exclude(fields: impl Into<FieldNames>, child: Expression) -> Expression {
527 Select
528 .try_new_expr(FieldSelection::Exclude(fields.into()), [child])
529 .vortex_expect("Failed to create Select expression")
530}
531
532// ---- Pack ----
533
534/// Creates an expression that packs values into a struct with named fields.
535///
536/// ```rust
537/// # use vortex_array::dtype::Nullability;
538/// # use vortex_array::expr::{pack, col, lit};
539/// let expr = pack([("id", col("user_id")), ("constant", lit(42))], Nullability::NonNullable);
540/// ```
541pub fn pack(
542 elements: impl IntoIterator<Item = (impl Into<FieldName>, Expression)>,
543 nullability: Nullability,
544) -> Expression {
545 let (names, values): (Vec<_>, Vec<_>) = elements
546 .into_iter()
547 .map(|(name, value)| (name.into(), value))
548 .unzip();
549 Pack.new_expr(
550 PackOptions {
551 names: names.into(),
552 nullability,
553 },
554 values,
555 )
556}
557
558// ---- Cast ----
559
560/// Creates an expression that casts values to a target data type.
561///
562/// Converts the input expression's values to the specified target type.
563///
564/// ```rust
565/// # use vortex_array::dtype::{DType, Nullability, PType};
566/// # use vortex_array::expr::{cast, root};
567/// let expr = cast(root(), DType::Primitive(PType::I64, Nullability::NonNullable));
568/// ```
569pub fn cast(child: Expression, target: DType) -> Expression {
570 Cast.try_new_expr(target, [child])
571 .vortex_expect("Failed to create Cast expression")
572}
573
574// ---- FillNull ----
575
576/// Creates an expression that replaces null values with a fill value.
577///
578/// ```rust
579/// # use vortex_array::expr::{fill_null, root, lit};
580/// let expr = fill_null(root(), lit(0i32));
581/// ```
582pub fn fill_null(child: Expression, fill_value: Expression) -> Expression {
583 FillNull.new_expr(EmptyOptions, [child, fill_value])
584}
585
586// ---- IsNull ----
587
588/// Creates an expression that checks for null values.
589///
590/// Returns a boolean array indicating which positions contain null values.
591///
592/// ```rust
593/// # use vortex_array::expr::{is_null, root};
594/// let expr = is_null(root());
595/// ```
596pub fn is_null(child: Expression) -> Expression {
597 IsNull.new_expr(EmptyOptions, vec![child])
598}
599
600// ---- IsNotNull ----
601
602/// Creates an expression that checks for non-null values.
603///
604/// Returns a boolean array indicating which positions contain non-null values.
605///
606/// ```rust
607/// # use vortex_array::expr::{is_not_null, root};
608/// let expr = is_not_null(root());
609/// ```
610pub fn is_not_null(child: Expression) -> Expression {
611 IsNotNull.new_expr(EmptyOptions, vec![child])
612}
613
614// ---- Like ----
615
616/// Creates a SQL LIKE expression.
617pub fn like(child: Expression, pattern: Expression) -> Expression {
618 Like.new_expr(
619 LikeOptions {
620 negated: false,
621 case_insensitive: false,
622 },
623 [child, pattern],
624 )
625}
626
627/// Creates a case-insensitive SQL ILIKE expression.
628pub fn ilike(child: Expression, pattern: Expression) -> Expression {
629 Like.new_expr(
630 LikeOptions {
631 negated: false,
632 case_insensitive: true,
633 },
634 [child, pattern],
635 )
636}
637
638/// Creates a negated SQL NOT LIKE expression.
639pub fn not_like(child: Expression, pattern: Expression) -> Expression {
640 Like.new_expr(
641 LikeOptions {
642 negated: true,
643 case_insensitive: false,
644 },
645 [child, pattern],
646 )
647}
648
649/// Creates a negated case-insensitive SQL NOT ILIKE expression.
650pub fn not_ilike(child: Expression, pattern: Expression) -> Expression {
651 Like.new_expr(
652 LikeOptions {
653 negated: true,
654 case_insensitive: true,
655 },
656 [child, pattern],
657 )
658}
659
660// ---- Mask ----
661
662/// Creates a mask expression that applies the given boolean mask to the input array.
663pub fn mask(array: Expression, mask: Expression) -> Expression {
664 Mask.new_expr(EmptyOptions, [array, mask])
665}
666
667// ---- Merge ----
668
669/// Creates an expression that merges struct expressions into a single struct.
670///
671/// Combines fields from all input expressions. If field names are duplicated,
672/// later expressions win. Fields are not recursively merged.
673///
674/// ```rust
675/// # use vortex_array::dtype::Nullability;
676/// # use vortex_array::expr::{merge, get_item, root};
677/// let expr = merge([get_item("a", root()), get_item("b", root())]);
678/// ```
679pub fn merge(elements: impl IntoIterator<Item = impl Into<Expression>>) -> Expression {
680 use itertools::Itertools as _;
681 let values = elements.into_iter().map(|value| value.into()).collect_vec();
682 Merge.new_expr(DuplicateHandling::default(), values)
683}
684
685/// Creates a merge expression with explicit duplicate handling.
686pub fn merge_opts(
687 elements: impl IntoIterator<Item = impl Into<Expression>>,
688 duplicate_handling: DuplicateHandling,
689) -> Expression {
690 use itertools::Itertools as _;
691 let values = elements.into_iter().map(|value| value.into()).collect_vec();
692 Merge.new_expr(duplicate_handling, values)
693}
694
695// ---- Zip ----
696
697/// Creates a zip expression that conditionally selects between two arrays.
698///
699/// ```rust
700/// # use vortex_array::expr::{zip_expr, root, lit};
701/// let expr = zip_expr(lit(true), root(), lit(0i32));
702/// ```
703pub fn zip_expr(mask: Expression, if_true: Expression, if_false: Expression) -> Expression {
704 Zip.new_expr(EmptyOptions, [if_true, if_false, mask])
705}
706
707// ---- Dynamic ----
708
709/// Creates a dynamic comparison expression.
710pub fn dynamic(
711 operator: CompareOperator,
712 rhs_value: impl Fn() -> Option<ScalarValue> + Send + Sync + 'static,
713 rhs_dtype: DType,
714 default: bool,
715 lhs: Expression,
716) -> Expression {
717 DynamicComparison.new_expr(
718 DynamicComparisonExpr {
719 operator,
720 rhs: Arc::new(Rhs {
721 value: Arc::new(rhs_value),
722 dtype: rhs_dtype,
723 }),
724 default,
725 },
726 [lhs],
727 )
728}
729
730// ---- ListContains ----
731
732/// Creates an expression that checks if a value is contained in a list.
733///
734/// Returns a boolean array indicating whether the value appears in each list.
735///
736/// ```rust
737/// # use vortex_array::expr::{list_contains, lit, root};
738/// let expr = list_contains(root(), lit(42));
739/// ```
740pub fn list_contains(list: Expression, value: Expression) -> Expression {
741 ListContains.new_expr(EmptyOptions, [list, value])
742}
743
744// ---- ByteLength ----
745
746/// Creates an expression that computes the byte length of each element.
747/// This is akin to ANSI SQL OCTET_LENGTH(), or DuckDB's strlen().
748///
749/// ```rust
750/// # use vortex_array::expr::{byte_length, root};
751/// let expr = byte_length(root());
752/// ```
753pub fn byte_length(input: Expression) -> Expression {
754 ByteLength.new_expr(EmptyOptions, [input])
755}
756
757// ---- ExtStorage ----
758
759/// Creates an expression that extracts the storage values from an extension array.
760///
761/// ```rust
762/// # use vortex_array::expr::{ext_storage, root};
763/// let expr = ext_storage(root());
764/// ```
765pub fn ext_storage(input: Expression) -> Expression {
766 ExtStorage.new_expr(EmptyOptions, [input])
767}
768
769// ---- ListLength ----
770
771/// Creates an expression that computes the number of elements in each list
772/// for `List` and `FixedSizeList` inputs. This is akin to ANSI SQL `CARDINALITY()`,
773/// or DuckDB's `len()`/`array_length()`.
774///
775/// ```rust
776/// # use vortex_array::expr::{list_length, root};
777/// let expr = list_length(root());
778/// ```
779pub fn list_length(input: Expression) -> Expression {
780 ListLength.new_expr(EmptyOptions, [input])
781}
782
783// ---- ListSum ----
784
785/// Creates an expression that sums the elements of each list for `List` and
786/// `FixedSizeList` inputs, akin to DuckDB's `list_sum()`.
787///
788/// Follows SQL `SUM` semantics per list: null lists, empty lists, and lists whose elements are
789/// all null yield null; null elements are skipped; integer and decimal overflow yields a null
790/// value. The result dtype follows `sum`'s widening rules and is always nullable. NaN float
791/// elements are skipped by default; see [`list_sum_opts`] for the NaN-including variant.
792///
793/// ```rust
794/// # use vortex_array::expr::{list_sum, root};
795/// let expr = list_sum(root());
796/// ```
797pub fn list_sum(input: Expression) -> Expression {
798 ListSum.new_expr(NumericalAggregateOpts::default(), [input])
799}
800
801/// Creates a [`list_sum`] expression with explicit [`NumericalAggregateOpts`], controlling
802/// whether NaN float elements are skipped (the default) or poison the list's sum to NaN.
803pub fn list_sum_opts(input: Expression, options: NumericalAggregateOpts) -> Expression {
804 ListSum.new_expr(options, [input])
805}