Skip to content

Commit 423b93f

Browse files
authored
Feature: Add naive ListView::bind (#5207)
This is a naive implementation, and we can do the fun switch-at-runtime implementation later Signed-off-by: Connor Tsui <[email protected]>
1 parent 7c0977a commit 423b93f

File tree

2 files changed

+200
-1
lines changed

2 files changed

+200
-1
lines changed

vortex-array/src/arrays/listview/vtable/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::{EncodingId, EncodingRef, vtable};
88
mod array;
99
mod canonical;
1010
mod operations;
11+
mod operator;
1112
mod serde;
1213
mod validity;
1314
mod visitor;
@@ -28,7 +29,7 @@ impl VTable for ListViewVTable {
2829
type VisitorVTable = Self;
2930
type ComputeVTable = NotSupported;
3031
type EncodeVTable = NotSupported;
31-
type OperatorVTable = NotSupported;
32+
type OperatorVTable = Self;
3233
type SerdeVTable = Self;
3334

3435
fn id(_encoding: &Self::Encoding) -> EncodingId {
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use std::sync::Arc;
5+
6+
use vortex_error::VortexResult;
7+
use vortex_vector::listview::ListViewVector;
8+
9+
use crate::ArrayRef;
10+
use crate::arrays::{ListViewArray, ListViewVTable};
11+
use crate::execution::{BatchKernelRef, BindCtx, kernel};
12+
use crate::vtable::{OperatorVTable, ValidityHelper};
13+
14+
impl OperatorVTable<ListViewVTable> for ListViewVTable {
15+
fn bind(
16+
array: &ListViewArray,
17+
selection: Option<&ArrayRef>,
18+
ctx: &mut dyn BindCtx,
19+
) -> VortexResult<BatchKernelRef> {
20+
// Create selection kernels over just the views rather than calculate the exact elements we
21+
// need from the child `elements` array.
22+
let offsets_kernel = ctx.bind(array.offsets(), selection)?;
23+
let sizes_kernel = ctx.bind(array.sizes(), selection)?;
24+
25+
let validity = ctx.bind_validity(array.validity(), array.len(), selection)?;
26+
27+
// TODO There is definitely a smarter way we can do this...
28+
let elements_kernel = ctx.bind(array.elements(), None)?;
29+
30+
Ok(kernel(move || {
31+
let offsets = offsets_kernel.execute()?.into_primitive();
32+
let sizes = sizes_kernel.execute()?.into_primitive();
33+
34+
let validity_mask = validity.execute()?;
35+
36+
// TODO There is definitely a smarter way we can do this...
37+
let elements = elements_kernel.execute()?;
38+
39+
Ok(ListViewVector::try_new(Arc::new(elements), offsets, sizes, validity_mask)?.into())
40+
}))
41+
}
42+
}
43+
44+
#[cfg(test)]
45+
mod tests {
46+
use std::sync::Arc;
47+
48+
use vortex_dtype::PTypeDowncast;
49+
use vortex_mask::Mask;
50+
use vortex_vector::VectorOps;
51+
52+
use crate::IntoArray;
53+
use crate::arrays::listview::tests::common::{
54+
create_basic_listview, create_nullable_listview, create_overlapping_listview,
55+
};
56+
use crate::arrays::{BoolArray, ListViewArray, PrimitiveArray};
57+
use crate::validity::Validity;
58+
59+
#[test]
60+
fn test_listview_operator_basic() {
61+
// Test basic ListView execution without selection.
62+
// ListView: [[0,1,2], [3,4], [5,6], [7,8,9]]
63+
let listview = create_basic_listview();
64+
65+
// Execute without selection.
66+
let result = listview.execute().unwrap();
67+
assert_eq!(result.len(), 4);
68+
69+
// Verify the result is a ListViewVector.
70+
let listview_vector = result.as_list();
71+
72+
// Verify offsets.
73+
let offsets = listview_vector.offsets().clone().into_u32();
74+
assert_eq!(offsets.elements().as_slice(), &[0, 3, 5, 7]);
75+
76+
// Verify sizes.
77+
let sizes = listview_vector.sizes().clone().into_u32();
78+
assert_eq!(sizes.elements().as_slice(), &[3, 2, 2, 3]);
79+
80+
// Verify elements are intact.
81+
let elements = listview_vector.elements().as_primitive().clone().into_i32();
82+
assert_eq!(
83+
elements.elements().as_slice(),
84+
&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
85+
);
86+
87+
// Verify validity is all valid.
88+
assert!(matches!(listview_vector.validity(), Mask::AllTrue(_)));
89+
}
90+
91+
#[test]
92+
fn test_listview_operator_with_selection() {
93+
// Create a ListView with 6 lists: [[10,11], [20,21], [30,31], [40,41], [50,51], [60,61]]
94+
let elements =
95+
PrimitiveArray::from_iter([10i32, 11, 20, 21, 30, 31, 40, 41, 50, 51, 60, 61])
96+
.into_array();
97+
let offsets = PrimitiveArray::from_iter([0u32, 2, 4, 6, 8, 10]).into_array();
98+
let sizes = PrimitiveArray::from_iter([2u32, 2, 2, 2, 2, 2]).into_array();
99+
let listview = ListViewArray::new(elements, offsets, sizes, Validity::AllValid);
100+
101+
// Create selection mask: [true, false, true, false, true, false].
102+
let selection = BoolArray::from_iter([true, false, true, false, true, false]).into_array();
103+
104+
// Execute with selection.
105+
let result = listview
106+
.execute_with_selection(Some(&Arc::new(selection)))
107+
.unwrap();
108+
109+
// Verify filtered length (3 lists selected).
110+
assert_eq!(result.len(), 3);
111+
112+
let listview_vector = result.as_list();
113+
114+
// Verify offsets are filtered to indices 0, 2, 4.
115+
let offsets = listview_vector.offsets().clone().into_u32();
116+
assert_eq!(offsets.elements().as_slice(), &[0, 4, 8]);
117+
118+
// Verify sizes are filtered to indices 0, 2, 4.
119+
let sizes = listview_vector.sizes().clone().into_u32();
120+
assert_eq!(sizes.elements().as_slice(), &[2, 2, 2]);
121+
122+
// Verify elements remain complete (not filtered).
123+
let elements = listview_vector.elements().as_primitive().clone().into_i32();
124+
assert_eq!(
125+
elements.elements().as_slice(),
126+
&[10, 11, 20, 21, 30, 31, 40, 41, 50, 51, 60, 61]
127+
);
128+
}
129+
130+
#[test]
131+
fn test_listview_operator_with_nulls_and_selection() {
132+
// Use the nullable listview: [[10,20], null, [50]]
133+
let listview = create_nullable_listview();
134+
135+
// Create selection mask: [true, true, false].
136+
let selection = BoolArray::from_iter([true, true, false]).into_array();
137+
138+
// Execute with selection.
139+
let result = listview
140+
.execute_with_selection(Some(&Arc::new(selection)))
141+
.unwrap();
142+
143+
// Verify filtered length (2 lists selected, including the null).
144+
assert_eq!(result.len(), 2);
145+
146+
let listview_vector = result.as_list();
147+
148+
// Verify offsets are filtered to indices 0 and 1.
149+
let offsets = listview_vector.offsets().clone().into_u32();
150+
assert_eq!(offsets.elements().as_slice(), &[0, 2]);
151+
152+
// Verify sizes are filtered to indices 0 and 1.
153+
let sizes = listview_vector.sizes().clone().into_u32();
154+
assert_eq!(sizes.elements().as_slice(), &[2, 2]);
155+
156+
// Verify validity mask correctly shows first list valid, second list null.
157+
assert!(listview_vector.validity().value(0)); // First list is valid.
158+
assert!(!listview_vector.validity().value(1)); // Second list is null.
159+
160+
// Verify elements remain complete.
161+
let elements = listview_vector.elements().as_primitive().clone().into_i32();
162+
assert_eq!(elements.elements().as_slice(), &[10, 20, 30, 40, 50]);
163+
}
164+
165+
#[test]
166+
fn test_listview_operator_overlapping_with_selection() {
167+
// Use the overlapping listview: [[5,6,7], [2,3], [8,9], [0,1], [1,2,3,4]]
168+
let listview = create_overlapping_listview();
169+
170+
// Create selection mask: [true, false, true, true, false].
171+
let selection = BoolArray::from_iter([true, false, true, true, false]).into_array();
172+
173+
// Execute with selection.
174+
let result = listview
175+
.execute_with_selection(Some(&Arc::new(selection)))
176+
.unwrap();
177+
178+
// Verify filtered length (3 lists selected).
179+
assert_eq!(result.len(), 3);
180+
181+
let listview_vector = result.as_list();
182+
183+
// Verify offsets are filtered to indices 0, 2, 3 (out-of-order preserved).
184+
let offsets = listview_vector.offsets().clone().into_u32();
185+
assert_eq!(offsets.elements().as_slice(), &[5, 8, 0]);
186+
187+
// Verify sizes are filtered.
188+
let sizes = listview_vector.sizes().clone().into_u32();
189+
assert_eq!(sizes.elements().as_slice(), &[3, 2, 2]);
190+
191+
// Verify elements remain complete (all 10 elements).
192+
let elements = listview_vector.elements().as_primitive().clone().into_i32();
193+
assert_eq!(
194+
elements.elements().as_slice(),
195+
&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
196+
);
197+
}
198+
}

0 commit comments

Comments
 (0)