mito2/sst/index/inverted_index/applier/builder/
in_list.rs1use std::collections::BTreeSet;
16
17use datafusion_expr::expr::InList;
18use index::inverted_index::search::predicate::{InListPredicate, Predicate};
19
20use crate::error::Result;
21use crate::sst::index::inverted_index::applier::builder::InvertedIndexApplierBuilder;
22
23impl InvertedIndexApplierBuilder<'_> {
24 pub(crate) fn collect_inlist(&mut self, inlist: &InList) -> Result<()> {
26 if inlist.negated {
27 return Ok(());
28 }
29 let Some(column_name) = Self::column_name(&inlist.expr) else {
30 return Ok(());
31 };
32 let Some((column_id, data_type)) = self.column_id_and_type(column_name)? else {
33 return Ok(());
34 };
35
36 let mut predicate = InListPredicate {
37 list: BTreeSet::new(),
38 };
39 for lit in &inlist.list {
40 let Some(lit) = Self::nonnull_lit(lit) else {
41 return Ok(());
42 };
43
44 predicate
45 .list
46 .insert(Self::encode_lit(lit, data_type.clone())?);
47 }
48
49 self.add_predicate(column_id, Predicate::InList(predicate));
50 Ok(())
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use std::collections::HashSet;
57
58 use super::*;
59 use crate::error::Error;
60 use crate::sst::index::inverted_index::applier::builder::tests::{
61 encoded_string, field_column, int64_lit, nonexistent_column, string_lit, tag_column,
62 test_object_store, test_region_metadata,
63 };
64 use crate::sst::index::puffin_manager::PuffinManagerFactory;
65
66 #[test]
67 fn test_collect_in_list_basic() {
68 let (_d, facotry) = PuffinManagerFactory::new_for_test_block("test_collect_in_list_basic_");
69 let metadata = test_region_metadata();
70 let mut builder = InvertedIndexApplierBuilder::new(
71 "test".to_string(),
72 test_object_store(),
73 &metadata,
74 HashSet::from_iter([1, 2, 3]),
75 facotry,
76 );
77
78 let in_list = InList {
79 expr: Box::new(tag_column()),
80 list: vec![string_lit("foo"), string_lit("bar")],
81 negated: false,
82 };
83
84 builder.collect_inlist(&in_list).unwrap();
85
86 let predicates = builder.output.get(&1).unwrap();
87 assert_eq!(predicates.len(), 1);
88 assert_eq!(
89 predicates[0],
90 Predicate::InList(InListPredicate {
91 list: BTreeSet::from_iter([encoded_string("foo"), encoded_string("bar")])
92 })
93 );
94 }
95
96 #[test]
97 fn test_collect_in_list_negated() {
98 let (_d, facotry) =
99 PuffinManagerFactory::new_for_test_block("test_collect_in_list_negated_");
100 let metadata = test_region_metadata();
101 let mut builder = InvertedIndexApplierBuilder::new(
102 "test".to_string(),
103 test_object_store(),
104 &metadata,
105 HashSet::from_iter([1, 2, 3]),
106 facotry,
107 );
108
109 let in_list = InList {
110 expr: Box::new(tag_column()),
111 list: vec![string_lit("foo"), string_lit("bar")],
112 negated: true,
113 };
114
115 builder.collect_inlist(&in_list).unwrap();
116 assert!(builder.output.is_empty());
117 }
118
119 #[test]
120 fn test_collect_in_list_field_column() {
121 let (_d, facotry) =
122 PuffinManagerFactory::new_for_test_block("test_collect_in_list_field_column_");
123 let metadata = test_region_metadata();
124 let mut builder = InvertedIndexApplierBuilder::new(
125 "test".to_string(),
126 test_object_store(),
127 &metadata,
128 HashSet::from_iter([1, 2, 3]),
129 facotry,
130 );
131
132 let in_list = InList {
133 expr: Box::new(field_column()),
134 list: vec![string_lit("foo"), string_lit("bar")],
135 negated: false,
136 };
137
138 builder.collect_inlist(&in_list).unwrap();
139
140 let predicates = builder.output.get(&3).unwrap();
141 assert_eq!(predicates.len(), 1);
142 assert_eq!(
143 predicates[0],
144 Predicate::InList(InListPredicate {
145 list: BTreeSet::from_iter([encoded_string("foo"), encoded_string("bar")])
146 })
147 );
148 }
149
150 #[test]
151 fn test_collect_in_list_type_mismatch() {
152 let (_d, facotry) =
153 PuffinManagerFactory::new_for_test_block("test_collect_in_list_type_mismatch_");
154 let metadata = test_region_metadata();
155 let mut builder = InvertedIndexApplierBuilder::new(
156 "test".to_string(),
157 test_object_store(),
158 &metadata,
159 HashSet::from_iter([1, 2, 3]),
160 facotry,
161 );
162
163 let in_list = InList {
164 expr: Box::new(tag_column()),
165 list: vec![int64_lit(123), int64_lit(456)],
166 negated: false,
167 };
168
169 let res = builder.collect_inlist(&in_list);
170 assert!(matches!(res, Err(Error::FieldTypeMismatch { .. })));
171 assert!(builder.output.is_empty());
172 }
173
174 #[test]
175 fn test_collect_in_list_nonexistent_column() {
176 let (_d, facotry) =
177 PuffinManagerFactory::new_for_test_block("test_collect_in_list_nonexistent_column_");
178
179 let metadata = test_region_metadata();
180 let mut builder = InvertedIndexApplierBuilder::new(
181 "test".to_string(),
182 test_object_store(),
183 &metadata,
184 HashSet::from_iter([1, 2, 3]),
185 facotry,
186 );
187
188 let in_list = InList {
189 expr: Box::new(nonexistent_column()),
190 list: vec![string_lit("foo"), string_lit("bar")],
191 negated: false,
192 };
193
194 let res = builder.collect_inlist(&in_list);
195 assert!(matches!(res, Err(Error::ColumnNotFound { .. })));
196 assert!(builder.output.is_empty());
197 }
198}