Skip to main content

velesdb_core/collection/graph/
edge_persistence.rs

1//! `EdgeStore` persistence (serialization/deserialization) methods.
2//!
3//! Extracted from `edge.rs` to reduce NLOC below the 500 threshold.
4
5use super::csr_snapshot::{CsrSnapshot, SnapshotBuilder};
6use super::edge::EdgeStore;
7use super::helpers::PostcardPersistence;
8use super::label_table::LabelTable;
9
10// ---------------------------------------------------------------------------
11// CSR snapshot methods (G1: zero-copy BFS)
12// ---------------------------------------------------------------------------
13impl EdgeStore {
14    /// Builds a CSR snapshot from the current outgoing index.
15    ///
16    /// This pre-computes contiguous arrays of target IDs, edge IDs, and
17    /// interned labels for all source nodes. After calling this, BFS
18    /// traversal uses `with_neighbors()` for zero-copy `&[u64]` access
19    /// instead of cloning full `GraphEdge` objects.
20    ///
21    /// # When to call
22    ///
23    /// - After loading from disk (graph is ready for reads)
24    /// - After a batch of mutations, before a read-heavy phase
25    ///
26    /// The snapshot is automatically invalidated by any write operation.
27    pub fn build_read_snapshot(&mut self) {
28        let label_table = LabelTable::new();
29        self.csr_snapshot = Some(SnapshotBuilder::build(self, &label_table));
30    }
31
32    /// Returns a reference to the CSR snapshot, if built.
33    #[must_use]
34    #[inline]
35    pub fn csr_snapshot(&self) -> Option<&CsrSnapshot> {
36        self.csr_snapshot.as_ref()
37    }
38
39    /// Returns `true` if a CSR snapshot is available for zero-copy reads.
40    #[must_use]
41    #[inline]
42    pub fn has_csr_snapshot(&self) -> bool {
43        self.csr_snapshot.is_some()
44    }
45
46    /// Provides zero-copy access to neighbor target IDs via a callback.
47    #[inline]
48    pub fn with_neighbors<F, R>(&self, source_id: u64, f: F) -> R
49    where
50        F: FnOnce(&[u64]) -> R,
51    {
52        if let Some(snapshot) = &self.csr_snapshot {
53            f(snapshot.neighbors(source_id))
54        } else {
55            let ids: Vec<u64> = self
56                .get_outgoing(source_id)
57                .iter()
58                .map(|e| e.target())
59                .collect();
60            f(&ids)
61        }
62    }
63
64    /// Provides zero-copy access to `(target_id, edge_id)` pairs via callback.
65    #[inline]
66    pub fn with_neighbor_edges<F, R>(&self, source_id: u64, f: F) -> R
67    where
68        F: FnOnce(&[u64], &[u64]) -> R,
69    {
70        if let Some(snapshot) = &self.csr_snapshot {
71            f(snapshot.neighbors(source_id), snapshot.edge_ids(source_id))
72        } else {
73            let edges = self.get_outgoing(source_id);
74            let targets: Vec<u64> = edges.iter().map(|e| e.target()).collect();
75            let eids: Vec<u64> = edges.iter().map(|e| e.id()).collect();
76            f(&targets, &eids)
77        }
78    }
79}
80
81impl PostcardPersistence for EdgeStore {}
82
83// Inherent persistence methods that delegate to `PostcardPersistence`.
84impl EdgeStore {
85    /// Serializes the edge store to bytes using `postcard`.
86    ///
87    /// # Errors
88    /// Returns an error if serialization fails.
89    pub fn to_bytes(&self) -> std::result::Result<Vec<u8>, postcard::Error> {
90        <Self as PostcardPersistence>::to_bytes(self)
91    }
92
93    /// Deserializes an edge store from bytes.
94    ///
95    /// # Errors
96    /// Returns an error if deserialization fails (e.g., corrupted data).
97    pub fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, postcard::Error> {
98        <Self as PostcardPersistence>::from_bytes(bytes)
99    }
100
101    /// Saves the edge store to a file.
102    ///
103    /// # Errors
104    /// Returns an error if serialization or file I/O fails.
105    pub fn save_to_file(&self, path: &std::path::Path) -> std::io::Result<()> {
106        <Self as PostcardPersistence>::save_to_file(self, path)
107    }
108
109    /// Loads an edge store from a file.
110    ///
111    /// Automatically builds a CSR snapshot after loading for zero-copy
112    /// BFS traversal (G1).
113    ///
114    /// # Errors
115    /// Returns an error if file I/O or deserialization fails.
116    pub fn load_from_file(path: &std::path::Path) -> std::io::Result<Self> {
117        let mut store = <Self as PostcardPersistence>::load_from_file(path)?;
118        store.build_read_snapshot();
119        Ok(store)
120    }
121}