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