Skip to main content

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::{DynArray, IntoArray, ToCanonical};
178/// # use vortex_array::validity::Validity;
179/// # use vortex_buffer::buffer;
180/// # use vortex_array::expr::{eq, root, lit};
181/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
182/// let result = xs.to_array().apply(&eq(root(), lit(3))).unwrap();
183///
184/// assert_eq!(
185///     result.to_bool().to_bit_buffer(),
186///     BoolArray::from_iter(vec![false, false, true]).to_bit_buffer(),
187/// );
188/// ```
189pub fn eq(lhs: Expression, rhs: Expression) -> Expression {
190    Binary
191        .try_new_expr(Operator::Eq, [lhs, rhs])
192        .vortex_expect("Failed to create Eq binary expression")
193}
194
195/// Create a new [`Binary`] using the [`NotEq`](Operator::NotEq) operator.
196///
197/// ## Example usage
198///
199/// ```
200/// # use vortex_array::arrays::{BoolArray, PrimitiveArray};
201/// # use vortex_array::{DynArray, IntoArray, ToCanonical};
202/// # use vortex_array::validity::Validity;
203/// # use vortex_buffer::buffer;
204/// # use vortex_array::expr::{root, lit, not_eq};
205/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
206/// let result = xs.to_array().apply(&not_eq(root(), lit(3))).unwrap();
207///
208/// assert_eq!(
209///     result.to_bool().to_bit_buffer(),
210///     BoolArray::from_iter(vec![true, true, false]).to_bit_buffer(),
211/// );
212/// ```
213pub fn not_eq(lhs: Expression, rhs: Expression) -> Expression {
214    Binary
215        .try_new_expr(Operator::NotEq, [lhs, rhs])
216        .vortex_expect("Failed to create NotEq binary expression")
217}
218
219/// Create a new [`Binary`] using the [`Gte`](Operator::Gte) operator.
220///
221/// ## Example usage
222///
223/// ```
224/// # use vortex_array::arrays::{BoolArray, PrimitiveArray };
225/// # use vortex_array::{DynArray, IntoArray, ToCanonical};
226/// # use vortex_array::validity::Validity;
227/// # use vortex_buffer::buffer;
228/// # use vortex_array::expr::{gt_eq, root, lit};
229/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
230/// let result = xs.to_array().apply(&gt_eq(root(), lit(3))).unwrap();
231///
232/// assert_eq!(
233///     result.to_bool().to_bit_buffer(),
234///     BoolArray::from_iter(vec![false, false, true]).to_bit_buffer(),
235/// );
236/// ```
237pub fn gt_eq(lhs: Expression, rhs: Expression) -> Expression {
238    Binary
239        .try_new_expr(Operator::Gte, [lhs, rhs])
240        .vortex_expect("Failed to create Gte binary expression")
241}
242
243/// Create a new [`Binary`] using the [`Gt`](Operator::Gt) operator.
244///
245/// ## Example usage
246///
247/// ```
248/// # use vortex_array::arrays::{BoolArray, PrimitiveArray };
249/// # use vortex_array::{DynArray, IntoArray, ToCanonical};
250/// # use vortex_array::validity::Validity;
251/// # use vortex_buffer::buffer;
252/// # use vortex_array::expr::{gt, root, lit};
253/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
254/// let result = xs.to_array().apply(&gt(root(), lit(2))).unwrap();
255///
256/// assert_eq!(
257///     result.to_bool().to_bit_buffer(),
258///     BoolArray::from_iter(vec![false, false, true]).to_bit_buffer(),
259/// );
260/// ```
261pub fn gt(lhs: Expression, rhs: Expression) -> Expression {
262    Binary
263        .try_new_expr(Operator::Gt, [lhs, rhs])
264        .vortex_expect("Failed to create Gt binary expression")
265}
266
267/// Create a new [`Binary`] using the [`Lte`](Operator::Lte) operator.
268///
269/// ## Example usage
270///
271/// ```
272/// # use vortex_array::arrays::{BoolArray, PrimitiveArray };
273/// # use vortex_array::{DynArray, IntoArray, ToCanonical};
274/// # use vortex_array::validity::Validity;
275/// # use vortex_buffer::buffer;
276/// # use vortex_array::expr::{root, lit, lt_eq};
277/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
278/// let result = xs.to_array().apply(&lt_eq(root(), lit(2))).unwrap();
279///
280/// assert_eq!(
281///     result.to_bool().to_bit_buffer(),
282///     BoolArray::from_iter(vec![true, true, false]).to_bit_buffer(),
283/// );
284/// ```
285pub fn lt_eq(lhs: Expression, rhs: Expression) -> Expression {
286    Binary
287        .try_new_expr(Operator::Lte, [lhs, rhs])
288        .vortex_expect("Failed to create Lte binary expression")
289}
290
291/// Create a new [`Binary`] using the [`Lt`](Operator::Lt) operator.
292///
293/// ## Example usage
294///
295/// ```
296/// # use vortex_array::arrays::{BoolArray, PrimitiveArray };
297/// # use vortex_array::{DynArray, IntoArray, ToCanonical};
298/// # use vortex_array::validity::Validity;
299/// # use vortex_buffer::buffer;
300/// # use vortex_array::expr::{root, lit, lt};
301/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
302/// let result = xs.to_array().apply(&lt(root(), lit(3))).unwrap();
303///
304/// assert_eq!(
305///     result.to_bool().to_bit_buffer(),
306///     BoolArray::from_iter(vec![true, true, false]).to_bit_buffer(),
307/// );
308/// ```
309pub fn lt(lhs: Expression, rhs: Expression) -> Expression {
310    Binary
311        .try_new_expr(Operator::Lt, [lhs, rhs])
312        .vortex_expect("Failed to create Lt binary expression")
313}
314
315/// Create a new [`Binary`] using the [`Or`](Operator::Or) operator.
316///
317/// ## Example usage
318///
319/// ```
320/// # use vortex_array::arrays::BoolArray;
321/// # use vortex_array::{DynArray, IntoArray, ToCanonical};
322/// # use vortex_array::expr::{root, lit, or};
323/// let xs = BoolArray::from_iter(vec![true, false, true]);
324/// let result = xs.to_array().apply(&or(root(), lit(false))).unwrap();
325///
326/// assert_eq!(
327///     result.to_bool().to_bit_buffer(),
328///     BoolArray::from_iter(vec![true, false, true]).to_bit_buffer(),
329/// );
330/// ```
331pub fn or(lhs: Expression, rhs: Expression) -> Expression {
332    Binary
333        .try_new_expr(Operator::Or, [lhs, rhs])
334        .vortex_expect("Failed to create Or binary expression")
335}
336
337/// Collects a list of `or`ed values into a single expression using a balanced tree.
338///
339/// This creates a balanced binary tree to avoid deep nesting that could cause
340/// stack overflow during drop or evaluation.
341///
342/// [a, b, c, d] => or(or(a, b), or(c, d))
343pub fn or_collect<I>(iter: I) -> Option<Expression>
344where
345    I: IntoIterator<Item = Expression>,
346{
347    iter.into_iter().reduce_balanced(or)
348}
349
350/// Create a new [`Binary`] using the [`And`](Operator::And) operator.
351///
352/// ## Example usage
353///
354/// ```
355/// # use vortex_array::arrays::BoolArray;
356/// # use vortex_array::{DynArray, IntoArray, ToCanonical};
357/// # use vortex_array::expr::{and, root, lit};
358/// let xs = BoolArray::from_iter(vec![true, false, true]);
359/// let result = xs.to_array().apply(&and(root(), lit(true))).unwrap();
360///
361/// assert_eq!(
362///     result.to_bool().to_bit_buffer(),
363///     BoolArray::from_iter(vec![true, false, true]).to_bit_buffer(),
364/// );
365/// ```
366pub fn and(lhs: Expression, rhs: Expression) -> Expression {
367    Binary
368        .try_new_expr(Operator::And, [lhs, rhs])
369        .vortex_expect("Failed to create And binary expression")
370}
371
372/// Collects a list of `and`ed values into a single expression using a balanced tree.
373///
374/// This creates a balanced binary tree to avoid deep nesting that could cause
375/// stack overflow during drop or evaluation.
376///
377/// [a, b, c, d] => and(and(a, b), and(c, d))
378pub fn and_collect<I>(iter: I) -> Option<Expression>
379where
380    I: IntoIterator<Item = Expression>,
381{
382    iter.into_iter().reduce_balanced(and)
383}
384
385/// Create a new [`Binary`] using the [`Add`](Operator::Add) operator.
386///
387/// ## Example usage
388///
389/// ```
390/// # use vortex_array::{DynArray, IntoArray};
391/// # use vortex_array::arrow::IntoArrowArray as _;
392/// # use vortex_buffer::buffer;
393/// # use vortex_array::expr::{checked_add, lit, root};
394/// let xs = buffer![1, 2, 3].into_array();
395/// let result = xs.apply(&checked_add(root(), lit(5))).unwrap();
396///
397/// assert_eq!(
398///     &result.into_arrow_preferred().unwrap(),
399///     &buffer![6, 7, 8]
400///         .into_array()
401///         .into_arrow_preferred()
402///         .unwrap()
403/// );
404/// ```
405pub fn checked_add(lhs: Expression, rhs: Expression) -> Expression {
406    Binary
407        .try_new_expr(Operator::Add, [lhs, rhs])
408        .vortex_expect("Failed to create Add binary expression")
409}
410
411// ---- Not ----
412
413/// Creates an expression that logically inverts boolean values.
414///
415/// Returns the logical negation of the input boolean expression.
416///
417/// ```rust
418/// # use vortex_array::expr::{not, root};
419/// let expr = not(root());
420/// ```
421pub fn not(operand: Expression) -> Expression {
422    Not.new_expr(EmptyOptions, vec![operand])
423}
424
425// ---- Between ----
426
427/// Creates an expression that checks if values are between two bounds.
428///
429/// Returns a boolean array indicating which values fall within the specified range.
430/// The comparison strictness is controlled by the options parameter.
431///
432/// ```rust
433/// # use vortex_array::scalar_fn::fns::between::BetweenOptions;
434/// # use vortex_array::scalar_fn::fns::between::StrictComparison;
435/// # use vortex_array::expr::{between, lit, root};
436/// let opts = BetweenOptions {
437///     lower_strict: StrictComparison::NonStrict,
438///     upper_strict: StrictComparison::NonStrict,
439/// };
440/// let expr = between(root(), lit(10), lit(20), opts);
441/// ```
442pub fn between(
443    arr: Expression,
444    lower: Expression,
445    upper: Expression,
446    options: BetweenOptions,
447) -> Expression {
448    Between
449        .try_new_expr(options, [arr, lower, upper])
450        .vortex_expect("Failed to create Between expression")
451}
452
453// ---- Select ----
454
455/// Creates an expression that selects (includes) specific fields from an array.
456///
457/// Projects only the specified fields from the child expression, which must be of DType struct.
458/// ```rust
459/// # use vortex_array::expr::{select, root};
460/// let expr = select(["name", "age"], root());
461/// ```
462pub fn select(field_names: impl Into<FieldNames>, child: Expression) -> Expression {
463    Select
464        .try_new_expr(FieldSelection::Include(field_names.into()), [child])
465        .vortex_expect("Failed to create Select expression")
466}
467
468/// Creates an expression that excludes specific fields from an array.
469///
470/// Projects all fields except the specified ones from the input struct expression.
471///
472/// ```rust
473/// # use vortex_array::expr::{select_exclude, root};
474/// let expr = select_exclude(["internal_id", "metadata"], root());
475/// ```
476pub fn select_exclude(fields: impl Into<FieldNames>, child: Expression) -> Expression {
477    Select
478        .try_new_expr(FieldSelection::Exclude(fields.into()), [child])
479        .vortex_expect("Failed to create Select expression")
480}
481
482// ---- Pack ----
483
484/// Creates an expression that packs values into a struct with named fields.
485///
486/// ```rust
487/// # use vortex_array::dtype::Nullability;
488/// # use vortex_array::expr::{pack, col, lit};
489/// let expr = pack([("id", col("user_id")), ("constant", lit(42))], Nullability::NonNullable);
490/// ```
491pub fn pack(
492    elements: impl IntoIterator<Item = (impl Into<FieldName>, Expression)>,
493    nullability: Nullability,
494) -> Expression {
495    let (names, values): (Vec<_>, Vec<_>) = elements
496        .into_iter()
497        .map(|(name, value)| (name.into(), value))
498        .unzip();
499    Pack.new_expr(
500        PackOptions {
501            names: names.into(),
502            nullability,
503        },
504        values,
505    )
506}
507
508// ---- Cast ----
509
510/// Creates an expression that casts values to a target data type.
511///
512/// Converts the input expression's values to the specified target type.
513///
514/// ```rust
515/// # use vortex_array::dtype::{DType, Nullability, PType};
516/// # use vortex_array::expr::{cast, root};
517/// let expr = cast(root(), DType::Primitive(PType::I64, Nullability::NonNullable));
518/// ```
519pub fn cast(child: Expression, target: DType) -> Expression {
520    Cast.try_new_expr(target, [child])
521        .vortex_expect("Failed to create Cast expression")
522}
523
524// ---- FillNull ----
525
526/// Creates an expression that replaces null values with a fill value.
527///
528/// ```rust
529/// # use vortex_array::expr::{fill_null, root, lit};
530/// let expr = fill_null(root(), lit(0i32));
531/// ```
532pub fn fill_null(child: Expression, fill_value: Expression) -> Expression {
533    FillNull.new_expr(EmptyOptions, [child, fill_value])
534}
535
536// ---- IsNull ----
537
538/// Creates an expression that checks for null values.
539///
540/// Returns a boolean array indicating which positions contain null values.
541///
542/// ```rust
543/// # use vortex_array::expr::{is_null, root};
544/// let expr = is_null(root());
545/// ```
546pub fn is_null(child: Expression) -> Expression {
547    IsNull.new_expr(EmptyOptions, vec![child])
548}
549
550// ---- Like ----
551
552/// Creates a SQL LIKE expression.
553pub fn like(child: Expression, pattern: Expression) -> Expression {
554    Like.new_expr(
555        LikeOptions {
556            negated: false,
557            case_insensitive: false,
558        },
559        [child, pattern],
560    )
561}
562
563/// Creates a case-insensitive SQL ILIKE expression.
564pub fn ilike(child: Expression, pattern: Expression) -> Expression {
565    Like.new_expr(
566        LikeOptions {
567            negated: false,
568            case_insensitive: true,
569        },
570        [child, pattern],
571    )
572}
573
574/// Creates a negated SQL NOT LIKE expression.
575pub fn not_like(child: Expression, pattern: Expression) -> Expression {
576    Like.new_expr(
577        LikeOptions {
578            negated: true,
579            case_insensitive: false,
580        },
581        [child, pattern],
582    )
583}
584
585/// Creates a negated case-insensitive SQL NOT ILIKE expression.
586pub fn not_ilike(child: Expression, pattern: Expression) -> Expression {
587    Like.new_expr(
588        LikeOptions {
589            negated: true,
590            case_insensitive: true,
591        },
592        [child, pattern],
593    )
594}
595
596// ---- Mask ----
597
598/// Creates a mask expression that applies the given boolean mask to the input array.
599pub fn mask(array: Expression, mask: Expression) -> Expression {
600    Mask.new_expr(EmptyOptions, [array, mask])
601}
602
603// ---- Merge ----
604
605/// Creates an expression that merges struct expressions into a single struct.
606///
607/// Combines fields from all input expressions. If field names are duplicated,
608/// later expressions win. Fields are not recursively merged.
609///
610/// ```rust
611/// # use vortex_array::dtype::Nullability;
612/// # use vortex_array::expr::{merge, get_item, root};
613/// let expr = merge([get_item("a", root()), get_item("b", root())]);
614/// ```
615pub fn merge(elements: impl IntoIterator<Item = impl Into<Expression>>) -> Expression {
616    use itertools::Itertools as _;
617    let values = elements.into_iter().map(|value| value.into()).collect_vec();
618    Merge.new_expr(DuplicateHandling::default(), values)
619}
620
621/// Creates a merge expression with explicit duplicate handling.
622pub fn merge_opts(
623    elements: impl IntoIterator<Item = impl Into<Expression>>,
624    duplicate_handling: DuplicateHandling,
625) -> Expression {
626    use itertools::Itertools as _;
627    let values = elements.into_iter().map(|value| value.into()).collect_vec();
628    Merge.new_expr(duplicate_handling, values)
629}
630
631// ---- Zip ----
632
633/// Creates a zip expression that conditionally selects between two arrays.
634///
635/// ```rust
636/// # use vortex_array::expr::{zip_expr, root, lit};
637/// let expr = zip_expr(lit(true), root(), lit(0i32));
638/// ```
639pub fn zip_expr(mask: Expression, if_true: Expression, if_false: Expression) -> Expression {
640    Zip.new_expr(EmptyOptions, [if_true, if_false, mask])
641}
642
643// ---- Dynamic ----
644
645/// Creates a dynamic comparison expression.
646pub fn dynamic(
647    operator: CompareOperator,
648    rhs_value: impl Fn() -> Option<ScalarValue> + Send + Sync + 'static,
649    rhs_dtype: DType,
650    default: bool,
651    lhs: Expression,
652) -> Expression {
653    DynamicComparison.new_expr(
654        DynamicComparisonExpr {
655            operator,
656            rhs: Arc::new(Rhs {
657                value: Arc::new(rhs_value),
658                dtype: rhs_dtype,
659            }),
660            default,
661        },
662        [lhs],
663    )
664}
665
666// ---- ListContains ----
667
668/// Creates an expression that checks if a value is contained in a list.
669///
670/// Returns a boolean array indicating whether the value appears in each list.
671///
672/// ```rust
673/// # use vortex_array::expr::{list_contains, lit, root};
674/// let expr = list_contains(root(), lit(42));
675/// ```
676pub fn list_contains(list: Expression, value: Expression) -> Expression {
677    ListContains.new_expr(EmptyOptions, [list, value])
678}