Skip to main content

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((target, data_type)) = self.expr_to_index_target(&between.expr)? else {
29            return Ok(());
30        };
31        let Some(low) = Self::nonnull_lit(&between.low) else {
32            return Ok(());
33        };
34        let Some(high) = Self::nonnull_lit(&between.high) else {
35            return Ok(());
36        };
37
38        let predicate = Predicate::Range(RangePredicate {
39            range: Range {
40                lower: Some(Bound {
41                    inclusive: true,
42                    value: Self::encode_lit(low, data_type.clone())?,
43                }),
44                upper: Some(Bound {
45                    inclusive: true,
46                    value: Self::encode_lit(high, data_type)?,
47                }),
48            },
49        });
50
51        self.add_predicate(target, predicate);
52        Ok(())
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use std::collections::HashSet;
59
60    use store_api::region_request::PathType;
61
62    use super::*;
63    use crate::error::Error;
64    use crate::sst::index::inverted_index::applier::builder::tests::{
65        encoded_string, field_column, int64_lit, nonexistent_column, string_lit, tag_column,
66        test_object_store, test_region_metadata,
67    };
68    use crate::sst::index::puffin_manager::PuffinManagerFactory;
69
70    #[test]
71    fn test_collect_between_basic() {
72        let (_d, factory) = PuffinManagerFactory::new_for_test_block("test_collect_between_basic_");
73        let metadata = test_region_metadata();
74        let mut builder = InvertedIndexApplierBuilder::new(
75            "test".to_string(),
76            PathType::Bare,
77            test_object_store(),
78            &metadata,
79            HashSet::from_iter([1, 2, 3]),
80            factory,
81        );
82
83        let between = Between {
84            negated: false,
85            expr: Box::new(tag_column()),
86            low: Box::new(string_lit("abc")),
87            high: Box::new(string_lit("def")),
88        };
89
90        builder.collect_between(&between).unwrap();
91
92        let predicates = builder
93            .output
94            .get(&index::target::IndexTarget::ColumnId(1))
95            .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, factory) =
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            factory,
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, factory) =
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            factory,
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
163            .output
164            .get(&index::target::IndexTarget::ColumnId(3))
165            .unwrap();
166        assert_eq!(predicates.len(), 1);
167        assert_eq!(
168            predicates[0],
169            Predicate::Range(RangePredicate {
170                range: Range {
171                    lower: Some(Bound {
172                        inclusive: true,
173                        value: encoded_string("abc"),
174                    }),
175                    upper: Some(Bound {
176                        inclusive: true,
177                        value: encoded_string("def"),
178                    }),
179                }
180            })
181        );
182    }
183
184    #[test]
185    fn test_collect_between_type_mismatch() {
186        let (_d, factory) =
187            PuffinManagerFactory::new_for_test_block("test_collect_between_type_mismatch_");
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 between = Between {
199            negated: false,
200            expr: Box::new(tag_column()),
201            low: Box::new(int64_lit(123)),
202            high: Box::new(int64_lit(456)),
203        };
204
205        let res = builder.collect_between(&between);
206        assert!(matches!(res, Err(Error::Encode { .. })));
207        assert!(builder.output.is_empty());
208    }
209
210    #[test]
211    fn test_collect_between_nonexistent_column() {
212        let (_d, factory) =
213            PuffinManagerFactory::new_for_test_block("test_collect_between_nonexistent_column_");
214        let metadata = test_region_metadata();
215        let mut builder = InvertedIndexApplierBuilder::new(
216            "test".to_string(),
217            PathType::Bare,
218            test_object_store(),
219            &metadata,
220            HashSet::from_iter([1, 2, 3]),
221            factory,
222        );
223
224        let between = Between {
225            negated: false,
226            expr: Box::new(nonexistent_column()),
227            low: Box::new(string_lit("abc")),
228            high: Box::new(string_lit("def")),
229        };
230
231        let res = builder.collect_between(&between);
232        assert!(matches!(res, Err(Error::ColumnNotFound { .. })));
233        assert!(builder.output.is_empty());
234    }
235}