1#[derive(Debug, Clone)]
11pub enum Condition {
12 Pk(Vec<u8>),
14 BitmapEq { column_id: u16, value: Vec<u8> },
16 BitmapIn {
21 column_id: u16,
22 values: Vec<Vec<u8>>,
23 },
24 Ann {
26 column_id: u16,
27 query: Vec<f32>,
28 k: usize,
29 },
30 FmContains { column_id: u16, pattern: Vec<u8> },
32 FmContainsAll {
37 column_id: u16,
38 patterns: Vec<Vec<u8>>,
39 },
40 Range { column_id: u16, lo: i64, hi: i64 },
44 RangeF64 {
47 column_id: u16,
48 lo: f64,
49 lo_inclusive: bool,
50 hi: f64,
51 hi_inclusive: bool,
52 },
53 SparseMatch {
56 column_id: u16,
57 query: Vec<(u32, f32)>,
58 k: usize,
59 },
60 MinHashSimilar {
64 column_id: u16,
65 query: Vec<u64>,
66 k: usize,
67 },
68 IsNull { column_id: u16 },
72 IsNotNull { column_id: u16 },
75}
76
77#[derive(Debug, Default, Clone)]
79pub struct Query {
80 pub conditions: Vec<Condition>,
81}
82
83impl Query {
84 pub fn new() -> Self {
85 Self::default()
86 }
87 pub fn and(mut self, c: Condition) -> Self {
88 self.conditions.push(c);
89 self
90 }
91 pub fn pk(key: Vec<u8>) -> Self {
92 Self::new().and(Condition::Pk(key))
93 }
94}
95
96pub fn canonical_query_key(
105 conditions: &[Condition],
106 projection: Option<&[u16]>,
107 epoch: u64,
108) -> u64 {
109 let fold = |seed: u64, b: u64| -> u64 { seed.wrapping_mul(0x9E3779B97F4A7C15).wrapping_add(b) };
110 let mut acc = fold(0xA5A5_A5A5_A5A5_A5A5, epoch);
111 let mut digests: Vec<u64> = conditions.iter().map(hash_condition).collect();
113 digests.sort_unstable();
114 let n = digests.len() as u64;
115 acc = fold(acc, n);
116 for d in digests {
117 acc = fold(acc, d);
118 }
119 match projection {
122 Some(p) => {
123 let mut p = p.to_vec();
124 p.sort_unstable();
125 p.dedup();
126 acc = fold(acc, 0x5E);
127 acc = fold(acc, p.len() as u64);
128 for id in p {
129 acc = fold(acc, id as u64);
130 }
131 }
132 None => {
133 acc = fold(acc, 0xA5);
134 }
135 }
136 acc
137}
138
139fn hash_condition(c: &Condition) -> u64 {
143 use std::hash::{Hash, Hasher};
144 let mut h = std::collections::hash_map::DefaultHasher::new();
145 match c {
146 Condition::Pk(k) => {
147 0u8.hash(&mut h);
148 k.hash(&mut h);
149 }
150 Condition::BitmapEq { column_id, value } => {
151 1u8.hash(&mut h);
152 column_id.hash(&mut h);
153 value.hash(&mut h);
154 }
155 Condition::BitmapIn { column_id, values } => {
156 2u8.hash(&mut h);
157 column_id.hash(&mut h);
158 let mut v: Vec<&Vec<u8>> = values.iter().collect();
159 v.sort();
160 v.dedup();
161 v.len().hash(&mut h);
162 for b in v {
163 b.hash(&mut h);
164 }
165 }
166 Condition::Ann {
167 column_id,
168 query,
169 k,
170 } => {
171 3u8.hash(&mut h);
172 column_id.hash(&mut h);
173 k.hash(&mut h);
174 for f in query {
175 f.to_bits().hash(&mut h);
176 }
177 }
178 Condition::FmContains { column_id, pattern } => {
179 4u8.hash(&mut h);
180 column_id.hash(&mut h);
181 pattern.hash(&mut h);
182 }
183 Condition::FmContainsAll {
184 column_id,
185 patterns,
186 } => {
187 10u8.hash(&mut h);
188 column_id.hash(&mut h);
189 let mut sorted: Vec<&[u8]> = patterns.iter().map(|p| p.as_slice()).collect();
190 sorted.sort();
191 sorted.len().hash(&mut h);
192 for p in sorted {
193 p.hash(&mut h);
194 }
195 }
196 Condition::Range { column_id, lo, hi } => {
197 5u8.hash(&mut h);
198 column_id.hash(&mut h);
199 lo.hash(&mut h);
200 hi.hash(&mut h);
201 }
202 Condition::RangeF64 {
203 column_id,
204 lo,
205 lo_inclusive,
206 hi,
207 hi_inclusive,
208 } => {
209 6u8.hash(&mut h);
210 column_id.hash(&mut h);
211 lo.to_bits().hash(&mut h);
212 lo_inclusive.hash(&mut h);
213 hi.to_bits().hash(&mut h);
214 hi_inclusive.hash(&mut h);
215 }
216 Condition::SparseMatch {
217 column_id,
218 query,
219 k,
220 } => {
221 7u8.hash(&mut h);
222 column_id.hash(&mut h);
223 k.hash(&mut h);
224 let mut q: Vec<(u32, u32)> = query.iter().map(|(t, w)| (*t, w.to_bits())).collect();
225 q.sort_by_key(|(t, _)| *t);
226 for (t, wb) in q {
227 t.hash(&mut h);
228 wb.hash(&mut h);
229 }
230 }
231 Condition::MinHashSimilar {
232 column_id,
233 query,
234 k,
235 } => {
236 10u8.hash(&mut h);
237 column_id.hash(&mut h);
238 k.hash(&mut h);
239 let mut q = query.clone();
240 q.sort_unstable();
241 for t in q {
242 t.hash(&mut h);
243 }
244 }
245 Condition::IsNull { column_id } => {
246 8u8.hash(&mut h);
247 column_id.hash(&mut h);
248 }
249 Condition::IsNotNull { column_id } => {
250 9u8.hash(&mut h);
251 column_id.hash(&mut h);
252 }
253 }
254 h.finish()
255}
256
257pub fn condition_columns(conditions: &[Condition]) -> Vec<u16> {
262 let mut cols: Vec<u16> = conditions
263 .iter()
264 .filter_map(|c| match c {
265 Condition::Pk(_) => None,
266 Condition::BitmapEq { column_id, .. }
267 | Condition::BitmapIn { column_id, .. }
268 | Condition::Ann { column_id, .. }
269 | Condition::FmContains { column_id, .. }
270 | Condition::FmContainsAll { column_id, .. }
271 | Condition::Range { column_id, .. }
272 | Condition::RangeF64 { column_id, .. }
273 | Condition::SparseMatch { column_id, .. }
274 | Condition::MinHashSimilar { column_id, .. }
275 | Condition::IsNull { column_id }
276 | Condition::IsNotNull { column_id } => Some(*column_id),
277 })
278 .collect();
279 cols.sort_unstable();
280 cols.dedup();
281 cols
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287
288 #[test]
289 fn builder_chains() {
290 let q = Query::pk(b"k".to_vec()).and(Condition::Range {
291 column_id: 1,
292 lo: 0,
293 hi: 10,
294 });
295 assert_eq!(q.conditions.len(), 2);
296 }
297
298 #[test]
302 fn canonical_key_is_order_independent() {
303 let e = 7u64;
304 let a = Query::new()
305 .and(Condition::Range {
306 column_id: 1,
307 lo: 0,
308 hi: 10,
309 })
310 .and(Condition::BitmapEq {
311 column_id: 2,
312 value: b"x".to_vec(),
313 });
314 let b = Query::new()
315 .and(Condition::BitmapEq {
316 column_id: 2,
317 value: b"x".to_vec(),
318 })
319 .and(Condition::Range {
320 column_id: 1,
321 lo: 0,
322 hi: 10,
323 });
324 assert_eq!(
325 canonical_query_key(&a.conditions, None, e),
326 canonical_query_key(&b.conditions, None, e),
327 "condition order must not affect the key"
328 );
329
330 let ordered = Condition::BitmapIn {
332 column_id: 3,
333 values: vec![b"a".to_vec(), b"b".to_vec(), b"c".to_vec()],
334 };
335 let shuffled = Condition::BitmapIn {
336 column_id: 3,
337 values: vec![b"c".to_vec(), b"a".to_vec(), b"a".to_vec(), b"b".to_vec()],
338 };
339 assert_eq!(
340 canonical_query_key(std::slice::from_ref(&ordered), None, e),
341 canonical_query_key(&[shuffled], None, e),
342 "BitmapIn values must dedup+sort"
343 );
344
345 assert_ne!(
347 canonical_query_key(&a.conditions, None, e),
348 canonical_query_key(&a.conditions, None, e + 1),
349 "epoch must fold into the key"
350 );
351
352 let proj = vec![1u16, 2];
354 assert_ne!(
355 canonical_query_key(&a.conditions, None, e),
356 canonical_query_key(&a.conditions, Some(&proj), e),
357 "None projection must differ from an explicit projection"
358 );
359 let proj_rev = vec![2u16, 1];
361 assert_eq!(
362 canonical_query_key(&a.conditions, Some(&proj), e),
363 canonical_query_key(&a.conditions, Some(&proj_rev), e),
364 );
365 }
366}