1mod cast;
5mod compare;
6mod fill_null;
7pub(crate) mod is_constant;
8pub(crate) mod is_sorted;
9mod like;
10mod mask;
11pub(crate) mod min_max;
12pub(crate) mod rules;
13mod slice;
14
15use vortex_error::VortexResult;
16use vortex_mask::Mask;
17
18use super::Dict;
19use super::DictArray;
20use super::TakeExecute;
21use crate::ArrayRef;
22use crate::ExecutionCtx;
23use crate::IntoArray;
24use crate::array::ArrayView;
25use crate::arrays::dict::DictArraySlotsExt;
26use crate::arrays::filter::FilterReduce;
27
28impl TakeExecute for Dict {
29 fn take(
30 array: ArrayView<'_, Dict>,
31 indices: &ArrayRef,
32 _ctx: &mut ExecutionCtx,
33 ) -> VortexResult<Option<ArrayRef>> {
34 let codes = array.codes().take(indices.clone())?;
35
36 let taken = unsafe { DictArray::new_unchecked(codes, array.values().clone()) };
40
41 Ok(Some(taken.into_array()))
42 }
43}
44
45impl FilterReduce for Dict {
46 fn filter(array: ArrayView<'_, Dict>, mask: &Mask) -> VortexResult<Option<ArrayRef>> {
47 let codes = array.codes().filter(mask.clone())?;
48
49 let filtered = unsafe { DictArray::new_unchecked(codes, array.values().clone()) };
53
54 Ok(Some(filtered.into_array()))
55 }
56}
57
58#[cfg(test)]
59mod test {
60 use std::sync::LazyLock;
61
62 use vortex_buffer::buffer;
63 use vortex_error::VortexResult;
64 use vortex_session::VortexSession;
65
66 use crate::ArrayRef;
67 use crate::IntoArray;
68 use crate::VortexSessionExecute;
69 use crate::arrays::ConstantArray;
70 use crate::arrays::PrimitiveArray;
71 use crate::arrays::VarBinArray;
72 use crate::arrays::VarBinViewArray;
73 use crate::assert_arrays_eq;
74 use crate::builders::dict::dict_encode;
75 use crate::builtins::ArrayBuiltins;
76 use crate::compute::conformance::filter::test_filter_conformance;
77 use crate::compute::conformance::mask::test_mask_conformance;
78 use crate::compute::conformance::take::test_take_conformance;
79 use crate::dtype::DType;
80 use crate::dtype::Nullability;
81 use crate::dtype::PType::I32;
82 use crate::scalar_fn::fns::operators::Operator;
83
84 static SESSION: LazyLock<VortexSession> = LazyLock::new(crate::array_session);
85
86 #[test]
87 fn canonicalise_nullable_primitive() {
88 let mut ctx = SESSION.create_execution_ctx();
89 let values: Vec<Option<i32>> = (0..65)
90 .map(|i| match i % 3 {
91 0 => Some(42),
92 1 => Some(-9),
93 2 => None,
94 _ => unreachable!(),
95 })
96 .collect();
97
98 let dict = dict_encode(
99 &PrimitiveArray::from_option_iter(values.clone()).into_array(),
100 &mut SESSION.create_execution_ctx(),
101 )
102 .unwrap();
103
104 let expected = PrimitiveArray::from_option_iter(values);
105
106 assert_arrays_eq!(dict, expected, &mut ctx);
107 }
108
109 #[test]
110 fn canonicalise_non_nullable_primitive_32_unique_values() {
111 let mut ctx = SESSION.create_execution_ctx();
112 let unique_values: Vec<i32> = (0..32).collect();
113 let expected = PrimitiveArray::from_iter((0..1000).map(|i| unique_values[i % 32]));
114
115 let dict = dict_encode(
116 &expected.clone().into_array(),
117 &mut SESSION.create_execution_ctx(),
118 )
119 .unwrap()
120 .into_array();
121
122 assert_arrays_eq!(dict, expected, &mut ctx);
123 }
124
125 #[test]
126 fn canonicalise_non_nullable_primitive_100_unique_values() {
127 let mut ctx = SESSION.create_execution_ctx();
128 let unique_values: Vec<i32> = (0..100).collect();
129 let expected = PrimitiveArray::from_iter((0..1000).map(|i| unique_values[i % 100]));
130
131 let dict = dict_encode(
132 &expected.clone().into_array(),
133 &mut SESSION.create_execution_ctx(),
134 )
135 .unwrap()
136 .into_array();
137
138 assert_arrays_eq!(dict, expected, &mut ctx);
139 }
140
141 #[test]
142 fn canonicalise_nullable_varbin() -> VortexResult<()> {
143 let reference = VarBinViewArray::from_iter(
144 vec![Some("a"), Some("b"), None, Some("a"), None, Some("b")],
145 DType::Utf8(Nullability::Nullable),
146 );
147 assert_eq!(reference.len(), 6);
148 let mut ctx = SESSION.create_execution_ctx();
149 let dict = dict_encode(&reference.clone().into_array(), &mut ctx)?;
150 let flattened_dict = dict.into_array().execute::<VarBinViewArray>(&mut ctx)?;
151 let flattened_mask = flattened_dict
152 .validity()?
153 .execute_mask(flattened_dict.len(), &mut ctx)?;
154 let flattened_values = (0..flattened_dict.len())
155 .map(|i| {
156 flattened_mask
157 .value(i)
158 .then(|| flattened_dict.bytes_at(i).to_vec())
159 })
160 .collect::<Vec<_>>();
161 let reference_mask = reference
162 .validity()?
163 .execute_mask(reference.len(), &mut ctx)?;
164 let reference_values = (0..reference.len())
165 .map(|i| {
166 reference_mask
167 .value(i)
168 .then(|| reference.bytes_at(i).to_vec())
169 })
170 .collect::<Vec<_>>();
171 assert_eq!(flattened_values, reference_values);
172 Ok(())
173 }
174
175 fn sliced_dict_array() -> ArrayRef {
176 let reference = PrimitiveArray::from_option_iter([
177 Some(42),
178 Some(-9),
179 None,
180 Some(42),
181 Some(1),
182 Some(5),
183 ]);
184 let dict =
185 dict_encode(&reference.into_array(), &mut SESSION.create_execution_ctx()).unwrap();
186 dict.slice(1..4).unwrap()
187 }
188
189 #[test]
190 fn compare_sliced_dict() {
191 let mut ctx = SESSION.create_execution_ctx();
192 use crate::arrays::BoolArray;
193 let sliced = sliced_dict_array();
194 let compared = sliced
195 .binary(ConstantArray::new(42, 3).into_array(), Operator::Eq)
196 .unwrap();
197
198 let expected = BoolArray::from_iter([Some(false), None, Some(true)]);
199 assert_arrays_eq!(compared, expected.into_array(), &mut ctx);
200 }
201
202 #[test]
203 fn test_mask_dict_array() {
204 let array = dict_encode(
205 &buffer![2, 0, 2, 0, 10].into_array(),
206 &mut SESSION.create_execution_ctx(),
207 )
208 .unwrap();
209 test_mask_conformance(&array.into_array(), &mut SESSION.create_execution_ctx());
210
211 let array = dict_encode(
212 &PrimitiveArray::from_option_iter([Some(2), None, Some(2), Some(0), Some(10)])
213 .into_array(),
214 &mut SESSION.create_execution_ctx(),
215 )
216 .unwrap();
217 test_mask_conformance(&array.into_array(), &mut SESSION.create_execution_ctx());
218
219 let array = dict_encode(
220 &VarBinArray::from_iter(
221 [
222 Some("hello"),
223 None,
224 Some("hello"),
225 Some("good"),
226 Some("good"),
227 ],
228 DType::Utf8(Nullability::Nullable),
229 )
230 .into_array(),
231 &mut SESSION.create_execution_ctx(),
232 )
233 .unwrap();
234 test_mask_conformance(&array.into_array(), &mut SESSION.create_execution_ctx());
235 }
236
237 #[test]
238 fn test_filter_dict_array() {
239 let array = dict_encode(
240 &buffer![2, 0, 2, 0, 10].into_array(),
241 &mut SESSION.create_execution_ctx(),
242 )
243 .unwrap();
244 test_filter_conformance(&array.into_array(), &mut SESSION.create_execution_ctx());
245
246 let array = dict_encode(
247 &PrimitiveArray::from_option_iter([Some(2), None, Some(2), Some(0), Some(10)])
248 .into_array(),
249 &mut SESSION.create_execution_ctx(),
250 )
251 .unwrap();
252 test_filter_conformance(&array.into_array(), &mut SESSION.create_execution_ctx());
253
254 let array = dict_encode(
255 &VarBinArray::from_iter(
256 [
257 Some("hello"),
258 None,
259 Some("hello"),
260 Some("good"),
261 Some("good"),
262 ],
263 DType::Utf8(Nullability::Nullable),
264 )
265 .into_array(),
266 &mut SESSION.create_execution_ctx(),
267 )
268 .unwrap();
269 test_filter_conformance(&array.into_array(), &mut SESSION.create_execution_ctx());
270 }
271
272 #[test]
273 fn test_take_dict() {
274 let array = dict_encode(
275 &buffer![1, 2].into_array(),
276 &mut SESSION.create_execution_ctx(),
277 )
278 .unwrap();
279
280 let taken = array
281 .take(PrimitiveArray::from_option_iter([None::<i32>]).into_array())
282 .unwrap();
283 assert_eq!(taken.dtype(), &DType::Primitive(I32, Nullability::Nullable));
284 assert_arrays_eq!(
285 taken,
286 PrimitiveArray::from_option_iter([None::<i32>]),
287 &mut SESSION.create_execution_ctx()
288 );
289 }
290
291 #[test]
292 fn test_take_dict_conformance() {
293 let array = dict_encode(
294 &buffer![2, 0, 2, 0, 10].into_array(),
295 &mut SESSION.create_execution_ctx(),
296 )
297 .unwrap();
298 test_take_conformance(&array.into_array(), &mut SESSION.create_execution_ctx());
299
300 let array = dict_encode(
301 &PrimitiveArray::from_option_iter([Some(2), None, Some(2), Some(0), Some(10)])
302 .into_array(),
303 &mut SESSION.create_execution_ctx(),
304 )
305 .unwrap();
306 test_take_conformance(&array.into_array(), &mut SESSION.create_execution_ctx());
307
308 let array = dict_encode(
309 &VarBinArray::from_iter(
310 [
311 Some("hello"),
312 None,
313 Some("hello"),
314 Some("good"),
315 Some("good"),
316 ],
317 DType::Utf8(Nullability::Nullable),
318 )
319 .into_array(),
320 &mut SESSION.create_execution_ctx(),
321 )
322 .unwrap();
323 test_take_conformance(&array.into_array(), &mut SESSION.create_execution_ctx());
324 }
325}
326
327#[cfg(test)]
328mod tests {
329 use std::sync::LazyLock;
330
331 use rstest::rstest;
332 use vortex_buffer::buffer;
333 use vortex_session::VortexSession;
334
335 use crate::IntoArray;
336 use crate::VortexSessionExecute;
337 use crate::arrays::DictArray;
338 use crate::arrays::PrimitiveArray;
339 use crate::arrays::VarBinArray;
340 use crate::builders::dict::dict_encode;
341 use crate::compute::conformance::consistency::test_array_consistency;
342 use crate::dtype::DType;
343 use crate::dtype::Nullability;
344
345 static SESSION: LazyLock<VortexSession> = LazyLock::new(crate::array_session);
346
347 #[rstest]
348 #[case::dict_i32(dict_encode(&buffer![1i32, 2, 3, 2, 1].into_array(), &mut SESSION.create_execution_ctx()).unwrap())]
350 #[case::dict_nullable_codes(DictArray::try_new(
351 buffer![0u32, 1, 2, 2, 0].into_array(),
352 PrimitiveArray::from_option_iter([Some(10), Some(20), None]).into_array(),
353 ).unwrap())]
354 #[case::dict_nullable_values(dict_encode(
355 &PrimitiveArray::from_option_iter([Some(1i32), None, Some(2), Some(1), None]).into_array()
356 , &mut SESSION.create_execution_ctx()).unwrap())]
357 #[case::dict_u64(dict_encode(&buffer![100u64, 200, 100, 300, 200].into_array(), &mut SESSION.create_execution_ctx()).unwrap())]
358 #[case::dict_str(dict_encode(
360 &VarBinArray::from_iter(
361 ["hello", "world", "hello", "test", "world"].map(Some),
362 DType::Utf8(Nullability::NonNullable),
363 ).into_array()
364 , &mut SESSION.create_execution_ctx()).unwrap())]
365 #[case::dict_nullable_str(dict_encode(
366 &VarBinArray::from_iter(
367 [Some("hello"), None, Some("world"), Some("hello"), None],
368 DType::Utf8(Nullability::Nullable),
369 ).into_array()
370 , &mut SESSION.create_execution_ctx()).unwrap())]
371 #[case::dict_single(dict_encode(&buffer![42i32].into_array(), &mut SESSION.create_execution_ctx()).unwrap())]
373 #[case::dict_all_same(dict_encode(&buffer![5i32, 5, 5, 5, 5].into_array(), &mut SESSION.create_execution_ctx()).unwrap())]
374 #[case::dict_large(dict_encode(&PrimitiveArray::from_iter((0..1000).map(|i| i % 10)).into_array(), &mut SESSION.create_execution_ctx()).unwrap())]
375 fn test_dict_consistency(#[case] array: DictArray) {
376 test_array_consistency(&array.into_array(), &mut SESSION.create_execution_ctx());
377 }
378}