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(
296 ArrayParts::new(
297 Struct,
298 DType::Struct(dtype, validity.nullability()),
299 length,
300 EmptyArrayData,
301 )
302 .with_slots(slots),
303 )
304 }
305
306 pub unsafe fn new_unchecked(
312 fields: impl IntoIterator<Item = ArrayRef>,
313 dtype: StructFields,
314 length: usize,
315 validity: Validity,
316 ) -> Self {
317 let outer_dtype = DType::Struct(dtype, validity.nullability());
318 let slots = make_struct_slots(fields, &validity, length);
319 unsafe {
320 Array::from_parts_unchecked(
321 ArrayParts::new(Struct, outer_dtype, length, EmptyArrayData).with_slots(slots),
322 )
323 }
324 }
325
326 pub fn try_new_with_dtype(
328 fields: impl IntoIterator<Item = ArrayRef>,
329 dtype: StructFields,
330 length: usize,
331 validity: Validity,
332 ) -> VortexResult<Self> {
333 let outer_dtype = DType::Struct(dtype, validity.nullability());
334 let slots = make_struct_slots(fields, &validity, length);
335 Array::try_from_parts(
336 ArrayParts::new(Struct, outer_dtype, length, EmptyArrayData).with_slots(slots),
337 )
338 }
339
340 pub fn from_fields<N: AsRef<str>>(items: &[(N, ArrayRef)]) -> VortexResult<Self> {
342 Self::try_from_iter(items.iter().map(|(a, b)| (a, b.clone())))
343 }
344
345 pub fn try_from_iter_with_validity<
347 N: AsRef<str>,
348 A: IntoArray,
349 T: IntoIterator<Item = (N, A)>,
350 >(
351 iter: T,
352 validity: Validity,
353 ) -> VortexResult<Self> {
354 let (names, fields): (Vec<FieldName>, Vec<ArrayRef>) = iter
355 .into_iter()
356 .map(|(name, fields)| (FieldName::from(name.as_ref()), fields.into_array()))
357 .unzip();
358 let len = fields
359 .first()
360 .map(|f| f.len())
361 .ok_or_else(|| vortex_err!("StructArray cannot be constructed from an empty slice of arrays because the length is unspecified"))?;
362
363 Self::try_new(FieldNames::from_iter(names), fields, len, validity)
364 }
365
366 pub fn try_from_iter<N: AsRef<str>, A: IntoArray, T: IntoIterator<Item = (N, A)>>(
368 iter: T,
369 ) -> VortexResult<Self> {
370 let (names, fields): (Vec<FieldName>, Vec<ArrayRef>) = iter
371 .into_iter()
372 .map(|(name, field)| (FieldName::from(name.as_ref()), field.into_array()))
373 .unzip();
374 let len = fields
375 .first()
376 .map(ArrayRef::len)
377 .ok_or_else(|| vortex_err!("StructArray cannot be constructed from an empty slice of arrays because the length is unspecified"))?;
378
379 Self::try_new(
380 FieldNames::from_iter(names),
381 fields,
382 len,
383 Validity::NonNullable,
384 )
385 }
386
387 pub fn project(&self, projection: &[FieldName]) -> VortexResult<Self> {
395 let mut children = Vec::with_capacity(projection.len());
396 let mut names = Vec::with_capacity(projection.len());
397
398 for f_name in projection {
399 let idx = self
400 .struct_fields()
401 .find(f_name.as_ref())
402 .ok_or_else(|| vortex_err!("Unknown field {f_name}"))?;
403
404 names.push(self.names()[idx].clone());
405 children.push(self.unmasked_field(idx).clone());
406 }
407
408 Self::try_new(
409 FieldNames::from(names.as_slice()),
410 children,
411 self.len(),
412 self.validity()?,
413 )
414 }
415
416 pub fn new_fieldless_with_len(len: usize) -> Self {
418 let dtype = DType::Struct(
419 StructFields::new(FieldNames::default(), Vec::new()),
420 crate::dtype::Nullability::NonNullable,
421 );
422 let slots = make_struct_slots([], &Validity::NonNullable, len);
423 unsafe {
424 Array::from_parts_unchecked(
425 ArrayParts::new(Struct, dtype, len, EmptyArrayData).with_slots(slots),
426 )
427 }
428 }
429
430 pub fn into_data_parts(self) -> StructDataParts {
432 let fields = self.fields().to_vec();
433 let validity = self.validity().vortex_expect("StructArray validity");
434 StructDataParts {
435 struct_fields: self.struct_fields().clone(),
436 fields,
437 validity,
438 }
439 }
440
441 pub fn remove_column(&self, name: impl Into<FieldName>) -> Option<(Self, ArrayRef)> {
442 let name = name.into();
443 let struct_dtype = self.struct_fields();
444 let len = self.len();
445
446 let position = struct_dtype.find(name.as_ref())?;
447
448 let slot_position = StructSlots::FIELDS_OFFSET + position;
449 let field = self.unmasked_field(position).clone();
450 let slots = self.slots();
453 let mut new_slots = ArraySlots::with_capacity(slots.len() - 1);
454 new_slots.extend(slots[..slot_position].iter().cloned());
455 new_slots.extend(slots[slot_position + 1..].iter().cloned());
456
457 let new_dtype = struct_dtype.without_field(position).ok()?;
458 let new_array = unsafe {
459 Array::from_parts_unchecked(
460 ArrayParts::new(
461 Struct,
462 DType::Struct(new_dtype, self.dtype().nullability()),
463 len,
464 EmptyArrayData,
465 )
466 .with_slots(new_slots),
467 )
468 };
469 Some((new_array, field))
470 }
471
472 pub fn with_column(&self, name: impl Into<FieldName>, array: ArrayRef) -> VortexResult<Self> {
473 let name = name.into();
474 let struct_dtype = self.struct_fields();
475
476 let names = struct_dtype.names().iter().cloned().chain(once(name));
477 let types = struct_dtype.fields().chain(once(array.dtype().clone()));
478 let new_fields = StructFields::new(names.collect(), types.collect());
479
480 let children = self.iter_unmasked_fields().cloned().chain(once(array));
481
482 Self::try_new_with_dtype(children, new_fields, self.len(), self.validity()?)
483 }
484
485 pub fn remove_column_owned(&self, name: impl Into<FieldName>) -> Option<(Self, ArrayRef)> {
486 self.remove_column(name)
487 }
488
489 pub fn try_concat<T>(chunks: impl IntoIterator<Item = T>) -> VortexResult<Self>
490 where
491 T: Borrow<Array<Struct>>,
492 {
493 let mut it = chunks.into_iter();
494 let Some(first) = it.next() else {
495 vortex_bail!("cannot concat empty iterator of arrays");
496 };
497 let first_dtype = first.borrow().dtype().clone();
498 let struct_fields = first_dtype.as_struct_fields().clone();
499 let names = struct_fields.names();
500
501 let it = [first].into_iter().chain(it);
502 let (field_arrays_per_chunk, validities) = it
503 .map(|chunk| {
504 let chunk = chunk.borrow();
505 if &first_dtype != chunk.dtype() {
506 vortex_bail!(
507 "cannot concatenate struct arrays with differing dtypes: {}, {}",
508 first_dtype,
509 chunk.dtype(),
510 );
511 }
512
513 let fields = names
514 .iter()
515 .map(|name| {
516 chunk
517 .unmasked_field_by_name(name)
518 .vortex_expect("field exists because it is in dtype")
519 .clone()
520 })
521 .collect::<Vec<_>>();
522 let validity = chunk.validity()?;
523
524 Ok((fields, (validity, chunk.len())))
525 })
526 .process_results(|iter| iter.unzip::<_, _, Vec<_>, Vec<_>>())?;
527
528 let field_arrays = struct_fields
529 .fields()
530 .enumerate()
531 .map(|(i, dtype)| {
532 let chunks = field_arrays_per_chunk.iter().map(|x| x[i].clone());
534 unsafe { ChunkedArray::new_unchecked(chunks, dtype) }.into_array()
535 })
536 .collect::<Vec<_>>();
537 let len = validities.iter().map(|(_v, len)| len).sum();
538 let validity = Validity::concat(validities).vortex_expect("verified non-empty above");
539
540 Ok(unsafe { Array::<Struct>::new_unchecked(field_arrays, struct_fields, len, validity) })
550 }
551
552 pub fn push_validity_into_children(&self, remove_struct_validity: bool) -> VortexResult<Self> {
558 let struct_validity = self.struct_validity();
559
560 let new_validity = if remove_struct_validity {
561 Validity::NonNullable
562 } else {
563 struct_validity.clone()
564 };
565
566 if struct_validity.definitely_no_nulls() {
569 return Self::try_new_with_dtype(
570 self.iter_unmasked_fields().cloned(),
571 self.struct_fields().clone(),
572 self.len(),
573 new_validity,
574 );
575 }
576
577 let mask = struct_validity.to_array(self.len());
579 let fields = self
580 .iter_unmasked_fields()
581 .map(|field| field.clone().mask(mask.clone()))
582 .collect::<VortexResult<Vec<_>>>()?;
583
584 Self::try_new(self.names().clone(), fields, self.len(), new_validity)
585 }
586}