Skip to main content

uqa_storage/key_value/
storage_backend.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Persistent-storage facade that assembles the key/value adapters.
8
9use super::{
10    btree_index, Analyzer, Arc, BTreeMap, DocId, DocumentStore, InvertedIndex, KeyValueCatalog,
11    KeyValueDocumentStore, KeyValueHNSWIndex, KeyValueIVFIndex, KeyValueInvertedIndex,
12    KeyValueStore, KeyValueVectorIndex, PersistentStorageBackend, PersistentStorageIdentity,
13    StorageBackendResult, Value, VectorIndex, VectorIndexOpenMode, VectorIndexSpec,
14};
15use crate::{CatalogFacade, PersistentStorageSession};
16
17/// Persistent storage factory implemented over [`KeyValueStore`].
18#[derive(Clone)]
19pub struct KeyValueStorageBackend {
20    store: Arc<dyn KeyValueStore>,
21}
22
23impl KeyValueStorageBackend {
24    pub fn new(store: Arc<dyn KeyValueStore>) -> Self {
25        Self { store }
26    }
27
28    pub fn store(&self) -> Arc<dyn KeyValueStore> {
29        Arc::clone(&self.store)
30    }
31}
32
33impl PersistentStorageBackend for KeyValueStorageBackend {
34    fn storage_identity(&self) -> StorageBackendResult<Option<PersistentStorageIdentity>> {
35        self.store.storage_identity()
36    }
37
38    fn open_session(&self) -> StorageBackendResult<PersistentStorageSession> {
39        let store = self.store.open_session()?;
40        let catalog: Arc<dyn CatalogFacade> = Arc::new(KeyValueCatalog::new(Arc::clone(&store)));
41        let backend: Arc<dyn PersistentStorageBackend> = Arc::new(Self::new(store));
42        Ok(PersistentStorageSession::new(catalog, backend))
43    }
44
45    fn document_store(&self, table: &str) -> Box<dyn DocumentStore> {
46        Box::new(KeyValueDocumentStore::new(Arc::clone(&self.store), table))
47    }
48
49    fn migrate_document_storage(&self) -> StorageBackendResult<()> {
50        KeyValueDocumentStore::migrate_legacy_storage(self.store.as_ref())
51    }
52
53    fn inverted_index(&self, table: &str, analyzer: Analyzer) -> Box<dyn InvertedIndex> {
54        Box::new(KeyValueInvertedIndex::new(
55            Arc::clone(&self.store),
56            table,
57            analyzer,
58        ))
59    }
60
61    fn migrate_inverted_index_storage(&self) -> StorageBackendResult<()> {
62        KeyValueInvertedIndex::migrate_legacy_storage(self.store.as_ref())
63    }
64
65    fn vector_index(
66        &self,
67        table: &str,
68        field: &str,
69        dimensions: u32,
70        spec: VectorIndexSpec,
71        mode: VectorIndexOpenMode,
72    ) -> StorageBackendResult<Box<dyn VectorIndex>> {
73        match spec {
74            VectorIndexSpec::BruteForce => Ok(Box::new(KeyValueVectorIndex::new(
75                Arc::clone(&self.store),
76                table,
77                field,
78                dimensions,
79            ))),
80            VectorIndexSpec::IVF(params) => match mode {
81                VectorIndexOpenMode::Create => Ok(Box::new(KeyValueIVFIndex::create(
82                    Arc::clone(&self.store),
83                    table,
84                    field,
85                    dimensions,
86                    params,
87                )?)),
88                VectorIndexOpenMode::Restore => Ok(Box::new(KeyValueIVFIndex::restore(
89                    Arc::clone(&self.store),
90                    table,
91                    field,
92                    dimensions,
93                    params,
94                )?)),
95            },
96            VectorIndexSpec::HNSW(params) => match mode {
97                VectorIndexOpenMode::Create => Ok(Box::new(KeyValueHNSWIndex::create(
98                    Arc::clone(&self.store),
99                    table,
100                    field,
101                    dimensions,
102                    params,
103                )?)),
104                VectorIndexOpenMode::Restore => Ok(Box::new(KeyValueHNSWIndex::restore(
105                    Arc::clone(&self.store),
106                    table,
107                    field,
108                    dimensions,
109                    params,
110                )?)),
111            },
112        }
113    }
114
115    fn drop_vector_index_metadata(&self, table: &str, field: &str) -> StorageBackendResult<()> {
116        KeyValueIVFIndex::drop_metadata(self.store.as_ref(), table, field)
117    }
118
119    fn persists_btree_indexes(&self) -> bool {
120        true
121    }
122
123    fn load_btree_index(
124        &self,
125        table: &str,
126        field: &crate::ValueIndexKey,
127    ) -> StorageBackendResult<Option<Vec<(DocId, Value)>>> {
128        btree_index::load(self.store.as_ref(), table, field)
129    }
130
131    fn btree_index_fields(&self, table: &str) -> StorageBackendResult<Vec<crate::ValueIndexKey>> {
132        btree_index::fields(self.store.as_ref(), table)
133    }
134
135    fn replace_btree_index(
136        &self,
137        table: &str,
138        field: &crate::ValueIndexKey,
139        values: &[(DocId, Value)],
140    ) -> StorageBackendResult<()> {
141        btree_index::replace(self.store.as_ref(), table, field, values)
142    }
143
144    fn replace_btree_indexes(
145        &self,
146        table: &str,
147        indexes: &[(&crate::ValueIndexKey, &[(DocId, Value)])],
148    ) -> StorageBackendResult<()> {
149        btree_index::replace_many(self.store.as_ref(), table, indexes)
150    }
151
152    fn apply_btree_index_write(
153        &self,
154        table: &str,
155        doc_id: DocId,
156        values: Option<&BTreeMap<crate::ValueIndexKey, Value>>,
157    ) -> StorageBackendResult<()> {
158        btree_index::apply_write(self.store.as_ref(), table, doc_id, values)
159    }
160
161    fn drop_btree_index(
162        &self,
163        table: &str,
164        field: &crate::ValueIndexKey,
165    ) -> StorageBackendResult<()> {
166        btree_index::drop_index(self.store.as_ref(), table, field)
167    }
168
169    fn clear_btree_indexes(&self, table: &str) -> StorageBackendResult<()> {
170        btree_index::clear_entries(self.store.as_ref(), table)
171    }
172
173    fn begin_transaction(&self) -> StorageBackendResult<()> {
174        self.store.begin_transaction()
175    }
176
177    fn begin_read_transaction(&self) -> StorageBackendResult<()> {
178        self.store.begin_read_transaction()
179    }
180
181    fn begin_upgradeable_transaction(&self) -> StorageBackendResult<()> {
182        self.store.begin_upgradeable_transaction()
183    }
184
185    fn in_transaction(&self) -> bool {
186        self.store.in_transaction()
187    }
188
189    fn transaction_has_written(&self) -> StorageBackendResult<bool> {
190        self.store.transaction_has_written()
191    }
192
193    fn change_version(&self) -> StorageBackendResult<Option<u64>> {
194        self.store.change_version()
195    }
196
197    fn change_version_monitor_is_nonblocking(&self) -> StorageBackendResult<bool> {
198        self.store.change_version_monitor_is_nonblocking()
199    }
200
201    fn pin_transaction_snapshot(&self) -> StorageBackendResult<()> {
202        self.store.pin_transaction_snapshot()
203    }
204
205    fn commit_transaction(&self) -> StorageBackendResult<()> {
206        self.store.commit_transaction()
207    }
208
209    fn rollback_transaction(&self) -> StorageBackendResult<()> {
210        self.store.rollback_transaction()
211    }
212
213    fn savepoint(&self, id: crate::StorageSavepointId) -> StorageBackendResult<()> {
214        self.store.savepoint(&id.backend_name())
215    }
216
217    fn release_savepoint(&self, id: crate::StorageSavepointId) -> StorageBackendResult<()> {
218        self.store.release_savepoint(&id.backend_name())
219    }
220
221    fn rollback_to_savepoint(&self, id: crate::StorageSavepointId) -> StorageBackendResult<()> {
222        self.store.rollback_to_savepoint(&id.backend_name())
223    }
224}