Skip to content

Commit c98dc87

Browse files
committed
variant get
Signed-off-by: Adam Gutglick <adam@spiraldb.com>
1 parent db00b43 commit c98dc87

12 files changed

Lines changed: 536 additions & 0 deletions

File tree

encodings/parquet-variant/src/kernel.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ use vortex_mask::Mask;
1919

2020
use crate::ParquetVariant;
2121
use crate::array::ParquetVariantArray;
22+
use crate::variant_get::VariantGetExecuteParent;
2223

2324
pub(crate) static PARENT_KERNELS: ParentKernelSet<ParquetVariant> = ParentKernelSet::new(&[
2425
ParentKernelSet::lift(&FilterExecuteAdaptor(ParquetVariant)),
2526
ParentKernelSet::lift(&SliceExecuteAdaptor(ParquetVariant)),
2627
ParentKernelSet::lift(&TakeExecuteAdaptor(ParquetVariant)),
28+
ParentKernelSet::lift(&VariantGetExecuteParent),
2729
]);
2830

2931
impl SliceKernel for ParquetVariant {

encodings/parquet-variant/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ mod array;
2828
mod kernel;
2929
mod operations;
3030
mod validity;
31+
mod variant_get;
32+
#[cfg(test)]
33+
mod variant_get_tests;
3134
mod vtable;
3235

3336
pub use array::ParquetVariantArray;
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
//! Execute-parent kernel for `variant_get` on `ParquetVariantArray`.
5+
//!
6+
//! Delegates to `parquet_variant_compute::variant_get` after converting to Arrow.
7+
8+
use std::sync::Arc;
9+
10+
use parquet_variant::VariantPathElement;
11+
use parquet_variant_compute::GetOptions;
12+
use parquet_variant_compute::VariantArray as ArrowVariantArray;
13+
use vortex_array::ArrayRef;
14+
use vortex_array::ExecutionCtx;
15+
use vortex_array::arrays::scalar_fn::ExactScalarFn;
16+
use vortex_array::arrays::scalar_fn::ScalarFnArrayView;
17+
use vortex_array::dtype::FieldName;
18+
use vortex_array::kernel::ExecuteParentKernel;
19+
use vortex_array::scalar_fn::fns::variant_get::VariantGet;
20+
use vortex_error::VortexResult;
21+
use vortex_error::vortex_err;
22+
23+
use crate::ParquetVariant;
24+
use crate::array::ParquetVariantArray;
25+
26+
#[derive(Debug)]
27+
pub(crate) struct VariantGetExecuteParent;
28+
29+
impl ExecuteParentKernel<ParquetVariant> for VariantGetExecuteParent {
30+
type Parent = ExactScalarFn<VariantGet>;
31+
32+
fn execute_parent(
33+
&self,
34+
array: &ParquetVariantArray,
35+
parent: ScalarFnArrayView<'_, VariantGet>,
36+
_child_idx: usize,
37+
ctx: &mut ExecutionCtx,
38+
) -> VortexResult<Option<ArrayRef>> {
39+
let field_name: &FieldName = parent.options;
40+
variant_get_impl(array, field_name, ctx).map(Some)
41+
}
42+
}
43+
44+
fn variant_get_impl(
45+
array: &ParquetVariantArray,
46+
field_name: &FieldName,
47+
ctx: &mut ExecutionCtx,
48+
) -> VortexResult<ArrayRef> {
49+
// Convert to Arrow VariantArray
50+
let arrow_variant = array.to_arrow(ctx)?;
51+
52+
// Build path for a single field access
53+
let path_element = VariantPathElement::Field {
54+
name: field_name.as_ref().into(),
55+
};
56+
let options = GetOptions::new_with_path(vec![path_element].into());
57+
58+
// Delegate to the parquet-variant-compute kernel.
59+
// With as_type = None, the result is itself a VariantArray.
60+
let inner: Arc<dyn arrow_array::Array> = Arc::new(arrow_variant.into_inner());
61+
let arrow_result = parquet_variant_compute::variant_get(&inner, options)
62+
.map_err(|e| vortex_err!("variant_get failed: {e}"))?;
63+
64+
// Convert back to Vortex
65+
let result_variant = ArrowVariantArray::try_new(
66+
arrow_result
67+
.as_any()
68+
.downcast_ref::<arrow_array::StructArray>()
69+
.ok_or_else(|| vortex_err!("variant_get did not return a StructArray"))?,
70+
)
71+
.map_err(|e| vortex_err!("failed to create VariantArray from result: {e}"))?;
72+
73+
ParquetVariantArray::from_arrow_variant(&result_variant)
74+
}
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
#[cfg(test)]
5+
mod tests {
6+
use arrow_array::StructArray;
7+
use arrow_buffer::NullBuffer;
8+
use parquet_variant::Variant as PqVariant;
9+
use parquet_variant::VariantBuilderExt;
10+
use parquet_variant_compute::VariantArrayBuilder;
11+
use vortex_array::ArrayRef;
12+
use vortex_array::DynArray;
13+
use vortex_array::LEGACY_SESSION;
14+
use vortex_array::VortexSessionExecute;
15+
use vortex_array::expr::root;
16+
use vortex_array::expr::variant_get;
17+
use vortex_error::VortexResult;
18+
19+
use crate::ParquetVariantArray;
20+
21+
/// Apply variant_get and execute through the full pipeline (including execute_parent).
22+
fn apply_variant_get(arr: &ArrayRef, field: &str) -> VortexResult<ArrayRef> {
23+
let expr = variant_get(field, root());
24+
let lazy = arr.apply(&expr)?;
25+
let mut ctx = LEGACY_SESSION.create_execution_ctx();
26+
lazy.execute::<ArrayRef>(&mut ctx)
27+
}
28+
29+
/// Build a VariantArray of objects: [{"a": 1, "b": "x"}, {"a": 2, "c": true}, {"b": "y"}]
30+
fn make_object_array() -> VortexResult<ArrayRef> {
31+
let mut builder = VariantArrayBuilder::new(3);
32+
33+
builder
34+
.new_object()
35+
.with_field("a", 1i32)
36+
.with_field("b", "x")
37+
.finish();
38+
39+
builder
40+
.new_object()
41+
.with_field("a", 2i32)
42+
.with_field("c", true)
43+
.finish();
44+
45+
builder.new_object().with_field("b", "y").finish();
46+
47+
ParquetVariantArray::from_arrow_variant(&builder.build())
48+
}
49+
50+
/// Build a nullable VariantArray: [{"a": 10}, NULL, {"a": 30}]
51+
fn make_nullable_object_array() -> VortexResult<ArrayRef> {
52+
let mut builder = VariantArrayBuilder::new(3);
53+
54+
builder.new_object().with_field("a", 10i32).finish();
55+
56+
builder.new_object().with_field("a", 20i32).finish();
57+
58+
builder.new_object().with_field("a", 30i32).finish();
59+
60+
let inner = builder.build().into_inner();
61+
let null_struct = StructArray::try_new(
62+
inner.fields().clone(),
63+
inner.columns().to_vec(),
64+
Some(NullBuffer::from(vec![true, false, true])),
65+
)
66+
.unwrap();
67+
let arrow_variant = parquet_variant_compute::VariantArray::try_new(&null_struct).unwrap();
68+
ParquetVariantArray::from_arrow_variant(&arrow_variant)
69+
}
70+
71+
#[test]
72+
fn test_variant_get_basic() -> VortexResult<()> {
73+
let arr = make_object_array()?;
74+
let result = apply_variant_get(&arr, "a")?;
75+
76+
assert_eq!(result.len(), 3);
77+
78+
// Row 0: {"a": 1, ...} → variant(1)
79+
let s0 = result.scalar_at(0)?;
80+
assert!(!s0.is_null());
81+
let inner0 = s0.as_variant().value().unwrap();
82+
assert_eq!(*inner0, 1i32.into());
83+
84+
// Row 1: {"a": 2, ...} → variant(2)
85+
let s1 = result.scalar_at(1)?;
86+
assert!(!s1.is_null());
87+
let inner1 = s1.as_variant().value().unwrap();
88+
assert_eq!(*inner1, 2i32.into());
89+
90+
// Row 2: {"b": "y"} → null (field "a" missing)
91+
let s2 = result.scalar_at(2)?;
92+
assert!(s2.is_null());
93+
94+
Ok(())
95+
}
96+
97+
#[test]
98+
fn test_variant_get_missing_field() -> VortexResult<()> {
99+
let arr = make_object_array()?;
100+
let result = apply_variant_get(&arr, "nonexistent")?;
101+
102+
assert_eq!(result.len(), 3);
103+
for i in 0..3 {
104+
assert!(result.scalar_at(i)?.is_null(), "row {i} should be null");
105+
}
106+
107+
Ok(())
108+
}
109+
110+
#[test]
111+
fn test_variant_get_null_input() -> VortexResult<()> {
112+
let arr = make_nullable_object_array()?;
113+
let result = apply_variant_get(&arr, "a")?;
114+
115+
assert_eq!(result.len(), 3);
116+
117+
// Row 0: {"a": 10} → variant(10)
118+
assert!(!result.scalar_at(0)?.is_null());
119+
120+
// Row 1: NULL → null
121+
assert!(result.scalar_at(1)?.is_null());
122+
123+
// Row 2: {"a": 30} → variant(30)
124+
assert!(!result.scalar_at(2)?.is_null());
125+
126+
Ok(())
127+
}
128+
129+
#[test]
130+
fn test_variant_get_non_object() -> VortexResult<()> {
131+
// Array of primitive variants (not objects)
132+
let mut builder = VariantArrayBuilder::new(2);
133+
builder.append_variant(PqVariant::from(42i32));
134+
builder.append_variant(PqVariant::from("hello"));
135+
let arr = ParquetVariantArray::from_arrow_variant(&builder.build())?;
136+
137+
let result = apply_variant_get(&arr, "a")?;
138+
139+
assert_eq!(result.len(), 2);
140+
assert!(result.scalar_at(0)?.is_null());
141+
assert!(result.scalar_at(1)?.is_null());
142+
143+
Ok(())
144+
}
145+
146+
#[test]
147+
fn test_variant_get_different_field() -> VortexResult<()> {
148+
let arr = make_object_array()?;
149+
let result = apply_variant_get(&arr, "b")?;
150+
151+
assert_eq!(result.len(), 3);
152+
153+
// Row 0: {"a": 1, "b": "x"} → variant("x")
154+
assert!(!result.scalar_at(0)?.is_null());
155+
156+
// Row 1: {"a": 2, "c": true} → null (no "b")
157+
assert!(result.scalar_at(1)?.is_null());
158+
159+
// Row 2: {"b": "y"} → variant("y")
160+
assert!(!result.scalar_at(2)?.is_null());
161+
162+
Ok(())
163+
}
164+
}

vortex-array/public-api.lock

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14480,6 +14480,8 @@ pub fn vortex_array::expr::select_exclude(fields: impl core::convert::Into<vorte
1448014480

1448114481
pub fn vortex_array::expr::split_conjunction(expr: &vortex_array::expr::Expression) -> alloc::vec::Vec<vortex_array::expr::Expression>
1448214482

14483+
pub fn vortex_array::expr::variant_get(field: impl core::convert::Into<vortex_array::dtype::FieldName>, child: vortex_array::expr::Expression) -> vortex_array::expr::Expression
14484+
1448314485
pub fn vortex_array::expr::zip_expr(mask: vortex_array::expr::Expression, if_true: vortex_array::expr::Expression, if_false: vortex_array::expr::Expression) -> vortex_array::expr::Expression
1448414486

1448514487
pub type vortex_array::expr::Annotations<'a, A> = vortex_utils::aliases::hash_map::HashMap<&'a vortex_array::expr::Expression, vortex_utils::aliases::hash_set::HashSet<A>>
@@ -19098,6 +19100,52 @@ pub fn vortex_array::scalar_fn::fns::select::Select::stat_falsification(&self, o
1909819100

1909919101
pub fn vortex_array::scalar_fn::fns::select::Select::validity(&self, options: &Self::Options, expression: &vortex_array::expr::Expression) -> vortex_error::VortexResult<core::option::Option<vortex_array::expr::Expression>>
1910019102

19103+
pub mod vortex_array::scalar_fn::fns::variant_get
19104+
19105+
pub struct vortex_array::scalar_fn::fns::variant_get::VariantGet
19106+
19107+
impl core::clone::Clone for vortex_array::scalar_fn::fns::variant_get::VariantGet
19108+
19109+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::clone(&self) -> vortex_array::scalar_fn::fns::variant_get::VariantGet
19110+
19111+
impl vortex_array::scalar_fn::ScalarFnVTable for vortex_array::scalar_fn::fns::variant_get::VariantGet
19112+
19113+
pub type vortex_array::scalar_fn::fns::variant_get::VariantGet::Options = vortex_array::dtype::FieldName
19114+
19115+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::arity(&self, _field_name: &vortex_array::dtype::FieldName) -> vortex_array::scalar_fn::Arity
19116+
19117+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::child_name(&self, _instance: &Self::Options, child_idx: usize) -> vortex_array::scalar_fn::ChildName
19118+
19119+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::coerce_args(&self, options: &Self::Options, args: &[vortex_array::dtype::DType]) -> vortex_error::VortexResult<alloc::vec::Vec<vortex_array::dtype::DType>>
19120+
19121+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::deserialize(&self, metadata: &[u8], _session: &vortex_session::VortexSession) -> vortex_error::VortexResult<Self::Options>
19122+
19123+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::execute(&self, _field_name: &vortex_array::dtype::FieldName, _args: &dyn vortex_array::scalar_fn::ExecutionArgs, _ctx: &mut vortex_array::ExecutionCtx) -> vortex_error::VortexResult<vortex_array::ArrayRef>
19124+
19125+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::fmt_sql(&self, field_name: &vortex_array::dtype::FieldName, expr: &vortex_array::expr::Expression, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
19126+
19127+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::id(&self) -> vortex_array::scalar_fn::ScalarFnId
19128+
19129+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::is_fallible(&self, _field_name: &vortex_array::dtype::FieldName) -> bool
19130+
19131+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::is_null_sensitive(&self, _field_name: &vortex_array::dtype::FieldName) -> bool
19132+
19133+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::reduce(&self, field_name: &vortex_array::dtype::FieldName, node: &dyn vortex_array::scalar_fn::ReduceNode, ctx: &dyn vortex_array::scalar_fn::ReduceCtx) -> vortex_error::VortexResult<core::option::Option<vortex_array::scalar_fn::ReduceNodeRef>>
19134+
19135+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::return_dtype(&self, _field_name: &vortex_array::dtype::FieldName, arg_dtypes: &[vortex_array::dtype::DType]) -> vortex_error::VortexResult<vortex_array::dtype::DType>
19136+
19137+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::serialize(&self, instance: &Self::Options) -> vortex_error::VortexResult<core::option::Option<alloc::vec::Vec<u8>>>
19138+
19139+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::simplify(&self, options: &Self::Options, expr: &vortex_array::expr::Expression, ctx: &dyn vortex_array::scalar_fn::SimplifyCtx) -> vortex_error::VortexResult<core::option::Option<vortex_array::expr::Expression>>
19140+
19141+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::simplify_untyped(&self, options: &Self::Options, expr: &vortex_array::expr::Expression) -> vortex_error::VortexResult<core::option::Option<vortex_array::expr::Expression>>
19142+
19143+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::stat_expression(&self, options: &Self::Options, expr: &vortex_array::expr::Expression, stat: vortex_array::expr::stats::Stat, catalog: &dyn vortex_array::expr::pruning::StatsCatalog) -> core::option::Option<vortex_array::expr::Expression>
19144+
19145+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::stat_falsification(&self, options: &Self::Options, expr: &vortex_array::expr::Expression, catalog: &dyn vortex_array::expr::pruning::StatsCatalog) -> core::option::Option<vortex_array::expr::Expression>
19146+
19147+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::validity(&self, options: &Self::Options, expression: &vortex_array::expr::Expression) -> vortex_error::VortexResult<core::option::Option<vortex_array::expr::Expression>>
19148+
1910119149
pub mod vortex_array::scalar_fn::fns::zip
1910219150

1910319151
pub struct vortex_array::scalar_fn::fns::zip::Zip
@@ -20182,6 +20230,44 @@ pub fn vortex_array::scalar_fn::fns::select::Select::stat_falsification(&self, o
2018220230

2018320231
pub fn vortex_array::scalar_fn::fns::select::Select::validity(&self, options: &Self::Options, expression: &vortex_array::expr::Expression) -> vortex_error::VortexResult<core::option::Option<vortex_array::expr::Expression>>
2018420232

20233+
impl vortex_array::scalar_fn::ScalarFnVTable for vortex_array::scalar_fn::fns::variant_get::VariantGet
20234+
20235+
pub type vortex_array::scalar_fn::fns::variant_get::VariantGet::Options = vortex_array::dtype::FieldName
20236+
20237+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::arity(&self, _field_name: &vortex_array::dtype::FieldName) -> vortex_array::scalar_fn::Arity
20238+
20239+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::child_name(&self, _instance: &Self::Options, child_idx: usize) -> vortex_array::scalar_fn::ChildName
20240+
20241+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::coerce_args(&self, options: &Self::Options, args: &[vortex_array::dtype::DType]) -> vortex_error::VortexResult<alloc::vec::Vec<vortex_array::dtype::DType>>
20242+
20243+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::deserialize(&self, metadata: &[u8], _session: &vortex_session::VortexSession) -> vortex_error::VortexResult<Self::Options>
20244+
20245+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::execute(&self, _field_name: &vortex_array::dtype::FieldName, _args: &dyn vortex_array::scalar_fn::ExecutionArgs, _ctx: &mut vortex_array::ExecutionCtx) -> vortex_error::VortexResult<vortex_array::ArrayRef>
20246+
20247+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::fmt_sql(&self, field_name: &vortex_array::dtype::FieldName, expr: &vortex_array::expr::Expression, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
20248+
20249+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::id(&self) -> vortex_array::scalar_fn::ScalarFnId
20250+
20251+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::is_fallible(&self, _field_name: &vortex_array::dtype::FieldName) -> bool
20252+
20253+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::is_null_sensitive(&self, _field_name: &vortex_array::dtype::FieldName) -> bool
20254+
20255+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::reduce(&self, field_name: &vortex_array::dtype::FieldName, node: &dyn vortex_array::scalar_fn::ReduceNode, ctx: &dyn vortex_array::scalar_fn::ReduceCtx) -> vortex_error::VortexResult<core::option::Option<vortex_array::scalar_fn::ReduceNodeRef>>
20256+
20257+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::return_dtype(&self, _field_name: &vortex_array::dtype::FieldName, arg_dtypes: &[vortex_array::dtype::DType]) -> vortex_error::VortexResult<vortex_array::dtype::DType>
20258+
20259+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::serialize(&self, instance: &Self::Options) -> vortex_error::VortexResult<core::option::Option<alloc::vec::Vec<u8>>>
20260+
20261+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::simplify(&self, options: &Self::Options, expr: &vortex_array::expr::Expression, ctx: &dyn vortex_array::scalar_fn::SimplifyCtx) -> vortex_error::VortexResult<core::option::Option<vortex_array::expr::Expression>>
20262+
20263+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::simplify_untyped(&self, options: &Self::Options, expr: &vortex_array::expr::Expression) -> vortex_error::VortexResult<core::option::Option<vortex_array::expr::Expression>>
20264+
20265+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::stat_expression(&self, options: &Self::Options, expr: &vortex_array::expr::Expression, stat: vortex_array::expr::stats::Stat, catalog: &dyn vortex_array::expr::pruning::StatsCatalog) -> core::option::Option<vortex_array::expr::Expression>
20266+
20267+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::stat_falsification(&self, options: &Self::Options, expr: &vortex_array::expr::Expression, catalog: &dyn vortex_array::expr::pruning::StatsCatalog) -> core::option::Option<vortex_array::expr::Expression>
20268+
20269+
pub fn vortex_array::scalar_fn::fns::variant_get::VariantGet::validity(&self, options: &Self::Options, expression: &vortex_array::expr::Expression) -> vortex_error::VortexResult<core::option::Option<vortex_array::expr::Expression>>
20270+
2018520271
impl vortex_array::scalar_fn::ScalarFnVTable for vortex_array::scalar_fn::fns::zip::Zip
2018620272

2018720273
pub type vortex_array::scalar_fn::fns::zip::Zip::Options = vortex_array::scalar_fn::EmptyOptions

vortex-array/src/expr/exprs.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ use crate::scalar_fn::fns::pack::PackOptions;
4545
use crate::scalar_fn::fns::root::Root;
4646
use crate::scalar_fn::fns::select::FieldSelection;
4747
use crate::scalar_fn::fns::select::Select;
48+
use crate::scalar_fn::fns::variant_get::VariantGet;
4849
use crate::scalar_fn::fns::zip::Zip;
4950

5051
// ---- Root ----
@@ -676,3 +677,17 @@ pub fn dynamic(
676677
pub fn list_contains(list: Expression, value: Expression) -> Expression {
677678
ListContains.new_expr(EmptyOptions, [list, value])
678679
}
680+
681+
// ---- VariantGet ----
682+
683+
/// Creates an expression that extracts a field from a variant object by name.
684+
///
685+
/// Returns a new variant containing the field's value, or null if the field does not exist.
686+
///
687+
/// ```rust
688+
/// # use vortex_array::expr::{variant_get, root};
689+
/// let expr = variant_get("field_name", root());
690+
/// ```
691+
pub fn variant_get(field: impl Into<FieldName>, child: Expression) -> Expression {
692+
VariantGet.new_expr(field.into(), vec![child])
693+
}

0 commit comments

Comments
 (0)