1use 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>;