Skip to content

Commit 1831786

Browse files
committed
save
Signed-off-by: Andrew Duffy <[email protected]>
1 parent 3a19c77 commit 1831786

File tree

1 file changed

+85
-38
lines changed

1 file changed

+85
-38
lines changed

vortex-layout/src/layouts/struct_/reader.rs

Lines changed: 85 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@ use std::collections::BTreeSet;
55
use std::ops::Range;
66
use std::sync::Arc;
77

8-
use futures::{FutureExt, try_join};
8+
use futures::try_join;
99
use itertools::Itertools;
10-
use vortex_array::arrays::StructArray;
1110
use vortex_array::stats::Precision;
12-
use vortex_array::validity::Validity;
13-
use vortex_array::{Array, IntoArray, MaskFuture, ToCanonical};
11+
use vortex_array::{MaskFuture, ToCanonical};
1412
use vortex_dtype::{DType, FieldMask, FieldName, Nullability, StructFields};
1513
use vortex_error::{VortexExpect, VortexResult, vortex_err};
1614
use vortex_expr::transform::immediate_access::annotate_scope_access;
@@ -275,25 +273,10 @@ impl LayoutReader for StructReader {
275273

276274
// Partition the expression into expressions that can be evaluated over individual fields
277275
let array_future = match &self.partition_expr(expr.clone()) {
278-
Partitioned::Single(name, partition) => {
279-
// We should yield a new StructArray that has the given partition info.
280-
let name = name.clone();
281-
self.field_reader(&name)?
282-
.projection_evaluation(row_range, partition, mask)?
283-
.map(|result| {
284-
result.and_then(move |array| {
285-
let len = array.len();
286-
StructArray::try_new(
287-
vec![name].into(),
288-
vec![array],
289-
len,
290-
Validity::NonNullable,
291-
)
292-
.map(|a| a.into_array())
293-
})
294-
})
295-
.boxed()
296-
}
276+
Partitioned::Single(name, partition) => self
277+
.field_reader(name)?
278+
.projection_evaluation(row_range, partition, mask)?,
279+
297280
Partitioned::Multi(partitioned) => {
298281
// Apply the validity to each internal field instead.
299282
partitioned
@@ -329,8 +312,8 @@ mod tests {
329312
use vortex_array::validity::Validity;
330313
use vortex_array::{Array, ArrayContext, IntoArray, MaskFuture, ToCanonical};
331314
use vortex_buffer::buffer;
332-
use vortex_dtype::{DType, FieldDType, FieldName, Nullability, PType, StructFields};
333-
use vortex_expr::{col, eq, get_item, gt, lit, or, pack, root};
315+
use vortex_dtype::{DType, FieldName, Nullability, PType};
316+
use vortex_expr::{col, eq, get_item, gt, lit, or, pack, root, select};
334317
use vortex_io::runtime::single::block_on;
335318
use vortex_mask::Mask;
336319
use vortex_scalar::Scalar;
@@ -407,6 +390,57 @@ mod tests {
407390
(segments, layout)
408391
}
409392

393+
/// Writes a nested struct layout with the following values:
394+
///
395+
/// | a |
396+
/// |------------------|
397+
/// |`{"b": {"c": 4 }}`|
398+
/// | `NULL` |
399+
/// |`{"b": {"c": 6 }}`|
400+
#[fixture]
401+
fn nested_struct_layout() -> (Arc<dyn SegmentSource>, LayoutRef) {
402+
let ctx = ArrayContext::empty();
403+
let segments = Arc::new(TestSegments::default());
404+
let (ptr, eof) = SequenceId::root().split();
405+
let strategy =
406+
StructStrategy::new(FlatLayoutStrategy::default(), FlatLayoutStrategy::default());
407+
let layout = block_on(|handle| {
408+
strategy.write_stream(
409+
ctx,
410+
segments.clone(),
411+
StructArray::try_from_iter_with_validity(
412+
[(
413+
"a",
414+
StructArray::try_from_iter_with_validity(
415+
[(
416+
"b",
417+
StructArray::try_from_iter_with_validity(
418+
[("c", buffer![4, 5, 6].into_array())],
419+
Validity::NonNullable,
420+
)
421+
.unwrap()
422+
.into_array(),
423+
)],
424+
Validity::Array(BoolArray::from_iter([true, false, true]).into_array()),
425+
)
426+
.unwrap()
427+
.into_array(),
428+
)],
429+
Validity::NonNullable,
430+
)
431+
.unwrap()
432+
.into_array()
433+
.to_array_stream()
434+
.sequenced(ptr),
435+
eof,
436+
handle,
437+
)
438+
})
439+
.unwrap();
440+
441+
(segments, layout)
442+
}
443+
410444
#[rstest]
411445
fn test_struct_layout_or(
412446
#[from(struct_layout)] (segments, layout): (Arc<dyn SegmentSource>, LayoutRef),
@@ -527,24 +561,37 @@ mod tests {
527561
.unwrap();
528562

529563
let result = block_on(move |_| project).unwrap();
530-
// Result should be a struct with single field
564+
// Result should be the primitive array with a single field.
531565
assert_eq!(
532566
result.dtype(),
533-
&DType::Struct(
534-
StructFields::from_fields(
535-
vec![FieldName::from("a")].into(),
536-
vec![FieldDType::from(DType::Primitive(
537-
PType::I32,
538-
Nullability::NonNullable,
539-
))]
540-
),
541-
Nullability::Nullable,
542-
)
567+
&DType::Primitive(PType::I32, Nullability::Nullable)
543568
);
544569

570+
// ...and the result is masked with the validity of the parent StructArray
545571
assert_eq!(result.scalar_at(0), Scalar::null(result.dtype().clone()),);
572+
assert_eq!(result.scalar_at(1), 2.into());
573+
assert_eq!(result.scalar_at(2), 3.into());
574+
}
575+
576+
#[rstest]
577+
fn test_struct_layout_nested(
578+
#[from(nested_struct_layout)] (segments, layout): (Arc<dyn SegmentSource>, LayoutRef),
579+
) {
580+
// Project out the nested struct field.
581+
// The projection should preserve the nulls of the `a` column when we select out the
582+
// child column `c`.
583+
let reader = layout.new_reader("".into(), segments).unwrap();
584+
let expr = select(
585+
vec![FieldName::from("c")],
586+
get_item("b", get_item("a", root())),
587+
);
588+
589+
// Also make sure that nulls are handled appropriately.
590+
// Also make sure the mask is pushed down and applied to the nested types.
591+
let project = reader
592+
.projection_evaluation(&(0..3), &expr, MaskFuture::new_true(3))
593+
.unwrap();
546594

547-
assert!(result.scalar_at(1).is_valid());
548-
assert!(result.scalar_at(2).is_valid());
595+
// evaluate the projection, yielding some buff
549596
}
550597
}

0 commit comments

Comments
 (0)