vortex_array/arrays/union/
array.rs1use vortex_error::VortexExpect;
5use vortex_error::VortexResult;
6use vortex_error::vortex_ensure;
7use vortex_error::vortex_err;
8
9use crate::ArrayRef;
10use crate::ArraySlots;
11use crate::IntoArray;
12use crate::array::Array;
13use crate::array::ArrayParts;
14use crate::array::EmptyArrayData;
15use crate::array::TypedArrayRef;
16use crate::array_slots;
17use crate::arrays::ConstantArray;
18use crate::arrays::PrimitiveArray;
19use crate::arrays::Union;
20use crate::arrays::union::union_type_ids_dtype;
21use crate::dtype::DType;
22use crate::dtype::Nullability;
23use crate::dtype::PType;
24use crate::dtype::UnionVariants;
25use crate::scalar::Scalar;
26
27#[array_slots(Union)]
29pub struct UnionSlots {
30 #[slot(0)]
32 pub type_ids: ArrayRef,
33 #[slot(1..)]
35 pub children: Vec<ArrayRef>,
36}
37
38pub(super) fn make_union_parts(
39 type_ids: ArrayRef,
40 variants: UnionVariants,
41 children: impl IntoIterator<Item = ArrayRef>,
42) -> ArrayParts<Union> {
43 let len = type_ids.len();
44 let nullability = type_ids.dtype().nullability();
45 let children = children.into_iter();
46 let (lower, _) = children.size_hint();
47 let mut slots = ArraySlots::with_capacity(UnionSlots::CHILDREN_OFFSET + lower);
48 slots.push(Some(type_ids));
49 slots.extend(children.map(Some));
50
51 ArrayParts::new(
52 Union,
53 DType::Union(variants, nullability),
54 len,
55 EmptyArrayData,
56 )
57 .with_slots(slots)
58}
59
60pub struct UnionDataParts {
62 pub variants: UnionVariants,
64 pub type_ids: ArrayRef,
66 pub children: Vec<ArrayRef>,
68}
69
70pub trait UnionArrayExt: UnionArraySlotsExt {
75 fn variants(&self) -> &UnionVariants {
77 match self.as_ref().dtype() {
78 DType::Union(variants, _) => variants,
79 _ => unreachable!("UnionArrayExt requires a union dtype"),
80 }
81 }
82
83 fn iter_children(&self) -> impl ExactSizeIterator<Item = &ArrayRef> + '_ {
85 self.children().iter()
86 }
87
88 fn child(&self, index: usize) -> Option<&ArrayRef> {
90 self.children().get(index)
91 }
92
93 fn child_by_type_id(&self, type_id: u8) -> Option<&ArrayRef> {
95 self.child(self.variants().tag_to_child_index(type_id)?)
96 }
97
98 fn child_by_name_opt(&self, name: impl AsRef<str>) -> Option<&ArrayRef> {
100 self.child(self.variants().find(name)?)
101 }
102
103 fn child_by_name(&self, name: impl AsRef<str>) -> VortexResult<&ArrayRef> {
105 let name = name.as_ref();
106 self.child_by_name_opt(name).ok_or_else(|| {
107 vortex_err!(
108 "Variant {name} not found in union array with names {:?}",
109 self.variants().names()
110 )
111 })
112 }
113}
114impl<T: TypedArrayRef<Union>> UnionArrayExt for T {}
115
116impl Array<Union> {
117 pub fn new(
124 type_ids: ArrayRef,
125 variants: UnionVariants,
126 children: impl IntoIterator<Item = ArrayRef>,
127 ) -> Self {
128 Self::try_new(type_ids, variants, children).vortex_expect("UnionArray construction failed")
129 }
130
131 pub fn try_new(
141 type_ids: ArrayRef,
142 variants: UnionVariants,
143 children: impl IntoIterator<Item = ArrayRef>,
144 ) -> VortexResult<Self> {
145 vortex_ensure!(
146 matches!(type_ids.dtype(), DType::Primitive(PType::U8, _)),
147 "UnionArray type_ids must be u8, got {}",
148 type_ids.dtype()
149 );
150
151 Array::try_from_parts(make_union_parts(type_ids, variants, children))
152 }
153
154 pub unsafe fn new_unchecked(
161 type_ids: ArrayRef,
162 variants: UnionVariants,
163 children: impl IntoIterator<Item = ArrayRef>,
164 ) -> Self {
165 unsafe { Array::from_parts_unchecked(make_union_parts(type_ids, variants, children)) }
166 }
167
168 pub fn into_data_parts(self) -> UnionDataParts {
170 let variants = self.variants().clone();
171 let type_ids = self.type_ids().clone();
172 let children = self.iter_children().cloned().collect();
173 UnionDataParts {
174 variants,
175 type_ids,
176 children,
177 }
178 }
179
180 pub fn constant(scalar: &Scalar, len: usize) -> VortexResult<Self> {
189 let union = scalar
190 .as_union_opt()
191 .ok_or_else(|| vortex_err!("Expected a union scalar, got {}", scalar.dtype()))?;
192 let variants = union.variants().clone();
193 let nullability = union.nullability();
194
195 let type_ids = match union.type_id() {
196 Some(type_id) => Scalar::primitive(type_id, nullability),
197 None => Scalar::null(union_type_ids_dtype(nullability)),
198 };
199
200 let selected = union.child_index().zip(union.child());
201
202 let children = variants
203 .variants()
204 .enumerate()
205 .map(|(index, dtype)| {
206 let value = match &selected {
207 Some((selected_index, child)) if *selected_index == index => child.clone(),
208 _ => Scalar::default_value(&dtype),
209 };
210
211 ConstantArray::new(value, len).into_array()
212 })
213 .collect::<Vec<_>>();
214
215 Self::try_new(
216 ConstantArray::new(type_ids, len).into_array(),
217 variants,
218 children,
219 )
220 }
221
222 pub(crate) fn empty(variants: UnionVariants, nullability: Nullability) -> Self {
224 let type_ids = PrimitiveArray::empty::<u8>(nullability).into_array();
225 let children: Vec<_> = variants
226 .variants()
227 .map(|dtype| crate::Canonical::empty(&dtype).into_array())
228 .collect();
229
230 Self::new(type_ids, variants, children)
231 }
232}