|
1 | 1 | // lib/src/database.rs |
| 2 | +// Corrected: 2025-07-02 - Final version ensuring correct trait usage and error handling. |
| 3 | +// Fixed models import path and conditional RocksDBStorage import. |
| 4 | + |
| 5 | +use std::sync::Arc; |
| 6 | +// Conditionally import RocksDBStorage |
| 7 | +#[cfg(feature = "with-rocksdb")] |
| 8 | +use crate::storage_engine::RocksDBStorage; |
| 9 | +use crate::storage_engine::{GraphStorageEngine, SledGraphStorage, StorageConfig, StorageEngineType, open_sled_db}; |
| 10 | +use models::{Vertex, Edge, Identifier}; // Corrected: Removed `crate::` prefix |
| 11 | +use models::errors::{GraphError, GraphResult}; // Use GraphResult directly |
| 12 | +use uuid::Uuid; |
| 13 | +use serde_json::Value; // For query results |
2 | 14 |
|
3 | | -use uuid::Uuid; // Uuid is used in the Transaction trait |
4 | | - |
5 | | -// Core graph model types |
6 | | -use models::{Edge, Identifier, Json, Vertex}; |
7 | | - |
8 | | -// More specific model types, assuming their locations within the 'models' crate |
9 | | -// Please adjust these paths if your 'models' crate has a different internal structure. |
10 | | -use models::bulk_insert::BulkInsertItem; |
11 | | -// FIX: Changed from models::edges::EdgeDirection to models::queries::EdgeDirection |
12 | | -use models::queries::EdgeDirection; // CORRECTED PATH |
13 | | -use models::properties::{EdgeProperties, NamedProperty, PropertyValue, VertexProperties}; |
14 | | -use models::queries::{Query, QueryOutputValue}; |
15 | | - |
16 | | -// Application-specific models, if they are used within database.rs or re-exported from here |
17 | | -use models::medical::{Login, User}; // Assuming these are in models::medical |
18 | | - |
19 | | -// Import your custom error type |
20 | | -use crate::errors::Result; |
21 | | - |
22 | | -// Define a type alias for a dynamic iterator, commonly used for trait objects |
23 | | -// and allowing different iterator implementations to be returned. |
24 | | -pub type DynIter<'a, T> = Box<dyn Iterator<Item = Result<T>> + 'a>; |
25 | | - |
26 | | - |
27 | | -/// Defines the core functionality required for a datastore. |
| 15 | +/// A graph database wrapper that provides a simplified interface for |
| 16 | +/// interacting with an underlying `GraphStorageEngine`. |
28 | 17 | /// |
29 | | -/// Any type implementing this trait can serve as the backend for the `Database`. |
30 | | -pub trait Datastore { |
31 | | - /// The associated transaction type for this datastore. |
32 | | - /// |
33 | | - /// This associated type allows the `Datastore` trait to define methods |
34 | | - /// that return a specific transaction type, which in turn implements |
35 | | - /// the `Transaction` trait. The `'a` lifetime parameter indicates that |
36 | | - /// the transaction's lifetime is tied to the lifetime of the `Datastore` |
37 | | - /// instance from which it was created. |
38 | | - type Transaction<'a> |
39 | | - where |
40 | | - Self: 'a; // Self must outlive 'a, or 'a is tied to Self's lifetime |
41 | | - |
42 | | - /// Begins a new transaction for this datastore. |
43 | | - /// |
44 | | - /// The returned transaction can be used for both read and write operations. |
45 | | - fn transaction(&'_ self) -> Self::Transaction<'_>; |
| 18 | +/// This struct manages the storage engine instance and provides convenient methods |
| 19 | +/// for graph operations. |
| 20 | +#[derive(Debug)] |
| 21 | +pub struct Database { |
| 22 | + storage_engine: Arc<dyn GraphStorageEngine>, |
46 | 23 | } |
47 | 24 |
|
| 25 | +impl Database { |
| 26 | + /// Creates a new database instance based on the provided storage configuration. |
| 27 | + /// |
| 28 | + /// This function initializes and starts the chosen storage engine (Sled or RocksDB). |
| 29 | + /// |
| 30 | + /// # Arguments |
| 31 | + /// * `config`: The configuration for the storage engine. |
| 32 | + /// |
| 33 | + /// # Returns |
| 34 | + /// A `GraphResult` indicating success or a `GraphError` if initialization fails. |
| 35 | + pub async fn new(config: StorageConfig) -> GraphResult<Self> { |
| 36 | + let storage_engine: Arc<dyn GraphStorageEngine> = match config.engine_type { |
| 37 | + StorageEngineType::Sled => { |
| 38 | + // Open Sled DB and create SledGraphStorage |
| 39 | + let db = open_sled_db(&config.data_path)?; |
| 40 | + Arc::new(SledGraphStorage::new(db)?) |
| 41 | + }, |
| 42 | + StorageEngineType::RocksDB => { |
| 43 | + // Conditionally compile RocksDB initialization if the feature is enabled |
| 44 | + #[cfg(feature = "with-rocksdb")] |
| 45 | + { |
| 46 | + Arc::new(RocksDBStorage::new(&config)?) |
| 47 | + } |
| 48 | + #[cfg(not(feature = "with-rocksdb"))] |
| 49 | + { |
| 50 | + return Err(GraphError::ConfigError( |
| 51 | + "RocksDB backend requested but 'with-rocksdb' feature is not enabled. \ |
| 52 | + Please enable it in lib/Cargo.toml".to_string() |
| 53 | + )); |
| 54 | + } |
| 55 | + }, |
| 56 | + // Add other storage types here as they are implemented (e.g., PostgreSQL, Redis) |
| 57 | + _ => return Err(GraphError::ConfigError( |
| 58 | + format!("Unsupported storage engine type: {:?}", config.engine_type) |
| 59 | + )), |
| 60 | + }; |
| 61 | + |
| 62 | + // Start the chosen storage engine |
| 63 | + storage_engine.start().await?; |
| 64 | + |
| 65 | + Ok(Database { storage_engine }) |
| 66 | + } |
48 | 67 |
|
49 | | -/// Defines the operations that can be performed within a database transaction. |
50 | | -/// |
51 | | -/// This trait outlines the read and write methods for interacting with the graph data. |
52 | | -pub trait Transaction<'a>: Sized { |
53 | | - // Graph Read Operations |
54 | | - |
55 | | - /// Returns the total number of vertices in the database. |
56 | | - fn vertex_count(&self) -> u64; |
57 | | - |
58 | | - /// Returns an iterator over all vertices in the database. |
59 | | - fn all_vertices(&'a self) -> Result<DynIter<'a, Vertex>>; |
60 | | - |
61 | | - /// Returns an iterator over vertices starting from a given offset UUID. |
62 | | - /// Useful for pagination or resuming iteration. |
63 | | - fn range_vertices(&'a self, offset: Uuid) -> Result<DynIter<'a, Vertex>>; |
64 | | - |
65 | | - /// Returns an iterator over specific vertices identified by their UUIDs. |
66 | | - fn specific_vertices(&'a self, ids: Vec<Uuid>) -> Result<DynIter<'a, Vertex>>; |
67 | | - |
68 | | - /// Returns an optional iterator of vertex UUIDs that have a property with the given name. |
69 | | - /// Returns `None` if the property is not indexed. |
70 | | - fn vertex_ids_with_property(&'a self, name: Identifier) -> Result<Option<DynIter<'a, Uuid>>>; |
71 | | - |
72 | | - /// Returns an optional iterator of vertex UUIDs that have a property with the given name |
73 | | - /// and a specific JSON value. Returns `None` if the property is not indexed. |
74 | | - fn vertex_ids_with_property_value(&'a self, name: Identifier, value: &Json) -> Result<Option<DynIter<'a, Uuid>>>; |
75 | | - |
76 | | - /// Returns the total number of edges in the database. |
77 | | - fn edge_count(&self) -> u64; |
78 | | - |
79 | | - /// Returns an iterator over all edges in the database. |
80 | | - fn all_edges(&'a self) -> Result<DynIter<'a, Edge>>; |
81 | | - |
82 | | - /// Returns an iterator over edges starting from a given offset Edge. |
83 | | - /// Useful for pagination or resuming iteration. |
84 | | - fn range_edges(&'a self, offset: Edge) -> Result<DynIter<'a, Edge>>; |
85 | | - |
86 | | - /// Returns an iterator over reversed edges (inbound to outbound) starting from a given offset Edge. |
87 | | - fn range_reversed_edges(&'a self, offset: Edge) -> Result<DynIter<'a, Edge>>; |
88 | | - |
89 | | - /// Returns an iterator over specific edges. |
90 | | - fn specific_edges(&'a self, edges: Vec<Edge>) -> Result<DynIter<'a, Edge>>; |
91 | | - |
92 | | - /// Returns an optional iterator of edges that have a property with the given name. |
93 | | - /// Returns `None` if the property is not indexed. |
94 | | - fn edges_with_property(&'a self, name: Identifier) -> Result<Option<DynIter<'a, Edge>>>; |
95 | | - |
96 | | - /// Returns an optional iterator of edges that have a property with the given name |
97 | | - /// and a specific JSON value. Returns `None` if the property is not indexed. |
98 | | - fn edges_with_property_value(&'a self, name: Identifier, value: &Json) -> Result<Option<DynIter<'a, Edge>>>; |
99 | | - |
100 | | - /// Retrieves the value of a specific property for a given vertex. |
101 | | - fn vertex_property(&self, vertex: &Vertex, name: Identifier) -> Result<Option<Json>>; |
102 | | - |
103 | | - /// Returns an iterator over all properties (name and value) for a given vertex. |
104 | | - fn all_vertex_properties_for_vertex(&'a self, vertex: &Vertex) -> Result<DynIter<'a, (Identifier, Json)>>; |
105 | | - |
106 | | - /// Retrieves the value of a specific property for a given edge. |
107 | | - fn edge_property(&self, edge: &Edge, name: Identifier) -> Result<Option<Json>>; |
108 | | - |
109 | | - /// Returns an iterator over all properties (name and value) for a given edge. |
110 | | - fn all_edge_properties_for_edge(&'a self, edge: &Edge) -> Result<DynIter<'a, (Identifier, Json)>>; |
111 | | - |
112 | | - // Graph Write Operations |
113 | | - |
114 | | - /// Deletes a collection of vertices and all their associated edges and properties. |
115 | | - fn delete_vertices(&mut self, vertices: Vec<Vertex>) -> Result<()>; |
| 68 | + /// Returns a reference to the underlying graph storage engine. |
| 69 | + pub fn storage(&self) -> &Arc<dyn GraphStorageEngine> { |
| 70 | + &self.storage_engine |
| 71 | + } |
116 | 72 |
|
117 | | - /// Deletes a collection of edges and their associated properties. |
118 | | - fn delete_edges(&mut self, edges: Vec<Edge>) -> Result<()>; |
| 73 | + // --- Proxy methods to GraphStorageEngine for convenience --- |
119 | 74 |
|
120 | | - /// Deletes specific properties from vertices. |
121 | | - fn delete_vertex_properties(&mut self, props: Vec<(Uuid, Identifier)>) -> Result<()>; |
| 75 | + /// Creates a new vertex in the database. |
| 76 | + pub async fn create_vertex(&self, vertex: Vertex) -> GraphResult<()> { |
| 77 | + self.storage_engine.as_ref().create_vertex(vertex).await |
| 78 | + } |
122 | 79 |
|
123 | | - /// Deletes specific properties from edges. |
124 | | - fn delete_edge_properties(&mut self, props: Vec<(Edge, Identifier)>) -> Result<()>; |
| 80 | + /// Retrieves a vertex by its ID. |
| 81 | + pub async fn get_vertex(&self, id: &Uuid) -> GraphResult<Option<Vertex>> { |
| 82 | + self.storage_engine.as_ref().get_vertex(id).await |
| 83 | + } |
125 | 84 |
|
126 | | - /// Synchronizes the datastore's internal state with its persistent storage. |
127 | | - /// This might involve flushing buffers or performing compaction. |
128 | | - fn sync(&self) -> Result<()>; |
| 85 | + /// Updates an existing vertex in the database. |
| 86 | + pub async fn update_vertex(&self, vertex: Vertex) -> GraphResult<()> { |
| 87 | + self.storage_engine.as_ref().update_vertex(vertex).await |
| 88 | + } |
129 | 89 |
|
130 | | - /// Creates a new vertex in the database. Returns `true` if created, `false` if it already existed. |
131 | | - fn create_vertex(&mut self, vertex: &Vertex) -> Result<bool>; |
| 90 | + /// Deletes a vertex by its ID. |
| 91 | + pub async fn delete_vertex(&self, id: &Uuid) -> GraphResult<()> { |
| 92 | + self.storage_engine.as_ref().delete_vertex(id).await |
| 93 | + } |
132 | 94 |
|
133 | | - /// Creates a new edge in the database. Returns `true` if created, `false` if it already existed |
134 | | - /// or if the incident vertices do not exist. |
135 | | - fn create_edge(&mut self, edge: &Edge) -> Result<bool>; |
| 95 | + /// Retrieves all vertices from the database. |
| 96 | + pub async fn get_all_vertices(&self) -> GraphResult<Vec<Vertex>> { |
| 97 | + self.storage_engine.as_ref().get_all_vertices().await |
| 98 | + } |
136 | 99 |
|
137 | | - /// Performs a bulk insert of various graph items (vertices, edges, properties). |
138 | | - /// This method is typically optimized for performance by batching operations. |
139 | | - fn bulk_insert(&mut self, items: Vec<BulkInsertItem>) -> Result<()>; |
| 100 | + /// Creates a new edge in the database. |
| 101 | + pub async fn create_edge(&self, edge: Edge) -> GraphResult<()> { |
| 102 | + self.storage_engine.as_ref().create_edge(edge).await |
| 103 | + } |
140 | 104 |
|
141 | | - /// Indexes a property by its name. After indexing, queries on this property |
142 | | - /// will be more efficient. This operation may take time for existing data. |
143 | | - fn index_property(&mut self, name: Identifier) -> Result<()>; |
| 105 | + /// Retrieves an edge by its composite key (outbound ID, type, inbound ID). |
| 106 | + pub async fn get_edge(&self, outbound_id: &Uuid, edge_type: &Identifier, inbound_id: &Uuid) -> GraphResult<Option<Edge>> { |
| 107 | + self.storage_engine.as_ref().get_edge(outbound_id, edge_type, inbound_id).await |
| 108 | + } |
144 | 109 |
|
145 | | - /// Sets (creates or updates) a specific property for a collection of vertices. |
146 | | - fn set_vertex_properties(&mut self, vertices: Vec<Uuid>, name: Identifier, value: &Json) -> Result<()>; |
| 110 | + /// Updates an existing edge in the database. |
| 111 | + pub async fn update_edge(&self, edge: Edge) -> GraphResult<()> { |
| 112 | + self.storage_engine.as_ref().update_edge(edge).await |
| 113 | + } |
147 | 114 |
|
148 | | - /// Sets (creates or updates) a specific property for a collection of edges. |
149 | | - fn set_edge_properties(&mut self, edges: Vec<Edge>, name: Identifier, value: &Json) -> Result<()>; |
150 | | -} |
| 115 | + /// Deletes an edge by its composite key. |
| 116 | + pub async fn delete_edge(&self, outbound_id: &Uuid, edge_type: &Identifier, inbound_id: &Uuid) -> GraphResult<()> { |
| 117 | + self.storage_engine.as_ref().delete_edge(outbound_id, edge_type, inbound_id).await |
| 118 | + } |
151 | 119 |
|
| 120 | + /// Retrieves all edges from the database. |
| 121 | + pub async fn get_all_edges(&self) -> GraphResult<Vec<Edge>> { |
| 122 | + self.storage_engine.as_ref().get_all_edges().await |
| 123 | + } |
152 | 124 |
|
153 | | -/// A graph database wrapper that provides a simplified interface for |
154 | | -/// interacting with an underlying `Datastore`. |
155 | | -/// |
156 | | -/// This struct manages the datastore instance and provides convenient methods |
157 | | -/// to obtain read and write transactions. |
158 | | -#[derive(Debug)] |
159 | | -pub struct Database<D: Datastore> { |
160 | | - datastore: D, |
161 | | -} |
| 125 | + /// Executes a generic query string against the graph storage engine. |
| 126 | + /// The interpretation of the query string depends on the underlying engine. |
| 127 | + pub async fn query(&self, query_string: &str) -> GraphResult<Value> { |
| 128 | + self.storage_engine.as_ref().query(query_string).await |
| 129 | + } |
162 | 130 |
|
163 | | -impl<D: Datastore> Database<D> { |
164 | | - /// Creates a new database instance. |
165 | | - /// |
166 | | - /// # Arguments |
167 | | - /// * `datastore`: The underlying datastore implementation that will store the data. |
168 | | - pub fn new(datastore: D) -> Self { |
169 | | - Self { datastore } |
| 131 | + /// Returns the type of the underlying storage engine. |
| 132 | + pub fn get_type(&self) -> &'static str { |
| 133 | + self.storage_engine.as_ref().get_type() |
170 | 134 | } |
171 | 135 |
|
172 | | - /// Gets a read transaction for performing read operations on the database. |
173 | | - /// |
174 | | - /// The transaction's lifetime is tied to the `Database` instance. |
175 | | - pub fn read_txn(&self) -> D::Transaction<'_> { |
176 | | - self.datastore.transaction() |
| 136 | + /// Checks if the underlying storage engine is running. |
| 137 | + pub fn is_running(&self) -> bool { |
| 138 | + self.storage_engine.as_ref().is_running() |
177 | 139 | } |
178 | 140 |
|
179 | | - /// Gets a write transaction for performing write operations on the database. |
180 | | - /// |
181 | | - /// The transaction is typically committed automatically when it goes out of scope (dropped), |
182 | | - /// or it might provide explicit commit/rollback methods depending on the `Transaction` |
183 | | - /// trait implementation. |
184 | | - pub fn write_txn(&mut self) -> D::Transaction<'_> { |
185 | | - self.datastore.transaction() |
| 141 | + /// Stops the underlying storage engine gracefully. |
| 142 | + pub async fn stop(&self) -> GraphResult<()> { |
| 143 | + self.storage_engine.as_ref().stop().await |
186 | 144 | } |
187 | 145 | } |
| 146 | + |
0 commit comments