vortex_array/arrays/listview/vtable/
operator.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::sync::Arc;
5
6use vortex_error::VortexResult;
7use vortex_vector::listview::ListViewVector;
8
9use crate::ArrayRef;
10use crate::arrays::{ListViewArray, ListViewVTable};
11use crate::execution::{BatchKernelRef, BindCtx, kernel};
12use crate::vtable::{OperatorVTable, ValidityHelper};
13
14impl 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)]
45mod tests {
46    use vortex_dtype::PTypeDowncast;
47    use vortex_mask::Mask;
48    use vortex_vector::VectorOps;
49
50    use crate::IntoArray;
51    use crate::arrays::listview::tests::common::{
52        create_basic_listview, create_nullable_listview, create_overlapping_listview,
53    };
54    use crate::arrays::{ListViewArray, PrimitiveArray};
55    use crate::validity::Validity;
56
57    #[test]
58    fn test_listview_operator_basic() {
59        // Test basic ListView execution without selection.
60        // ListView: [[0,1,2], [3,4], [5,6], [7,8,9]]
61        let listview = create_basic_listview();
62
63        // Execute without selection.
64        let result = listview.execute().unwrap();
65        assert_eq!(result.len(), 4);
66
67        // Verify the result is a ListViewVector.
68        let listview_vector = result.as_list();
69
70        // Verify offsets.
71        let offsets = listview_vector.offsets().clone().into_u32();
72        assert_eq!(offsets.elements().as_slice(), &[0, 3, 5, 7]);
73
74        // Verify sizes.
75        let sizes = listview_vector.sizes().clone().into_u32();
76        assert_eq!(sizes.elements().as_slice(), &[3, 2, 2, 3]);
77
78        // Verify elements are intact.
79        let elements = listview_vector.elements().as_primitive().clone().into_i32();
80        assert_eq!(
81            elements.elements().as_slice(),
82            &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
83        );
84
85        // Verify validity is all valid.
86        assert!(matches!(listview_vector.validity(), Mask::AllTrue(_)));
87    }
88
89    #[test]
90    fn test_listview_operator_with_selection() {
91        // Create a ListView with 6 lists: [[10,11], [20,21], [30,31], [40,41], [50,51], [60,61]]
92        let elements =
93            PrimitiveArray::from_iter([10i32, 11, 20, 21, 30, 31, 40, 41, 50, 51, 60, 61])
94                .into_array();
95        let offsets = PrimitiveArray::from_iter([0u32, 2, 4, 6, 8, 10]).into_array();
96        let sizes = PrimitiveArray::from_iter([2u32, 2, 2, 2, 2, 2]).into_array();
97        let listview = ListViewArray::new(elements, offsets, sizes, Validity::AllValid);
98
99        // Create selection mask: [true, false, true, false, true, false].
100        let selection = Mask::from_iter([true, false, true, false, true, false]);
101
102        // Execute with selection.
103        let result = listview.execute_with_selection(&selection).unwrap();
104
105        // Verify filtered length (3 lists selected).
106        assert_eq!(result.len(), 3);
107
108        let listview_vector = result.as_list();
109
110        // Verify offsets are filtered to indices 0, 2, 4.
111        let offsets = listview_vector.offsets().clone().into_u32();
112        assert_eq!(offsets.elements().as_slice(), &[0, 4, 8]);
113
114        // Verify sizes are filtered to indices 0, 2, 4.
115        let sizes = listview_vector.sizes().clone().into_u32();
116        assert_eq!(sizes.elements().as_slice(), &[2, 2, 2]);
117
118        // Verify elements remain complete (not filtered).
119        let elements = listview_vector.elements().as_primitive().clone().into_i32();
120        assert_eq!(
121            elements.elements().as_slice(),
122            &[10, 11, 20, 21, 30, 31, 40, 41, 50, 51, 60, 61]
123        );
124    }
125
126    #[test]
127    fn test_listview_operator_with_nulls_and_selection() {
128        // Use the nullable listview: [[10,20], null, [50]]
129        let listview = create_nullable_listview();
130
131        // Create selection mask: [true, true, false].
132        let selection = Mask::from_iter([true, true, false]);
133
134        // Execute with selection.
135        let result = listview.execute_with_selection(&selection).unwrap();
136
137        // Verify filtered length (2 lists selected, including the null).
138        assert_eq!(result.len(), 2);
139
140        let listview_vector = result.as_list();
141
142        // Verify offsets are filtered to indices 0 and 1.
143        let offsets = listview_vector.offsets().clone().into_u32();
144        assert_eq!(offsets.elements().as_slice(), &[0, 2]);
145
146        // Verify sizes are filtered to indices 0 and 1.
147        let sizes = listview_vector.sizes().clone().into_u32();
148        assert_eq!(sizes.elements().as_slice(), &[2, 2]);
149
150        // Verify validity mask correctly shows first list valid, second list null.
151        assert!(listview_vector.validity().value(0)); // First list is valid.
152        assert!(!listview_vector.validity().value(1)); // Second list is null.
153
154        // Verify elements remain complete.
155        let elements = listview_vector.elements().as_primitive().clone().into_i32();
156        assert_eq!(elements.elements().as_slice(), &[10, 20, 30, 40, 50]);
157    }
158
159    #[test]
160    fn test_listview_operator_overlapping_with_selection() {
161        // Use the overlapping listview: [[5,6,7], [2,3], [8,9], [0,1], [1,2,3,4]]
162        let listview = create_overlapping_listview();
163
164        // Create selection mask: [true, false, true, true, false].
165        let selection = Mask::from_iter([true, false, true, true, false]);
166
167        // Execute with selection.
168        let result = listview.execute_with_selection(&selection).unwrap();
169
170        // Verify filtered length (3 lists selected).
171        assert_eq!(result.len(), 3);
172
173        let listview_vector = result.as_list();
174
175        // Verify offsets are filtered to indices 0, 2, 3 (out-of-order preserved).
176        let offsets = listview_vector.offsets().clone().into_u32();
177        assert_eq!(offsets.elements().as_slice(), &[5, 8, 0]);
178
179        // Verify sizes are filtered.
180        let sizes = listview_vector.sizes().clone().into_u32();
181        assert_eq!(sizes.elements().as_slice(), &[3, 2, 2]);
182
183        // Verify elements remain complete (all 10 elements).
184        let elements = listview_vector.elements().as_primitive().clone().into_i32();
185        assert_eq!(
186            elements.elements().as_slice(),
187            &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
188        );
189    }
190}