Skip to main content

velesdb_core/database/
collection_ops.rs

1//! Collection CRUD dispatcher: create, delete, list, get, and diagnostics.
2//!
3//! Type-specific operations are in sibling modules:
4//! - [`vector_ops`] — vector collection create/get
5//! - [`graph_ops`] — graph collection create/get
6//! - [`metadata_ops`] — metadata-only collection create/get
7
8#[allow(deprecated)]
9use crate::{Collection, CollectionType, DistanceMetric, Error, Result, StorageMode};
10
11use super::Database;
12
13// Almost every method below reads/writes `self.collections` (HashMap<String, Collection>),
14// so `#[allow(deprecated)]` is applied at impl-block level rather than per-method.
15#[allow(deprecated)]
16impl Database {
17    /// Ensures a collection name is free in memory and on disk.
18    ///
19    /// This prevents re-creating over a skipped/corrupted on-disk collection
20    /// that was not loaded into registries.
21    pub(super) fn ensure_collection_name_available(&self, name: &str) -> Result<()> {
22        let exists_in_registry = self.collections.read().contains_key(name)
23            || self.vector_colls.read().contains_key(name)
24            || self.graph_colls.read().contains_key(name)
25            || self.metadata_colls.read().contains_key(name);
26        if exists_in_registry {
27            return Err(Error::CollectionExists(name.to_string()));
28        }
29
30        let collection_path = self.data_dir.join(name);
31        if collection_path.exists() {
32            return Err(Error::CollectionExists(name.to_string()));
33        }
34
35        Ok(())
36    }
37
38    /// Creates a new collection with the specified parameters.
39    ///
40    /// # Arguments
41    ///
42    /// * `name` - Unique name for the collection
43    /// * `dimension` - Vector dimension (e.g., 768 for many embedding models)
44    /// * `metric` - Distance metric to use for similarity calculations
45    ///
46    /// # Errors
47    ///
48    /// - Returns `Error::CollectionExists` if a collection with the same name already exists.
49    /// - Returns an error if the directory cannot be created or storage initialization fails.
50    ///
51    /// # Examples
52    ///
53    /// ```rust,no_run
54    /// # use velesdb_core::{Database, DistanceMetric};
55    /// let db = Database::open("./data")?;
56    /// db.create_collection("documents", 768, DistanceMetric::Cosine)?;
57    /// # Ok::<(), velesdb_core::Error>(())
58    /// ```
59    pub fn create_collection(
60        &self,
61        name: &str,
62        dimension: usize,
63        metric: DistanceMetric,
64    ) -> Result<()> {
65        self.create_collection_with_options(name, dimension, metric, StorageMode::default())
66    }
67
68    /// Creates a new collection with custom storage options.
69    ///
70    /// # Errors
71    ///
72    /// Returns an error if a collection with the same name already exists.
73    pub fn create_collection_with_options(
74        &self,
75        name: &str,
76        dimension: usize,
77        metric: DistanceMetric,
78        storage_mode: StorageMode,
79    ) -> Result<()> {
80        self.create_vector_collection_with_options(name, dimension, metric, storage_mode)
81    }
82
83    /// Gets a reference to a collection by name.
84    ///
85    /// # Returns
86    ///
87    /// Returns `None` if the collection does not exist.
88    #[deprecated(
89        since = "2.0.0",
90        note = "Use get_vector_collection(), get_graph_collection(), or get_metadata_collection()"
91    )]
92    pub fn get_collection(&self, name: &str) -> Option<Collection> {
93        self.collections.read().get(name).cloned()
94    }
95
96    /// Returns the write generation for a named collection, if it exists.
97    #[must_use]
98    pub fn collection_write_generation(&self, name: &str) -> Option<u64> {
99        self.collections
100            .read()
101            .get(name)
102            .map(crate::Collection::write_generation)
103    }
104
105    /// Lists all collection names in the database.
106    ///
107    /// Includes collections created via any typed API (vector, graph, metadata).
108    pub fn list_collections(&self) -> Vec<String> {
109        // BUG-7: acquire all locks together for a consistent point-in-time snapshot.
110        let collections = self.collections.read();
111        let vector_colls = self.vector_colls.read();
112        let graph_colls = self.graph_colls.read();
113        let metadata_colls = self.metadata_colls.read();
114
115        let mut names: std::collections::HashSet<String> = collections.keys().cloned().collect();
116        for k in vector_colls.keys() {
117            names.insert(k.clone());
118        }
119        for k in graph_colls.keys() {
120            names.insert(k.clone());
121        }
122        for k in metadata_colls.keys() {
123            names.insert(k.clone());
124        }
125        let mut result: Vec<String> = names.into_iter().collect();
126        result.sort();
127        result
128    }
129
130    /// Deletes a collection by name.
131    ///
132    /// # Errors
133    ///
134    /// Returns an error if the collection does not exist in any registry.
135    pub fn delete_collection(&self, name: &str) -> Result<()> {
136        let exists = self.collections.read().contains_key(name)
137            || self.vector_colls.read().contains_key(name)
138            || self.graph_colls.read().contains_key(name)
139            || self.metadata_colls.read().contains_key(name);
140
141        if !exists {
142            return Err(Error::CollectionNotFound(name.to_string()));
143        }
144
145        let collection_path = self.data_dir.join(name);
146        if collection_path.exists() {
147            std::fs::remove_dir_all(&collection_path)?;
148        }
149
150        self.collections.write().remove(name);
151        self.vector_colls.write().remove(name);
152        self.graph_colls.write().remove(name);
153        self.metadata_colls.write().remove(name);
154        self.collection_stats.write().remove(name);
155
156        if let Some(ref obs) = self.observer {
157            obs.on_collection_deleted(name);
158        }
159
160        self.schema_version
161            .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
162
163        Ok(())
164    }
165
166    /// Creates a new collection with a specific type (Vector, Graph, or `MetadataOnly`).
167    ///
168    /// # Errors
169    ///
170    /// Returns an error if a collection with the same name already exists.
171    pub fn create_collection_typed(
172        &self,
173        name: &str,
174        collection_type: &CollectionType,
175    ) -> Result<()> {
176        match collection_type {
177            CollectionType::Vector {
178                dimension,
179                metric,
180                storage_mode,
181            } => {
182                self.create_vector_collection_with_options(name, *dimension, *metric, *storage_mode)
183            }
184            CollectionType::MetadataOnly => self.create_metadata_collection(name),
185            CollectionType::Graph {
186                dimension,
187                metric,
188                schema,
189            } => self.create_graph_collection_from_type(name, *dimension, *metric, schema),
190        }
191    }
192
193    /// Reads and parses `config.json` from a collection directory.
194    ///
195    /// Returns `None` if the config file does not exist or cannot be parsed.
196    pub(super) fn read_collection_config(
197        &self,
198        name: &str,
199    ) -> Option<crate::collection::CollectionConfig> {
200        let path = self.data_dir.join(name);
201        let config_path = path.join("config.json");
202        if !config_path.exists() {
203            return None;
204        }
205        let data = std::fs::read_to_string(&config_path).ok()?;
206        serde_json::from_str(&data).ok()
207    }
208
209    /// Propagates updated query limits to all active collections.
210    pub fn update_guardrails(&self, limits: &crate::guardrails::QueryLimits) {
211        let collections = self.collections.read();
212        for collection in collections.values() {
213            collection.guard_rails.update_limits(limits);
214        }
215    }
216
217    /// Returns diagnostics for a named collection.
218    ///
219    /// # Errors
220    ///
221    /// Returns `Error::CollectionNotFound` if the collection does not exist.
222    pub fn collection_diagnostics(
223        &self,
224        name: &str,
225    ) -> Result<crate::collection::CollectionDiagnostics> {
226        if let Some(c) = self.get_vector_collection(name) {
227            return Ok(c.diagnostics());
228        }
229        if let Some(c) = self.get_graph_collection(name) {
230            return Ok(c.diagnostics());
231        }
232        if let Some(c) = self.get_metadata_collection(name) {
233            return Ok(c.diagnostics());
234        }
235        Err(Error::CollectionNotFound(name.to_string()))
236    }
237}