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((target, data_type)) = self.expr_to_index_target(&inlist.expr)? else {
30 return Ok(());
31 };
32
33 let mut predicate = InListPredicate {
34 list: BTreeSet::new(),
35 };
36 for lit in &inlist.list {
37 let Some(lit) = Self::nonnull_lit(lit) else {
38 return Ok(());
39 };
40
41 predicate
42 .list
43 .insert(Self::encode_lit(lit, data_type.clone())?);
44 }
45
46 self.add_predicate(target, Predicate::InList(predicate));
47 Ok(())
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use std::collections::HashSet;
54
55 use store_api::region_request::PathType;
56
57 use super::*;
58 use crate::error::Error;
59 use crate::sst::index::inverted_index::applier::builder::tests::{
60 encoded_string, field_column, int64_lit, nonexistent_column, string_lit, tag_column,
61 test_object_store, test_region_metadata,
62 };
63 use crate::sst::index::puffin_manager::PuffinManagerFactory;
64
65 #[test]
66 fn test_collect_in_list_basic() {
67 let (_d, factory) = PuffinManagerFactory::new_for_test_block("test_collect_in_list_basic_");
68 let metadata = test_region_metadata();
69 let mut builder = InvertedIndexApplierBuilder::new(
70 "test".to_string(),
71 PathType::Bare,
72 test_object_store(),
73 &metadata,
74 HashSet::from_iter([1, 2, 3]),
75 factory,
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
87 .output
88 .get(&index::target::IndexTarget::ColumnId(1))
89 .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, factory) =
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 factory,
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, factory) =
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 factory,
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
146 .output
147 .get(&index::target::IndexTarget::ColumnId(3))
148 .unwrap();
149 assert_eq!(predicates.len(), 1);
150 assert_eq!(
151 predicates[0],
152 Predicate::InList(InListPredicate {
153 list: BTreeSet::from_iter([encoded_string("foo"), encoded_string("bar")])
154 })
155 );
156 }
157
158 #[test]
159 fn test_collect_in_list_type_mismatch() {
160 let (_d, factory) =
161 PuffinManagerFactory::new_for_test_block("test_collect_in_list_type_mismatch_");
162 let metadata = test_region_metadata();
163 let mut builder = InvertedIndexApplierBuilder::new(
164 "test".to_string(),
165 PathType::Bare,
166 test_object_store(),
167 &metadata,
168 HashSet::from_iter([1, 2, 3]),
169 factory,
170 );
171
172 let in_list = InList {
173 expr: Box::new(tag_column()),
174 list: vec![int64_lit(123), int64_lit(456)],
175 negated: false,
176 };
177
178 let res = builder.collect_inlist(&in_list);
179 assert!(matches!(res, Err(Error::Encode { .. })));
180 assert!(builder.output.is_empty());
181 }
182
183 #[test]
184 fn test_collect_in_list_nonexistent_column() {
185 let (_d, factory) =
186 PuffinManagerFactory::new_for_test_block("test_collect_in_list_nonexistent_column_");
187
188 let metadata = test_region_metadata();
189 let mut builder = InvertedIndexApplierBuilder::new(
190 "test".to_string(),
191 PathType::Bare,
192 test_object_store(),
193 &metadata,
194 HashSet::from_iter([1, 2, 3]),
195 factory,
196 );
197
198 let in_list = InList {
199 expr: Box::new(nonexistent_column()),
200 list: vec![string_lit("foo"), string_lit("bar")],
201 negated: false,
202 };
203
204 let res = builder.collect_inlist(&in_list);
205 assert!(matches!(res, Err(Error::ColumnNotFound { .. })));
206 assert!(builder.output.is_empty());
207 }
208}