1mod error;
49
50use nervusdb_v2_api::GraphStore;
51use nervusdb_v2_storage::api::StorageSnapshot;
52use nervusdb_v2_storage::engine::GraphEngine;
53use nervusdb_v2_storage::snapshot::Snapshot;
54use std::collections::BTreeMap;
55use std::path::{Path, PathBuf};
56
57pub use error::{Error, Result};
58pub use nervusdb_v2_api::{
59 EdgeKey, ExternalId, GraphSnapshot, InternalNodeId, LabelId, PropertyValue, RelTypeId,
60};
61pub use nervusdb_v2_query as query;
62
63#[derive(Debug)]
78pub struct Db {
79 engine: GraphEngine,
80 ndb_path: PathBuf,
81 wal_path: PathBuf,
82}
83
84impl Db {
85 pub fn open(path: impl AsRef<Path>) -> Result<Self> {
93 let path = path.as_ref();
94 let (ndb_path, wal_path) = derive_paths(path);
95 Self::open_paths(ndb_path, wal_path)
96 }
97
98 pub fn open_paths(ndb_path: impl AsRef<Path>, wal_path: impl AsRef<Path>) -> Result<Self> {
106 let ndb_path = ndb_path.as_ref().to_path_buf();
107 let wal_path = wal_path.as_ref().to_path_buf();
108 let engine = GraphEngine::open(&ndb_path, &wal_path)?;
109 Ok(Self {
110 engine,
111 ndb_path,
112 wal_path,
113 })
114 }
115
116 #[inline]
118 pub fn ndb_path(&self) -> &Path {
119 &self.ndb_path
120 }
121
122 #[inline]
124 pub fn wal_path(&self) -> &Path {
125 &self.wal_path
126 }
127
128 pub fn begin_read(&self) -> ReadTxn {
134 ReadTxn {
135 snapshot: self.engine.begin_read(),
136 }
137 }
138
139 pub fn snapshot(&self) -> DbSnapshot {
144 DbSnapshot(self.engine.snapshot())
145 }
146
147 pub fn begin_write(&self) -> WriteTxn<'_> {
156 WriteTxn {
157 inner: self.engine.begin_write(),
158 }
159 }
160
161 pub fn compact(&self) -> Result<()> {
167 self.engine.compact().map_err(Error::from)
168 }
169
170 pub fn checkpoint(&self) -> Result<()> {
175 self.engine.compact().map_err(Error::from)
177 }
178
179 pub fn close(self) -> Result<()> {
183 self.engine.checkpoint_on_close().map_err(Error::from)?;
184 Ok(())
185 }
186
187 pub fn create_index(&self, label: &str, property: &str) -> Result<()> {
194 self.engine
195 .create_index(label, property)
196 .map_err(Error::from)
197 }
198}
199
200pub struct DbSnapshot(StorageSnapshot);
202
203impl GraphSnapshot for DbSnapshot {
204 type Neighbors<'a> = Box<dyn Iterator<Item = EdgeKey> + 'a>;
205
206 fn neighbors(&self, src: InternalNodeId, rel: Option<RelTypeId>) -> Self::Neighbors<'_> {
207 Box::new(self.0.neighbors(src, rel))
208 }
209
210 fn nodes(&self) -> Box<dyn Iterator<Item = InternalNodeId> + '_> {
211 self.0.nodes()
212 }
213
214 fn resolve_external(&self, iid: InternalNodeId) -> Option<ExternalId> {
215 self.0.resolve_external(iid)
216 }
217
218 fn node_label(&self, iid: InternalNodeId) -> Option<LabelId> {
219 self.0.node_label(iid)
220 }
221
222 fn is_tombstoned_node(&self, iid: InternalNodeId) -> bool {
223 self.0.is_tombstoned_node(iid)
224 }
225
226 fn node_property(&self, iid: InternalNodeId, key: &str) -> Option<PropertyValue> {
227 self.0.node_property(iid, key)
228 }
229
230 fn edge_property(&self, edge: EdgeKey, key: &str) -> Option<PropertyValue> {
231 self.0.edge_property(edge, key)
232 }
233
234 fn node_properties(&self, iid: InternalNodeId) -> Option<BTreeMap<String, PropertyValue>> {
235 self.0.node_properties(iid)
236 }
237
238 fn edge_properties(&self, edge: EdgeKey) -> Option<BTreeMap<String, PropertyValue>> {
239 self.0.edge_properties(edge)
240 }
241
242 fn resolve_label_id(&self, name: &str) -> Option<LabelId> {
243 self.0.resolve_label_id(name)
244 }
245
246 fn resolve_rel_type_id(&self, name: &str) -> Option<RelTypeId> {
247 self.0.resolve_rel_type_id(name)
248 }
249
250 fn resolve_label_name(&self, id: LabelId) -> Option<String> {
251 self.0.resolve_label_name(id)
252 }
253
254 fn resolve_rel_type_name(&self, id: RelTypeId) -> Option<String> {
255 self.0.resolve_rel_type_name(id)
256 }
257
258 fn lookup_index(
259 &self,
260 label: &str,
261 field: &str,
262 value: &PropertyValue,
263 ) -> Option<Vec<InternalNodeId>> {
264 self.0.lookup_index(label, field, value)
265 }
266
267 fn node_count(&self, label: Option<LabelId>) -> u64 {
268 self.0.node_count(label)
269 }
270
271 fn edge_count(&self, rel: Option<RelTypeId>) -> u64 {
272 self.0.edge_count(rel)
273 }
274}
275
276#[derive(Debug, Clone)]
280pub struct ReadTxn {
281 snapshot: Snapshot,
282}
283
284impl ReadTxn {
285 pub fn neighbors(
290 &self,
291 src: InternalNodeId,
292 rel: Option<RelTypeId>,
293 ) -> impl Iterator<Item = EdgeKey> + '_ {
294 self.snapshot.neighbors(src, rel).map(|k| EdgeKey {
295 src: k.src,
296 rel: k.rel,
297 dst: k.dst,
298 })
299 }
300}
301
302pub struct WriteTxn<'a> {
307 inner: nervusdb_v2_storage::engine::WriteTxn<'a>,
308}
309
310impl<'a> WriteTxn<'a> {
311 pub fn create_node(
315 &mut self,
316 external_id: ExternalId,
317 label_id: LabelId,
318 ) -> Result<InternalNodeId> {
319 self.inner
320 .create_node(external_id, label_id)
321 .map_err(Error::from)
322 }
323
324 pub fn get_or_create_label(&mut self, name: &str) -> Result<LabelId> {
326 self.inner.get_or_create_label(name).map_err(Error::from)
327 }
328
329 pub fn create_edge(&mut self, src: InternalNodeId, rel: RelTypeId, dst: InternalNodeId) {
333 self.inner.create_edge(src, rel, dst);
334 }
335
336 pub fn tombstone_node(&mut self, node: InternalNodeId) {
341 self.inner.tombstone_node(node);
342 }
343
344 pub fn tombstone_edge(&mut self, src: InternalNodeId, rel: RelTypeId, dst: InternalNodeId) {
348 self.inner.tombstone_edge(src, rel, dst);
349 }
350
351 pub fn set_node_property(
355 &mut self,
356 node: InternalNodeId,
357 key: String,
358 value: PropertyValue,
359 ) -> Result<()> {
360 let storage_value = convert_to_storage_property_value(value);
361 self.inner.set_node_property(node, key, storage_value);
362 Ok(())
363 }
364
365 pub fn set_edge_property(
369 &mut self,
370 src: InternalNodeId,
371 rel: RelTypeId,
372 dst: InternalNodeId,
373 key: String,
374 value: PropertyValue,
375 ) -> Result<()> {
376 let storage_value = convert_to_storage_property_value(value);
377 self.inner
378 .set_edge_property(src, rel, dst, key, storage_value);
379 Ok(())
380 }
381
382 pub fn remove_node_property(&mut self, node: InternalNodeId, key: &str) -> Result<()> {
386 self.inner.remove_node_property(node, key);
387 Ok(())
388 }
389
390 pub fn remove_edge_property(
394 &mut self,
395 src: InternalNodeId,
396 rel: RelTypeId,
397 dst: InternalNodeId,
398 key: &str,
399 ) -> Result<()> {
400 self.inner.remove_edge_property(src, rel, dst, key);
401 Ok(())
402 }
403
404 pub fn commit(self) -> Result<()> {
413 self.inner.commit().map_err(Error::from)
414 }
415}
416
417fn convert_to_storage_property_value(
418 v: PropertyValue,
419) -> nervusdb_v2_storage::property::PropertyValue {
420 use nervusdb_v2_storage::property::PropertyValue as StoragePropertyValue;
421 match v {
422 PropertyValue::Null => StoragePropertyValue::Null,
423 PropertyValue::Bool(b) => StoragePropertyValue::Bool(b),
424 PropertyValue::Int(i) => StoragePropertyValue::Int(i),
425 PropertyValue::Float(f) => StoragePropertyValue::Float(f),
426 PropertyValue::String(s) => StoragePropertyValue::String(s),
427 PropertyValue::DateTime(i) => StoragePropertyValue::DateTime(i),
428 PropertyValue::Blob(b) => StoragePropertyValue::Blob(b),
429 PropertyValue::List(l) => StoragePropertyValue::List(
430 l.into_iter()
431 .map(convert_to_storage_property_value)
432 .collect(),
433 ),
434 PropertyValue::Map(m) => StoragePropertyValue::Map(
435 m.into_iter()
436 .map(|(k, v)| (k, convert_to_storage_property_value(v)))
437 .collect(),
438 ),
439 }
440}
441
442fn derive_paths(path: &Path) -> (PathBuf, PathBuf) {
443 match path.extension().and_then(|e| e.to_str()) {
444 Some("ndb") => (path.to_path_buf(), path.with_extension("wal")),
445 Some("wal") => (path.with_extension("ndb"), path.to_path_buf()),
446 _ => (path.with_extension("ndb"), path.with_extension("wal")),
447 }
448}
449
450impl nervusdb_v2_query::WriteableGraph for WriteTxn<'_> {
453 fn create_node(
454 &mut self,
455 external_id: ExternalId,
456 label_id: LabelId,
457 ) -> nervusdb_v2_query::Result<InternalNodeId> {
458 self.inner
459 .create_node(external_id, label_id)
460 .map_err(|e| nervusdb_v2_query::Error::Other(e.to_string()))
461 }
462
463 fn create_edge(
464 &mut self,
465 src: InternalNodeId,
466 rel: RelTypeId,
467 dst: InternalNodeId,
468 ) -> nervusdb_v2_query::Result<()> {
469 self.inner.create_edge(src, rel, dst);
470 Ok(())
471 }
472
473 fn set_node_property(
474 &mut self,
475 node: InternalNodeId,
476 key: String,
477 value: nervusdb_v2_storage::property::PropertyValue,
478 ) -> nervusdb_v2_query::Result<()> {
479 self.inner.set_node_property(node, key, value);
481 Ok(())
482 }
483
484 fn set_edge_property(
485 &mut self,
486 src: InternalNodeId,
487 rel: RelTypeId,
488 dst: InternalNodeId,
489 key: String,
490 value: nervusdb_v2_storage::property::PropertyValue,
491 ) -> nervusdb_v2_query::Result<()> {
492 self.inner.set_edge_property(src, rel, dst, key, value);
493 Ok(())
494 }
495
496 fn tombstone_node(&mut self, node: InternalNodeId) -> nervusdb_v2_query::Result<()> {
497 self.inner.tombstone_node(node);
498 Ok(())
499 }
500
501 fn tombstone_edge(
502 &mut self,
503 src: InternalNodeId,
504 rel: RelTypeId,
505 dst: InternalNodeId,
506 ) -> nervusdb_v2_query::Result<()> {
507 self.inner.tombstone_edge(src, rel, dst);
508 Ok(())
509 }
510
511 fn get_or_create_label_id(&mut self, name: &str) -> nervusdb_v2_query::Result<LabelId> {
512 self.inner
513 .get_or_create_label(name)
514 .map_err(|e| nervusdb_v2_query::Error::Other(e.to_string()))
515 }
516
517 fn get_or_create_rel_type_id(&mut self, name: &str) -> nervusdb_v2_query::Result<RelTypeId> {
518 self.inner
519 .get_or_create_rel_type(name)
520 .map_err(|e| nervusdb_v2_query::Error::Other(e.to_string()))
521 }
522}