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