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 store_api::region_request::PathType;
59
60 use super::*;
61 use crate::error::Error;
62 use crate::sst::index::inverted_index::applier::builder::tests::{
63 encoded_string, field_column, int64_lit, nonexistent_column, string_lit, tag_column,
64 test_object_store, test_region_metadata,
65 };
66 use crate::sst::index::puffin_manager::PuffinManagerFactory;
67
68 #[test]
69 fn test_collect_in_list_basic() {
70 let (_d, facotry) = PuffinManagerFactory::new_for_test_block("test_collect_in_list_basic_");
71 let metadata = test_region_metadata();
72 let mut builder = InvertedIndexApplierBuilder::new(
73 "test".to_string(),
74 PathType::Bare,
75 test_object_store(),
76 &metadata,
77 HashSet::from_iter([1, 2, 3]),
78 facotry,
79 );
80
81 let in_list = InList {
82 expr: Box::new(tag_column()),
83 list: vec![string_lit("foo"), string_lit("bar")],
84 negated: false,
85 };
86
87 builder.collect_inlist(&in_list).unwrap();
88
89 let predicates = builder.output.get(&1).unwrap();
90 assert_eq!(predicates.len(), 1);
91 assert_eq!(
92 predicates[0],
93 Predicate::InList(InListPredicate {
94 list: BTreeSet::from_iter([encoded_string("foo"), encoded_string("bar")])
95 })
96 );
97 }
98
99 #[test]
100 fn test_collect_in_list_negated() {
101 let (_d, facotry) =
102 PuffinManagerFactory::new_for_test_block("test_collect_in_list_negated_");
103 let metadata = test_region_metadata();
104 let mut builder = InvertedIndexApplierBuilder::new(
105 "test".to_string(),
106 PathType::Bare,
107 test_object_store(),
108 &metadata,
109 HashSet::from_iter([1, 2, 3]),
110 facotry,
111 );
112
113 let in_list = InList {
114 expr: Box::new(tag_column()),
115 list: vec![string_lit("foo"), string_lit("bar")],
116 negated: true,
117 };
118
119 builder.collect_inlist(&in_list).unwrap();
120 assert!(builder.output.is_empty());
121 }
122
123 #[test]
124 fn test_collect_in_list_field_column() {
125 let (_d, facotry) =
126 PuffinManagerFactory::new_for_test_block("test_collect_in_list_field_column_");
127 let metadata = test_region_metadata();
128 let mut builder = InvertedIndexApplierBuilder::new(
129 "test".to_string(),
130 PathType::Bare,
131 test_object_store(),
132 &metadata,
133 HashSet::from_iter([1, 2, 3]),
134 facotry,
135 );
136
137 let in_list = InList {
138 expr: Box::new(field_column()),
139 list: vec![string_lit("foo"), string_lit("bar")],
140 negated: false,
141 };
142
143 builder.collect_inlist(&in_list).unwrap();
144
145 let predicates = builder.output.get(&3).unwrap();
146 assert_eq!(predicates.len(), 1);
147 assert_eq!(
148 predicates[0],
149 Predicate::InList(InListPredicate {
150 list: BTreeSet::from_iter([encoded_string("foo"), encoded_string("bar")])
151 })
152 );
153 }
154
155 #[test]
156 fn test_collect_in_list_type_mismatch() {
157 let (_d, facotry) =
158 PuffinManagerFactory::new_for_test_block("test_collect_in_list_type_mismatch_");
159 let metadata = test_region_metadata();
160 let mut builder = InvertedIndexApplierBuilder::new(
161 "test".to_string(),
162 PathType::Bare,
163 test_object_store(),
164 &metadata,
165 HashSet::from_iter([1, 2, 3]),
166 facotry,
167 );
168
169 let in_list = InList {
170 expr: Box::new(tag_column()),
171 list: vec![int64_lit(123), int64_lit(456)],
172 negated: false,
173 };
174
175 let res = builder.collect_inlist(&in_list);
176 assert!(matches!(res, Err(Error::Encode { .. })));
177 assert!(builder.output.is_empty());
178 }
179
180 #[test]
181 fn test_collect_in_list_nonexistent_column() {
182 let (_d, facotry) =
183 PuffinManagerFactory::new_for_test_block("test_collect_in_list_nonexistent_column_");
184
185 let metadata = test_region_metadata();
186 let mut builder = InvertedIndexApplierBuilder::new(
187 "test".to_string(),
188 PathType::Bare,
189 test_object_store(),
190 &metadata,
191 HashSet::from_iter([1, 2, 3]),
192 facotry,
193 );
194
195 let in_list = InList {
196 expr: Box::new(nonexistent_column()),
197 list: vec![string_lit("foo"), string_lit("bar")],
198 negated: false,
199 };
200
201 let res = builder.collect_inlist(&in_list);
202 assert!(matches!(res, Err(Error::ColumnNotFound { .. })));
203 assert!(builder.output.is_empty());
204 }
205}