mongreldb_core/index/
learned_range.rs1use super::pgm::{LearnedIndex, PgmIndex};
14use std::collections::HashSet;
15
16#[inline]
20fn i64_key(v: i64) -> u64 {
21 (v as u64) ^ (1u64 << 63)
22}
23
24#[inline]
28fn f64_key(v: f64) -> u64 {
29 let bits = v.to_bits();
30 if bits & (1u64 << 63) != 0 {
31 !bits
32 } else {
33 bits ^ (1u64 << 63)
34 }
35}
36
37#[derive(Debug, Clone)]
38pub struct ColumnLearnedRange {
39 keys: Vec<u64>, row_ids: Vec<u64>, pgm: PgmIndex,
42}
43
44impl ColumnLearnedRange {
45 pub fn build_i64(pairs: &[(i64, u64)]) -> Self {
47 let mut sorted: Vec<(u64, u64)> = pairs.iter().map(|(v, r)| (i64_key(*v), *r)).collect();
48 sorted.sort_unstable_by_key(|(k, _)| *k);
49 let keys: Vec<u64> = sorted.iter().map(|(k, _)| *k).collect();
50 let row_ids: Vec<u64> = sorted.iter().map(|(_, r)| *r).collect();
51 let points: Vec<(u64, usize)> = keys.iter().enumerate().map(|(i, k)| (*k, i)).collect();
52 let pgm = if points.is_empty() {
53 PgmIndex::build(&[], 16)
54 } else {
55 PgmIndex::build(&points, 16)
56 };
57 Self { keys, row_ids, pgm }
58 }
59
60 fn lower_bound(&self, key: u64) -> usize {
64 let n = self.keys.len();
65 if n == 0 {
66 return 0;
67 }
68 let (lo, hi) = self.pgm.predict(key);
69 let lo = lo.min(n);
70 let hi = hi.min(n).max(lo);
71 let mut idx = lo + self.keys[lo..hi].partition_point(|k| *k < key);
72 if idx > 0 && self.keys[idx - 1] >= key {
74 let mut step = 1usize;
75 while idx >= step && self.keys[idx - step] >= key {
76 step <<= 1;
77 }
78 let start = idx - step.min(idx);
79 idx = start + self.keys[start..idx].partition_point(|k| *k < key);
80 }
81 if idx < n && self.keys[idx] < key {
83 let mut step = 1usize;
84 while idx + step <= n && self.keys[(idx + step).min(n) - 1] < key {
85 step <<= 1;
86 }
87 let end = (idx + step).min(n);
88 idx = idx + self.keys[idx..end].partition_point(|k| *k < key);
89 }
90 idx
91 }
92
93 fn upper_bound(&self, key: u64) -> usize {
95 let n = self.keys.len();
96 if n == 0 {
97 return 0;
98 }
99 let (lo, hi) = self.pgm.predict(key);
100 let lo = lo.min(n);
101 let hi = hi.min(n).max(lo);
102 let mut idx = lo + self.keys[lo..hi].partition_point(|k| *k <= key);
103 if idx > 0 && self.keys[idx - 1] > key {
104 let mut step = 1usize;
105 while idx >= step && self.keys[idx - step] > key {
106 step <<= 1;
107 }
108 let start = idx - step.min(idx);
109 idx = start + self.keys[start..idx].partition_point(|k| *k <= key);
110 }
111 if idx < n && self.keys[idx] <= key {
112 let mut step = 1usize;
113 while idx + step <= n && self.keys[(idx + step).min(n) - 1] <= key {
114 step <<= 1;
115 }
116 let end = (idx + step).min(n);
117 idx = idx + self.keys[idx..end].partition_point(|k| *k <= key);
118 }
119 idx
120 }
121
122 pub fn range(&self, lo: i64, hi: i64) -> HashSet<u64> {
124 if hi < lo || self.keys.is_empty() {
125 return HashSet::new();
126 }
127 let start = self.lower_bound(i64_key(lo));
128 let end = self.upper_bound(i64_key(hi));
129 self.row_ids[start..end].iter().copied().collect()
130 }
131
132 pub fn build_f64(pairs: &[(f64, u64)]) -> Self {
134 let mut sorted: Vec<(u64, u64)> = pairs.iter().map(|(v, r)| (f64_key(*v), *r)).collect();
135 sorted.sort_unstable_by_key(|(k, _)| *k);
136 let keys: Vec<u64> = sorted.iter().map(|(k, _)| *k).collect();
137 let row_ids: Vec<u64> = sorted.iter().map(|(_, r)| *r).collect();
138 let points: Vec<(u64, usize)> = keys.iter().enumerate().map(|(i, k)| (*k, i)).collect();
139 let pgm = if points.is_empty() {
140 PgmIndex::build(&[], 16)
141 } else {
142 PgmIndex::build(&points, 16)
143 };
144 Self { keys, row_ids, pgm }
145 }
146
147 pub fn range_f64(
150 &self,
151 lo: f64,
152 lo_inclusive: bool,
153 hi: f64,
154 hi_inclusive: bool,
155 ) -> HashSet<u64> {
156 if self.keys.is_empty() {
157 return HashSet::new();
158 }
159 let lo_key = f64_key(lo);
164 let hi_key = f64_key(hi);
165 let (start, end) = if hi < lo {
166 return HashSet::new();
167 } else if lo_inclusive && hi_inclusive {
168 (self.lower_bound(lo_key), self.upper_bound(hi_key))
169 } else if lo_inclusive {
170 (self.lower_bound(lo_key), self.lower_bound(hi_key))
172 } else if hi_inclusive {
173 (self.upper_bound(lo_key), self.upper_bound(hi_key))
175 } else {
176 (self.upper_bound(lo_key), self.lower_bound(hi_key))
177 };
178 self.row_ids[start..end].iter().copied().collect()
179 }
180
181 pub fn snapshot(&self) -> ColumnLearnedRangeSnapshot {
183 ColumnLearnedRangeSnapshot {
184 keys: self.keys.clone(),
185 row_ids: self.row_ids.clone(),
186 pgm: self.pgm.clone(),
187 }
188 }
189
190 pub fn from_snapshot(snap: ColumnLearnedRangeSnapshot) -> Self {
192 Self {
193 keys: snap.keys,
194 row_ids: snap.row_ids,
195 pgm: snap.pgm,
196 }
197 }
198}
199
200#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
202pub struct ColumnLearnedRangeSnapshot {
203 pub keys: Vec<u64>,
204 pub row_ids: Vec<u64>,
205 pub pgm: PgmIndex,
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn range_returns_exact_slice() {
214 let pairs = vec![
216 (100i64, 0u64),
217 (-5, 1),
218 (50, 2),
219 (100, 3),
220 (1000, 4),
221 (-5, 5),
222 ];
223 let idx = ColumnLearnedRange::build_i64(&pairs);
224 let r = idx.range(50, 100);
226 assert_eq!(r, [2, 0, 3].into_iter().collect::<HashSet<_>>());
227 let all = idx.range(i64::MIN, i64::MAX);
228 assert_eq!(all.len(), 6);
229 let none = idx.range(200, 300);
230 assert!(none.is_empty());
231 let negs = idx.range(i64::MIN, -5);
232 assert_eq!(negs, [1, 5].into_iter().collect::<HashSet<_>>());
233 }
234}