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        self.graph_colls
82            .write()
83            .insert(name.to_string(), coll.clone());
84
85        if let Some(ref obs) = self.observer {
86            let kind = CollectionType::Graph {
87                dimension,
88                metric,
89                schema: schema.clone(),
90            };
91            obs.on_collection_created(name, &kind);
92        }
93
94        self.schema_version
95            .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
96    }
97
98    /// Returns a `GraphCollection` by name.
99    ///
100    /// Checks the typed registry first.  Falls back to opening from disk if the
101    /// collection was not registered in-memory (e.g. after a restart or when
102    /// the collection was auto-created by a graph handler).  The instance is
103    /// cached into the registry so subsequent calls are free.
104    ///
105    /// Returns `None` if the collection does not exist on disk.
106    #[must_use]
107    pub fn get_graph_collection(&self, name: &str) -> Option<GraphCollection> {
108        if let Some(c) = self.graph_colls.read().get(name).cloned() {
109            return Some(c);
110        }
111        self.open_graph_collection_from_disk(name)
112    }
113
114    /// Disk fallback for `get_graph_collection`.
115    fn open_graph_collection_from_disk(&self, name: &str) -> Option<GraphCollection> {
116        let cfg = self.read_collection_config(name)?;
117        cfg.graph_schema.as_ref()?;
118        let coll = GraphCollection::open(self.data_dir.join(name)).ok()?;
119        self.graph_colls
120            .write()
121            .insert(name.to_string(), coll.clone());
122        Some(coll)
123    }
124}