Skip to main content

mindb/query/
mod.rs

1//! High level query handlers that compose the different index layers.
2#![allow(dead_code)]
3
4use std::collections::HashSet;
5
6use serde_json::Value;
7
8use crate::index::{
9    SequenceNumber, VersionPointer,
10    bloom::BloomFilter,
11    hot::HotIndex,
12    secondary::{FullTextSidecar, JsonTrieSidecar, RoaringBitmapSidecar, SlugMap, TimeFenceIndex},
13    sparse::SparseIndex,
14};
15
16/// MVCC snapshot descriptor passed to all query handlers.
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub struct MvccSnapshot {
19    pub sequence: SequenceNumber,
20}
21
22impl MvccSnapshot {
23    /// Creates a snapshot pinned at the provided sequence.
24    pub fn new(sequence: SequenceNumber) -> Self {
25        Self { sequence }
26    }
27
28    /// Snapshot that can observe every committed version.
29    pub fn open() -> Self {
30        Self {
31            sequence: SequenceNumber::MAX,
32        }
33    }
34}
35
36/// Shared view over the logical indexes.
37#[derive(Clone)]
38pub struct IndexRegistry {
39    pub hot: HotIndex,
40    pub slug: SlugMap,
41    pub time_fence: TimeFenceIndex,
42    pub roaring: RoaringBitmapSidecar,
43    pub json_trie: JsonTrieSidecar,
44    pub fts: FullTextSidecar,
45}
46
47impl IndexRegistry {
48    pub fn new(
49        hot: HotIndex,
50        slug: SlugMap,
51        time_fence: TimeFenceIndex,
52        roaring: RoaringBitmapSidecar,
53        json_trie: JsonTrieSidecar,
54        fts: FullTextSidecar,
55    ) -> Self {
56        Self {
57            hot,
58            slug,
59            time_fence,
60            roaring,
61            json_trie,
62            fts,
63        }
64    }
65}
66
67/// Segment level accelerators exposed to the query layer.
68pub struct SegmentAccelerators<'a> {
69    pub sparse: Option<&'a SparseIndex>,
70    pub bloom: Option<&'a BloomFilter>,
71}
72
73impl<'a> SegmentAccelerators<'a> {
74    pub fn empty() -> Self {
75        Self {
76            sparse: None,
77            bloom: None,
78        }
79    }
80}
81
82/// Composite filter request spanning bitmap tags and JSON predicates.
83pub struct FilterRequest<'a> {
84    pub bitmap_tags: &'a [String],
85    pub json_equals: &'a [(&'a str, &'a Value)],
86}
87
88/// Primary-key lookup that first consults the hot index and optionally falls back
89/// to segment-level accelerators.
90pub fn get(
91    registry: &IndexRegistry,
92    accelerators: SegmentAccelerators<'_>,
93    key: &[u8],
94    snapshot: MvccSnapshot,
95) -> Option<VersionPointer> {
96    if let Some(pointer) = registry.hot.get_latest(key, snapshot.sequence) {
97        return Some(pointer);
98    }
99
100    if let Some(filter) = accelerators.bloom {
101        if !filter.may_contain(key) {
102            return None;
103        }
104    }
105
106    accelerators
107        .sparse
108        .and_then(|sparse| sparse.locate(key, snapshot.sequence))
109}
110
111/// Slug lookup that resolves the slug to a key and obtains the visible pointer.
112pub fn get_by_slug(
113    registry: &IndexRegistry,
114    slug: &str,
115    snapshot: MvccSnapshot,
116) -> Option<(Vec<u8>, VersionPointer)> {
117    if let Some((key, pointer)) = registry.slug.get_latest(slug, snapshot.sequence) {
118        if let Some(latest) = registry.hot.get_latest(&key, snapshot.sequence) {
119            return Some((key, latest));
120        }
121        return Some((key, pointer));
122    }
123
124    None
125}
126
127/// Returns the keys whose timestamp falls within the inclusive range.
128pub fn scan_time(
129    registry: &IndexRegistry,
130    start: i64,
131    end: i64,
132    snapshot: MvccSnapshot,
133) -> Vec<(Vec<u8>, VersionPointer)> {
134    let mut results = registry
135        .time_fence
136        .scan_range(start, end, snapshot.sequence);
137
138    for (key, pointer) in results.iter_mut() {
139        if let Some(latest) = registry.hot.get_latest(key, snapshot.sequence) {
140            *pointer = latest;
141        }
142    }
143
144    results
145        .into_iter()
146        .filter(|(_, pointer)| pointer.is_visible_at(snapshot.sequence))
147        .collect()
148}
149
150/// Applies bitmap and JSON filters to produce a set of matching keys.
151pub fn filter(
152    registry: &IndexRegistry,
153    request: &FilterRequest<'_>,
154    snapshot: MvccSnapshot,
155) -> Vec<(Vec<u8>, VersionPointer)> {
156    let mut candidates: Option<HashSet<Vec<u8>>> = None;
157
158    if !request.bitmap_tags.is_empty() {
159        let keys = registry
160            .roaring
161            .intersect_keys(request.bitmap_tags, snapshot.sequence);
162        candidates = Some(keys.into_iter().collect());
163    }
164
165    for (path, value) in request.json_equals {
166        let keys = registry
167            .json_trie
168            .query_equals(path, value, snapshot.sequence);
169        let set: HashSet<Vec<u8>> = keys.into_iter().collect();
170        candidates = match candidates {
171            Some(existing) => Some(existing.intersection(&set).cloned().collect()),
172            None => Some(set),
173        };
174        if let Some(current) = &candidates {
175            if current.is_empty() {
176                return Vec::new();
177            }
178        }
179    }
180
181    let keys = match candidates {
182        Some(set) => set.into_iter().collect::<Vec<_>>(),
183        None => Vec::new(),
184    };
185
186    keys.into_iter()
187        .filter_map(|key| {
188            registry
189                .hot
190                .get_latest(&key, snapshot.sequence)
191                .filter(|pointer| pointer.is_visible_at(snapshot.sequence))
192                .map(|pointer| (key, pointer))
193        })
194        .collect()
195}
196
197/// Performs a full-text search across the registered documents.
198pub fn search(
199    registry: &IndexRegistry,
200    query: &str,
201    snapshot: MvccSnapshot,
202) -> Vec<(Vec<u8>, VersionPointer)> {
203    let terms: Vec<String> = query
204        .split_whitespace()
205        .map(|term| term.to_lowercase())
206        .collect();
207
208    if terms.is_empty() {
209        return Vec::new();
210    }
211
212    let keys = registry.fts.search(&terms, snapshot.sequence);
213
214    keys.into_iter()
215        .filter_map(|key| {
216            registry
217                .hot
218                .get_latest(&key, snapshot.sequence)
219                .filter(|pointer| pointer.is_visible_at(snapshot.sequence))
220                .map(|pointer| (key, pointer))
221        })
222        .collect()
223}
224
225/// Returns the keys that share the provided prefix ordered by their latest
226/// visible versions.
227pub fn scan_prefix(
228    registry: &IndexRegistry,
229    prefix: &[u8],
230    snapshot: MvccSnapshot,
231) -> Vec<(Vec<u8>, VersionPointer)> {
232    registry
233        .hot
234        .scan_prefix(prefix, snapshot.sequence)
235        .into_iter()
236        .filter(|(_, pointer)| pointer.is_visible_at(snapshot.sequence))
237        .collect()
238}