1#![warn(missing_docs)]
44#![warn(clippy::all)]
45#![warn(clippy::pedantic)]
46#![allow(clippy::module_name_repetitions)]
47
48pub mod collection;
49pub mod column_store;
50pub mod distance;
51pub mod error;
52pub mod filter;
53pub mod half_precision;
54pub mod index;
55pub mod metrics;
56pub mod perf_optimizations;
57pub mod point;
58pub mod quantization;
59pub mod simd;
60pub mod simd_avx512;
61pub mod simd_explicit;
62pub mod storage;
63pub mod velesql;
64
65pub use index::{HnswIndex, HnswParams, SearchQuality, VectorIndex};
66
67pub use collection::Collection;
68pub use distance::DistanceMetric;
69pub use error::{Error, Result};
70pub use filter::{Condition, Filter};
71pub use point::Point;
72pub use quantization::{
73 cosine_similarity_quantized, cosine_similarity_quantized_simd, dot_product_quantized,
74 dot_product_quantized_simd, euclidean_squared_quantized, euclidean_squared_quantized_simd,
75 BinaryQuantizedVector, QuantizedVector, StorageMode,
76};
77
78pub use column_store::{ColumnStore, ColumnType, ColumnValue, StringId, StringTable, TypedColumn};
79pub use metrics::{
80 average_metrics, compute_latency_percentiles, hit_rate, mean_average_precision, mrr, ndcg_at_k,
81 precision_at_k, recall_at_k, LatencyStats,
82};
83
84pub struct Database {
86 data_dir: std::path::PathBuf,
88 collections: parking_lot::RwLock<std::collections::HashMap<String, Collection>>,
90}
91
92impl Database {
93 pub fn open<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
103 let data_dir = path.as_ref().to_path_buf();
104 std::fs::create_dir_all(&data_dir)?;
105
106 Ok(Self {
107 data_dir,
108 collections: parking_lot::RwLock::new(std::collections::HashMap::new()),
109 })
110 }
111
112 pub fn create_collection(
124 &self,
125 name: &str,
126 dimension: usize,
127 metric: DistanceMetric,
128 ) -> Result<()> {
129 self.create_collection_with_options(name, dimension, metric, StorageMode::default())
130 }
131
132 pub fn create_collection_with_options(
145 &self,
146 name: &str,
147 dimension: usize,
148 metric: DistanceMetric,
149 storage_mode: StorageMode,
150 ) -> Result<()> {
151 let mut collections = self.collections.write();
152
153 if collections.contains_key(name) {
154 return Err(Error::CollectionExists(name.to_string()));
155 }
156
157 let collection_path = self.data_dir.join(name);
158 let collection =
159 Collection::create_with_options(collection_path, dimension, metric, storage_mode)?;
160 collections.insert(name.to_string(), collection);
161
162 Ok(())
163 }
164
165 pub fn get_collection(&self, name: &str) -> Option<Collection> {
175 self.collections.read().get(name).cloned()
176 }
177
178 pub fn list_collections(&self) -> Vec<String> {
180 self.collections.read().keys().cloned().collect()
181 }
182
183 pub fn delete_collection(&self, name: &str) -> Result<()> {
193 let mut collections = self.collections.write();
194
195 if collections.remove(name).is_none() {
196 return Err(Error::CollectionNotFound(name.to_string()));
197 }
198
199 let collection_path = self.data_dir.join(name);
200 if collection_path.exists() {
201 std::fs::remove_dir_all(collection_path)?;
202 }
203
204 Ok(())
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211 use tempfile::tempdir;
212
213 #[test]
214 fn test_database_open() {
215 let dir = tempdir().unwrap();
216 let db = Database::open(dir.path()).unwrap();
217 assert!(db.list_collections().is_empty());
218 }
219
220 #[test]
221 fn test_create_collection() {
222 let dir = tempdir().unwrap();
223 let db = Database::open(dir.path()).unwrap();
224
225 db.create_collection("test", 768, DistanceMetric::Cosine)
226 .unwrap();
227
228 assert_eq!(db.list_collections(), vec!["test"]);
229 }
230
231 #[test]
232 fn test_duplicate_collection_error() {
233 let dir = tempdir().unwrap();
234 let db = Database::open(dir.path()).unwrap();
235
236 db.create_collection("test", 768, DistanceMetric::Cosine)
237 .unwrap();
238
239 let result = db.create_collection("test", 768, DistanceMetric::Cosine);
240 assert!(result.is_err());
241 }
242
243 #[test]
244 fn test_get_collection() {
245 let dir = tempdir().unwrap();
246 let db = Database::open(dir.path()).unwrap();
247
248 assert!(db.get_collection("nonexistent").is_none());
250
251 db.create_collection("test", 768, DistanceMetric::Cosine)
253 .unwrap();
254
255 let collection = db.get_collection("test");
256 assert!(collection.is_some());
257
258 let config = collection.unwrap().config();
259 assert_eq!(config.dimension, 768);
260 assert_eq!(config.metric, DistanceMetric::Cosine);
261 }
262
263 #[test]
264 fn test_delete_collection() {
265 let dir = tempdir().unwrap();
266 let db = Database::open(dir.path()).unwrap();
267
268 db.create_collection("to_delete", 768, DistanceMetric::Cosine)
269 .unwrap();
270 assert_eq!(db.list_collections().len(), 1);
271
272 db.delete_collection("to_delete").unwrap();
274 assert!(db.list_collections().is_empty());
275 assert!(db.get_collection("to_delete").is_none());
276 }
277
278 #[test]
279 fn test_delete_nonexistent_collection() {
280 let dir = tempdir().unwrap();
281 let db = Database::open(dir.path()).unwrap();
282
283 let result = db.delete_collection("nonexistent");
284 assert!(result.is_err());
285 }
286
287 #[test]
288 fn test_multiple_collections() {
289 let dir = tempdir().unwrap();
290 let db = Database::open(dir.path()).unwrap();
291
292 db.create_collection("coll1", 128, DistanceMetric::Cosine)
293 .unwrap();
294 db.create_collection("coll2", 256, DistanceMetric::Euclidean)
295 .unwrap();
296 db.create_collection("coll3", 768, DistanceMetric::DotProduct)
297 .unwrap();
298
299 let collections = db.list_collections();
300 assert_eq!(collections.len(), 3);
301 assert!(collections.contains(&"coll1".to_string()));
302 assert!(collections.contains(&"coll2".to_string()));
303 assert!(collections.contains(&"coll3".to_string()));
304 }
305}