mito2/sst/index/inverted_index/applier/builder/
between.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use datafusion_expr::Between;
16use index::inverted_index::search::predicate::{Bound, Predicate, Range, RangePredicate};
17
18use crate::error::Result;
19use crate::sst::index::inverted_index::applier::builder::InvertedIndexApplierBuilder;
20
21impl InvertedIndexApplierBuilder<'_> {
22    /// Collects a `BETWEEN` expression in the form of `column BETWEEN lit AND lit`.
23    pub(crate) fn collect_between(&mut self, between: &Between) -> Result<()> {
24        if between.negated {
25            return Ok(());
26        }
27
28        let Some(column_name) = Self::column_name(&between.expr) else {
29            return Ok(());
30        };
31        let Some((column_id, data_type)) = self.column_id_and_type(column_name)? else {
32            return Ok(());
33        };
34        let Some(low) = Self::nonnull_lit(&between.low) else {
35            return Ok(());
36        };
37        let Some(high) = Self::nonnull_lit(&between.high) else {
38            return Ok(());
39        };
40
41        let predicate = Predicate::Range(RangePredicate {
42            range: Range {
43                lower: Some(Bound {
44                    inclusive: true,
45                    value: Self::encode_lit(low, data_type.clone())?,
46                }),
47                upper: Some(Bound {
48                    inclusive: true,
49                    value: Self::encode_lit(high, data_type)?,
50                }),
51            },
52        });
53
54        self.add_predicate(column_id, predicate);
55        Ok(())
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use std::collections::HashSet;
62
63    use store_api::region_request::PathType;
64
65    use super::*;
66    use crate::error::Error;
67    use crate::sst::index::inverted_index::applier::builder::tests::{
68        encoded_string, field_column, int64_lit, nonexistent_column, string_lit, tag_column,
69        test_object_store, test_region_metadata,
70    };
71    use crate::sst::index::puffin_manager::PuffinManagerFactory;
72
73    #[test]
74    fn test_collect_between_basic() {
75        let (_d, facotry) = PuffinManagerFactory::new_for_test_block("test_collect_between_basic_");
76        let metadata = test_region_metadata();
77        let mut builder = InvertedIndexApplierBuilder::new(
78            "test".to_string(),
79            PathType::Bare,
80            test_object_store(),
81            &metadata,
82            HashSet::from_iter([1, 2, 3]),
83            facotry,
84        );
85
86        let between = Between {
87            negated: false,
88            expr: Box::new(tag_column()),
89            low: Box::new(string_lit("abc")),
90            high: Box::new(string_lit("def")),
91        };
92
93        builder.collect_between(&between).unwrap();
94
95        let predicates = builder.output.get(&1).unwrap();
96        assert_eq!(predicates.len(), 1);
97        assert_eq!(
98            predicates[0],
99            Predicate::Range(RangePredicate {
100                range: Range {
101                    lower: Some(Bound {
102                        inclusive: true,
103                        value: encoded_string("abc"),
104                    }),
105                    upper: Some(Bound {
106                        inclusive: true,
107                        value: encoded_string("def"),
108                    }),
109                }
110            })
111        );
112    }
113
114    #[test]
115    fn test_collect_between_negated() {
116        let (_d, facotry) =
117            PuffinManagerFactory::new_for_test_block("test_collect_between_negated_");
118        let metadata = test_region_metadata();
119        let mut builder = InvertedIndexApplierBuilder::new(
120            "test".to_string(),
121            PathType::Bare,
122            test_object_store(),
123            &metadata,
124            HashSet::from_iter([1, 2, 3]),
125            facotry,
126        );
127
128        let between = Between {
129            negated: true,
130            expr: Box::new(tag_column()),
131            low: Box::new(string_lit("abc")),
132            high: Box::new(string_lit("def")),
133        };
134
135        builder.collect_between(&between).unwrap();
136        assert!(builder.output.is_empty());
137    }
138
139    #[test]
140    fn test_collect_between_field_column() {
141        let (_d, facotry) =
142            PuffinManagerFactory::new_for_test_block("test_collect_between_field_column_");
143        let metadata = test_region_metadata();
144        let mut builder = InvertedIndexApplierBuilder::new(
145            "test".to_string(),
146            PathType::Bare,
147            test_object_store(),
148            &metadata,
149            HashSet::from_iter([1, 2, 3]),
150            facotry,
151        );
152
153        let between = Between {
154            negated: false,
155            expr: Box::new(field_column()),
156            low: Box::new(string_lit("abc")),
157            high: Box::new(string_lit("def")),
158        };
159
160        builder.collect_between(&between).unwrap();
161
162        let predicates = builder.output.get(&3).unwrap();
163        assert_eq!(predicates.len(), 1);
164        assert_eq!(
165            predicates[0],
166            Predicate::Range(RangePredicate {
167                range: Range {
168                    lower: Some(Bound {
169                        inclusive: true,
170                        value: encoded_string("abc"),
171                    }),
172                    upper: Some(Bound {
173                        inclusive: true,
174                        value: encoded_string("def"),
175                    }),
176                }
177            })
178        );
179    }
180
181    #[test]
182    fn test_collect_between_type_mismatch() {
183        let (_d, facotry) =
184            PuffinManagerFactory::new_for_test_block("test_collect_between_type_mismatch_");
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 between = Between {
196            negated: false,
197            expr: Box::new(tag_column()),
198            low: Box::new(int64_lit(123)),
199            high: Box::new(int64_lit(456)),
200        };
201
202        let res = builder.collect_between(&between);
203        assert!(matches!(res, Err(Error::Encode { .. })));
204        assert!(builder.output.is_empty());
205    }
206
207    #[test]
208    fn test_collect_between_nonexistent_column() {
209        let (_d, facotry) =
210            PuffinManagerFactory::new_for_test_block("test_collect_between_nonexistent_column_");
211        let metadata = test_region_metadata();
212        let mut builder = InvertedIndexApplierBuilder::new(
213            "test".to_string(),
214            PathType::Bare,
215            test_object_store(),
216            &metadata,
217            HashSet::from_iter([1, 2, 3]),
218            facotry,
219        );
220
221        let between = Between {
222            negated: false,
223            expr: Box::new(nonexistent_column()),
224            low: Box::new(string_lit("abc")),
225            high: Box::new(string_lit("def")),
226        };
227
228        let res = builder.collect_between(&between);
229        assert!(matches!(res, Err(Error::ColumnNotFound { .. })));
230        assert!(builder.output.is_empty());
231    }
232}