Skip to main content

velesdb_core/database/
graph_ops.rs

1//! Graph collection creation and retrieval operations.
2
3use crate::collection::GraphCollection;
4use crate::{CollectionType, DistanceMetric, Result};
5
6use super::Database;
7
8impl Database {
9    /// Creates a new graph collection.
10    ///
11    /// # Errors
12    ///
13    /// Returns an error if a collection with the same name already exists.
14    #[allow(clippy::needless_pass_by_value)] // Public API — changing to &ref would be breaking.
15    pub fn create_graph_collection(
16        &self,
17        name: &str,
18        schema: crate::collection::GraphSchema,
19    ) -> Result<()> {
20        self.ensure_collection_name_available(name)?;
21        let path = self.data_dir.join(name);
22        let coll =
23            GraphCollection::create(path, name, None, DistanceMetric::Cosine, schema.clone())?;
24        self.register_graph_collection(name, &coll, None, DistanceMetric::Cosine, &schema);
25        Ok(())
26    }
27
28    /// Creates a new graph collection with node embeddings.
29    ///
30    /// Unlike [`create_graph_collection`](Self::create_graph_collection), this
31    /// variant configures a vector dimension and distance metric so that nodes
32    /// can store embeddings and support similarity search.
33    ///
34    /// # Errors
35    ///
36    /// Returns an error if a collection with the same name already exists.
37    #[allow(clippy::needless_pass_by_value)] // Public API — changing to &ref would be breaking.
38    pub fn create_graph_collection_with_embeddings(
39        &self,
40        name: &str,
41        schema: crate::collection::GraphSchema,
42        dimension: usize,
43        metric: DistanceMetric,
44    ) -> Result<()> {
45        self.ensure_collection_name_available(name)?;
46        self.enforce_vector_dimension_limit(dimension)?;
47        let path = self.data_dir.join(name);
48        let coll = GraphCollection::create(path, name, Some(dimension), metric, schema.clone())?;
49        self.register_graph_collection(name, &coll, Some(dimension), metric, &schema);
50        Ok(())
51    }
52
53    /// Internal helper for `create_collection_typed` with `Graph` variant.
54    pub(super) fn create_graph_collection_from_type(
55        &self,
56        name: &str,
57        dimension: Option<usize>,
58        metric: DistanceMetric,
59        schema: &crate::collection::GraphSchema,
60    ) -> Result<()> {
61        self.ensure_collection_name_available(name)?;
62        if let Some(d) = dimension {
63            self.enforce_vector_dimension_limit(d)?;
64        }
65        let path = self.data_dir.join(name);
66        let coll = GraphCollection::create(path, name, dimension, metric, schema.clone())?;
67        self.register_graph_collection(name, &coll, dimension, metric, schema);
68        Ok(())
69    }
70
71    /// Registers a graph collection in the typed registry,
72    /// notifies the observer, and bumps the schema version.
73    fn register_graph_collection(
74        &self,
75        name: &str,
76        coll: &GraphCollection,
77        dimension: Option<usize>,
78        metric: DistanceMetric,
79        schema: &crate::collection::GraphSchema,
80    ) {
81        // Parity item E: thread the live LimitsConfig caps into the collection.
82        self.push_runtime_limits(&coll.inner);
83
84        self.graph_colls
85            .write()
86            .insert(name.to_string(), coll.clone());
87
88        if let Some(ref obs) = self.observer {
89            let kind = CollectionType::Graph {
90                dimension,
91                metric,
92                schema: schema.clone(),
93            };
94            obs.on_collection_created(name, &kind);
95        }
96
97        self.schema_version
98            .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
99    }
100
101    /// Returns a `GraphCollection` by name.
102    ///
103    /// Checks the typed registry first.  Falls back to opening from disk if the
104    /// collection was not registered in-memory (e.g. after a restart or when
105    /// the collection was auto-created by a graph handler).  The instance is
106    /// cached into the registry so subsequent calls are free.
107    ///
108    /// Returns `None` if the collection does not exist on disk.
109    #[must_use]
110    pub fn get_graph_collection(&self, name: &str) -> Option<GraphCollection> {
111        if let Some(c) = self.graph_colls.read().get(name).cloned() {
112            return Some(c);
113        }
114        self.open_graph_collection_from_disk(name)
115    }
116
117    /// Disk fallback for `get_graph_collection`.
118    fn open_graph_collection_from_disk(&self, name: &str) -> Option<GraphCollection> {
119        let cfg = self.read_collection_config(name)?;
120        cfg.graph_schema.as_ref()?;
121        let coll = GraphCollection::open(self.data_dir.join(name)).ok()?;
122        // Parity item E: re-push runtime limits on disk-open (not persisted).
123        self.push_runtime_limits(&coll.inner);
124        self.graph_colls
125            .write()
126            .insert(name.to_string(), coll.clone());
127        Some(coll)
128    }
129}