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