vortex_array/arrays/struct_/
array.rs1use std::borrow::Borrow;
5use std::iter::once;
6
7use itertools::Itertools;
8use vortex_array_macros::array_slots;
9use vortex_error::VortexExpect;
10use vortex_error::VortexResult;
11use vortex_error::vortex_bail;
12use vortex_error::vortex_err;
13
14use crate::ArrayRef;
15use crate::ArraySlots;
16use crate::IntoArray;
17use crate::array::Array;
18use crate::array::ArrayParts;
19use crate::array::EmptyArrayData;
20use crate::array::TypedArrayRef;
21use crate::array::child_to_validity;
22use crate::array::validity_to_child;
23use crate::arrays::ChunkedArray;
24use crate::arrays::Struct;
25use crate::builtins::ArrayBuiltins;
26use crate::dtype::DType;
27use crate::dtype::FieldName;
28use crate::dtype::FieldNames;
29use crate::dtype::StructFields;
30use crate::validity::Validity;
31
32#[array_slots(Struct)]
35pub struct StructSlots {
36 #[slot(0)]
38 pub validity: Option<ArrayRef>,
39 #[slot(1..)]
41 pub fields: Vec<ArrayRef>,
42}
43
44pub struct StructDataParts {
167 pub struct_fields: StructFields,
168 pub fields: Vec<ArrayRef>,
169 pub validity: Validity,
170}
171
172pub(super) fn struct_slots_with_capacity(
178 validity: &Validity,
179 length: usize,
180 nfields: usize,
181) -> ArraySlots {
182 let mut slots = ArraySlots::with_capacity(StructSlots::FIELDS_OFFSET + nfields);
183 slots.push(validity_to_child(validity, length));
184 slots
185}
186
187pub(super) fn make_struct_slots(
188 fields: impl IntoIterator<Item = ArrayRef>,
189 validity: &Validity,
190 length: usize,
191) -> ArraySlots {
192 let fields = fields.into_iter();
195 let mut slots = struct_slots_with_capacity(validity, length, fields.size_hint().0);
196 slots.extend(fields.map(Some));
197 slots
198}
199
200pub trait StructArrayExt: StructArraySlotsExt {
205 fn nullability(&self) -> crate::dtype::Nullability {
206 match self.as_ref().dtype() {
207 DType::Struct(_, nullability) => *nullability,
208 _ => unreachable!("StructArrayExt requires a struct dtype"),
209 }
210 }
211
212 fn names(&self) -> &FieldNames {
213 self.as_ref().dtype().as_struct_fields().names()
214 }
215
216 fn struct_validity(&self) -> Validity {
217 child_to_validity(self.validity(), self.nullability())
218 }
219
220 fn iter_unmasked_fields(&self) -> impl ExactSizeIterator<Item = &ArrayRef> + '_ {
222 self.fields().iter()
223 }
224
225 fn unmasked_field_opt(&self, idx: usize) -> Option<&ArrayRef> {
230 self.fields().get(idx)
231 }
232
233 fn unmasked_field(&self, idx: usize) -> &ArrayRef {
239 self.unmasked_field_opt(idx)
240 .vortex_expect("StructArray field slot")
241 }
242
243 fn unmasked_field_by_name_opt(&self, name: impl AsRef<str>) -> Option<&ArrayRef> {
244 let name = name.as_ref();
245 self.struct_fields()
246 .find(name)
247 .map(|idx| self.unmasked_field(idx))
248 }
249
250 fn unmasked_field_by_name(&self, name: impl AsRef<str>) -> VortexResult<&ArrayRef> {
251 let name = name.as_ref();
252 self.unmasked_field_by_name_opt(name).ok_or_else(|| {
253 vortex_err!(
254 "Field {name} not found in struct array with names {:?}",
255 self.names()
256 )
257 })
258 }
259
260 fn struct_fields(&self) -> &StructFields {
261 self.as_ref().dtype().as_struct_fields()
262 }
263}
264impl<T: TypedArrayRef<Struct>> StructArrayExt for T {}
265
266impl Array<Struct> {
267 pub fn new(
269 names: FieldNames,
270 fields: impl IntoIterator<Item = ArrayRef>,
271 length: usize,
272 validity: Validity,
273 ) -> Self {
274 Self::try_new(names, fields, length, validity)
275 .vortex_expect("StructArray construction failed")
276 }
277
278 pub fn try_new(
280 names: FieldNames,
281 fields: impl IntoIterator<Item = ArrayRef>,
282 length: usize,
283 validity: Validity,
284 ) -> VortexResult<Self> {
285 let fields = fields.into_iter();
286 let (lower, _) = fields.size_hint();
287 let mut field_dtypes = Vec::with_capacity(lower);
288 let mut slots = ArraySlots::with_capacity(StructSlots::FIELDS_OFFSET + lower);
289 slots.push(validity_to_child(&validity, length));
290 for field in fields {
291 field_dtypes.push(field.dtype().clone());
292 slots.push(Some(field));
293 }
294 let dtype = StructFields::new(names, field_dtypes);
295 Array::try_from_parts(ArrayParts::new(
296 Struct,
297 DType::Struct(dtype, validity.nullability()),
298 length,
299 EmptyArrayData,
300 slots,
301 ))
302 }
303
304 pub unsafe fn new_unchecked(
310 fields: impl IntoIterator<Item = ArrayRef>,
311 dtype: StructFields,
312 length: usize,
313 validity: Validity,
314 ) -> Self {
315 let outer_dtype = DType::Struct(dtype, validity.nullability());
316 let slots = make_struct_slots(fields, &validity, length);
317 unsafe {
318 Array::from_parts_unchecked(ArrayParts::new(
319 Struct,
320 outer_dtype,
321 length,
322 EmptyArrayData,
323 slots,
324 ))
325 }
326 }
327
328 pub fn try_new_with_dtype(
330 fields: impl IntoIterator<Item = ArrayRef>,
331 dtype: StructFields,
332 length: usize,
333 validity: Validity,
334 ) -> VortexResult<Self> {
335 let outer_dtype = DType::Struct(dtype, validity.nullability());
336 let slots = make_struct_slots(fields, &validity, length);
337 Array::try_from_parts(ArrayParts::new(
338 Struct,
339 outer_dtype,
340 length,
341 EmptyArrayData,
342 slots,
343 ))
344 }
345
346 pub fn from_fields<N: AsRef<str>>(items: &[(N, ArrayRef)]) -> VortexResult<Self> {
348 Self::try_from_iter(items.iter().map(|(a, b)| (a, b.clone())))
349 }
350
351 pub fn try_from_iter_with_validity<
353 N: AsRef<str>,
354 A: IntoArray,
355 T: IntoIterator<Item = (N, A)>,
356 >(
357 iter: T,
358 validity: Validity,
359 ) -> VortexResult<Self> {
360 let (names, fields): (Vec<FieldName>, Vec<ArrayRef>) = iter
361 .into_iter()
362 .map(|(name, fields)| (FieldName::from(name.as_ref()), fields.into_array()))
363 .unzip();
364 let len = fields
365 .first()
366 .map(|f| f.len())
367 .ok_or_else(|| vortex_err!("StructArray cannot be constructed from an empty slice of arrays because the length is unspecified"))?;
368
369 Self::try_new(FieldNames::from_iter(names), fields, len, validity)
370 }
371
372 pub fn try_from_iter<N: AsRef<str>, A: IntoArray, T: IntoIterator<Item = (N, A)>>(
374 iter: T,
375 ) -> VortexResult<Self> {
376 let (names, fields): (Vec<FieldName>, Vec<ArrayRef>) = iter
377 .into_iter()
378 .map(|(name, field)| (FieldName::from(name.as_ref()), field.into_array()))
379 .unzip();
380 let len = fields
381 .first()
382 .map(ArrayRef::len)
383 .ok_or_else(|| vortex_err!("StructArray cannot be constructed from an empty slice of arrays because the length is unspecified"))?;
384
385 Self::try_new(
386 FieldNames::from_iter(names),
387 fields,
388 len,
389 Validity::NonNullable,
390 )
391 }
392
393 pub fn project(&self, projection: &[FieldName]) -> VortexResult<Self> {
401 let mut children = Vec::with_capacity(projection.len());
402 let mut names = Vec::with_capacity(projection.len());
403
404 for f_name in projection {
405 let idx = self
406 .struct_fields()
407 .find(f_name.as_ref())
408 .ok_or_else(|| vortex_err!("Unknown field {f_name}"))?;
409
410 names.push(self.names()[idx].clone());
411 children.push(self.unmasked_field(idx).clone());
412 }
413
414 Self::try_new(
415 FieldNames::from(names.as_slice()),
416 children,
417 self.len(),
418 self.validity()?,
419 )
420 }
421
422 pub fn new_fieldless_with_len(len: usize) -> Self {
424 let dtype = DType::Struct(
425 StructFields::new(FieldNames::default(), Vec::new()),
426 crate::dtype::Nullability::NonNullable,
427 );
428 let slots = make_struct_slots([], &Validity::NonNullable, len);
429 unsafe {
430 Array::from_parts_unchecked(ArrayParts::new(Struct, dtype, len, EmptyArrayData, slots))
431 }
432 }
433
434 pub fn into_data_parts(self) -> StructDataParts {
436 let fields = self.fields().to_vec();
437 let validity = self.validity().vortex_expect("StructArray validity");
438 StructDataParts {
439 struct_fields: self.struct_fields().clone(),
440 fields,
441 validity,
442 }
443 }
444
445 pub fn remove_column(&self, name: impl Into<FieldName>) -> Option<(Self, ArrayRef)> {
446 let name = name.into();
447 let struct_dtype = self.struct_fields();
448 let len = self.len();
449
450 let position = struct_dtype.find(name.as_ref())?;
451
452 let slot_position = StructSlots::FIELDS_OFFSET + position;
453 let field = self.unmasked_field(position).clone();
454 let slots = self.slots();
457 let mut new_slots = ArraySlots::with_capacity(slots.len() - 1);
458 new_slots.extend(slots[..slot_position].iter().cloned());
459 new_slots.extend(slots[slot_position + 1..].iter().cloned());
460
461 let new_dtype = struct_dtype.without_field(position).ok()?;
462 let new_array = unsafe {
463 Array::from_parts_unchecked(ArrayParts::new(
464 Struct,
465 DType::Struct(new_dtype, self.dtype().nullability()),
466 len,
467 EmptyArrayData,
468 new_slots,
469 ))
470 };
471 Some((new_array, field))
472 }
473
474 pub fn with_column(&self, name: impl Into<FieldName>, array: ArrayRef) -> VortexResult<Self> {
475 let name = name.into();
476 let struct_dtype = self.struct_fields();
477
478 let names = struct_dtype.names().iter().cloned().chain(once(name));
479 let types = struct_dtype.fields().chain(once(array.dtype().clone()));
480 let new_fields = StructFields::new(names.collect(), types.collect());
481
482 let children = self.iter_unmasked_fields().cloned().chain(once(array));
483
484 Self::try_new_with_dtype(children, new_fields, self.len(), self.validity()?)
485 }
486
487 pub fn remove_column_owned(&self, name: impl Into<FieldName>) -> Option<(Self, ArrayRef)> {
488 self.remove_column(name)
489 }
490
491 pub fn try_concat<T>(chunks: impl IntoIterator<Item = T>) -> VortexResult<Self>
492 where
493 T: Borrow<Array<Struct>>,
494 {
495 let mut it = chunks.into_iter();
496 let Some(first) = it.next() else {
497 vortex_bail!("cannot concat empty iterator of arrays");
498 };
499 let first_dtype = first.borrow().dtype().clone();
500 let struct_fields = first_dtype.as_struct_fields().clone();
501 let names = struct_fields.names();
502
503 let it = [first].into_iter().chain(it);
504 let (field_arrays_per_chunk, validities) = it
505 .map(|chunk| {
506 let chunk = chunk.borrow();
507 if &first_dtype != chunk.dtype() {
508 vortex_bail!(
509 "cannot concatenate struct arrays with differing dtypes: {}, {}",
510 first_dtype,
511 chunk.dtype(),
512 );
513 }
514
515 let fields = names
516 .iter()
517 .map(|name| {
518 chunk
519 .unmasked_field_by_name(name)
520 .vortex_expect("field exists because it is in dtype")
521 .clone()
522 })
523 .collect::<Vec<_>>();
524 let validity = chunk.validity()?;
525
526 Ok((fields, (validity, chunk.len())))
527 })
528 .process_results(|iter| iter.unzip::<_, _, Vec<_>, Vec<_>>())?;
529
530 let field_arrays = struct_fields
531 .fields()
532 .enumerate()
533 .map(|(i, dtype)| {
534 let chunks = field_arrays_per_chunk.iter().map(|x| x[i].clone());
536 unsafe { ChunkedArray::new_unchecked(chunks, dtype) }.into_array()
537 })
538 .collect::<Vec<_>>();
539 let len = validities.iter().map(|(_v, len)| len).sum();
540 let validity = Validity::concat(validities).vortex_expect("verified non-empty above");
541
542 Ok(unsafe { Array::<Struct>::new_unchecked(field_arrays, struct_fields, len, validity) })
552 }
553
554 pub fn push_validity_into_children(&self, remove_struct_validity: bool) -> VortexResult<Self> {
560 let struct_validity = self.struct_validity();
561
562 let new_validity = if remove_struct_validity {
563 Validity::NonNullable
564 } else {
565 struct_validity.clone()
566 };
567
568 if struct_validity.definitely_no_nulls() {
571 return Self::try_new_with_dtype(
572 self.iter_unmasked_fields().cloned(),
573 self.struct_fields().clone(),
574 self.len(),
575 new_validity,
576 );
577 }
578
579 let mask = struct_validity.to_array(self.len());
581 let fields = self
582 .iter_unmasked_fields()
583 .map(|field| field.clone().mask(mask.clone()))
584 .collect::<VortexResult<Vec<_>>>()?;
585
586 Self::try_new(self.names().clone(), fields, self.len(), new_validity)
587 }
588}