Skip to main content

reddb_server/
index.rs

1//! Index layer contracts and in-memory index catalogue.
2//!
3//! This is a thin, stable abstraction layer above the concrete index
4//! implementations already present in `storage`.
5
6use std::collections::BTreeMap;
7use std::time::{SystemTime, UNIX_EPOCH};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub enum IndexKind {
11    BTree,
12    Hash,
13    Bitmap,
14    Spatial,
15    VectorHnsw,
16    VectorInverted,
17    GraphAdjacency,
18    FullText,
19    DocumentPathValue,
20    HybridSearch,
21}
22
23impl IndexKind {
24    pub const fn as_str(self) -> &'static str {
25        match self {
26            Self::BTree => "btree",
27            Self::Hash => "hash",
28            Self::Bitmap => "bitmap",
29            Self::Spatial => "spatial.rtree",
30            Self::VectorHnsw => "vector.hnsw",
31            Self::VectorInverted => "vector.inverted",
32            Self::GraphAdjacency => "graph.adjacency",
33            Self::FullText => "text.fulltext",
34            Self::DocumentPathValue => "document.pathvalue",
35            Self::HybridSearch => "search.hybrid",
36        }
37    }
38}
39
40#[derive(Debug, Clone)]
41pub struct IndexConfig {
42    pub name: String,
43    pub kind: IndexKind,
44    pub enabled: bool,
45    pub warmup: bool,
46    pub updated_at_ms: u128,
47}
48
49impl IndexConfig {
50    pub fn new(name: impl Into<String>, kind: IndexKind) -> Self {
51        Self {
52            name: name.into(),
53            kind,
54            enabled: true,
55            warmup: false,
56            updated_at_ms: SystemTime::now()
57                .duration_since(UNIX_EPOCH)
58                .unwrap_or_default()
59                .as_millis(),
60        }
61    }
62
63    pub fn disabled(mut self) -> Self {
64        self.enabled = false;
65        self
66    }
67
68    pub fn with_warmup(mut self, warmup: bool) -> Self {
69        self.warmup = warmup;
70        self
71    }
72}
73
74#[derive(Debug, Clone, Default)]
75pub struct IndexStats {
76    pub memory_bytes: u64,
77    pub entries: usize,
78    pub queries: u64,
79    pub builds: u64,
80    pub errors: u64,
81}
82
83#[derive(Debug, Clone)]
84pub struct IndexMetric {
85    pub name: String,
86    pub kind: IndexKind,
87    pub enabled: bool,
88    pub last_refresh_ms: Option<u128>,
89    pub stats: IndexStats,
90}
91
92#[derive(Debug, Default)]
93pub struct IndexCatalog {
94    metrics: BTreeMap<String, IndexMetric>,
95}
96
97impl IndexCatalog {
98    pub fn register(&mut self, cfg: IndexConfig) {
99        let metric = IndexMetric {
100            name: cfg.name.clone(),
101            kind: cfg.kind,
102            enabled: cfg.enabled,
103            last_refresh_ms: Some(cfg.updated_at_ms),
104            stats: IndexStats::default(),
105        };
106        self.metrics.insert(cfg.name, metric);
107    }
108
109    pub fn snapshot(&self) -> Vec<IndexMetric> {
110        self.metrics.values().cloned().collect()
111    }
112
113    pub fn enabled(&self) -> Vec<String> {
114        self.metrics
115            .iter()
116            .filter_map(|(name, metric)| metric.enabled.then_some(name.clone()))
117            .collect()
118    }
119
120    pub fn disable(&mut self, name: &str) -> bool {
121        if let Some(metric) = self.metrics.get_mut(name) {
122            metric.enabled = false;
123        }
124
125        self.metrics.contains_key(name)
126    }
127
128    pub fn touch(&mut self, name: &str) {
129        if let Some(metric) = self.metrics.get_mut(name) {
130            metric.last_refresh_ms = Some(
131                SystemTime::now()
132                    .duration_since(UNIX_EPOCH)
133                    .unwrap_or_default()
134                    .as_millis(),
135            );
136        }
137    }
138
139    pub fn register_default_vector_graph(table: bool, graph: bool) -> Self {
140        let mut catalog = Self::default();
141        if table {
142            catalog.register(IndexConfig::new("metadata-btree", IndexKind::BTree));
143        }
144        if graph {
145            catalog.register(IndexConfig::new(
146                "graph-adjacency",
147                IndexKind::GraphAdjacency,
148            ));
149        }
150        catalog.register(IndexConfig::new("vector-hnsw", IndexKind::VectorHnsw).with_warmup(true));
151        catalog.register(IndexConfig::new(
152            "vector-inverted",
153            IndexKind::VectorInverted,
154        ));
155        catalog
156    }
157}
158
159pub trait IndexRuntime {
160    fn describe(&self) -> Vec<IndexMetric>;
161    fn apply_metric(&mut self, metric: IndexMetric);
162}
163
164impl IndexRuntime for IndexCatalog {
165    fn describe(&self) -> Vec<IndexMetric> {
166        self.snapshot()
167    }
168
169    fn apply_metric(&mut self, metric: IndexMetric) {
170        self.metrics.insert(metric.name.clone(), metric);
171    }
172}
173
174pub type IndexCatalogSnapshot = Vec<IndexMetric>;