Skip to main content

velesdb_core/collection/graph/
edge_removal.rs

1//! Edge removal operations for `EdgeStore`.
2//!
3//! Extracted from `edge.rs` to keep file NLOC under the 500 threshold.
4//! Contains `remove_edge`, `remove_node_edges`, and the internal
5//! `purge_*` index-cleanup helpers.
6
7use super::edge::EdgeStore;
8
9impl EdgeStore {
10    /// Removes an edge by ID.
11    ///
12    /// Cleans up all indices: outgoing, incoming, by_label, and outgoing_by_label.
13    pub fn remove_edge(&mut self, edge_id: u64) {
14        if let Some(edge) = self.edges.remove(&edge_id) {
15            let source = edge.source();
16            self.purge_outgoing_index(edge_id, source);
17            self.purge_incoming_index(edge_id, edge.target());
18            self.purge_label_indices(edge_id, source, edge.label());
19            // Invalidate CSR snapshot (G1).
20            self.csr_snapshot = None;
21        }
22    }
23
24    /// Removes an edge by ID, only cleaning the outgoing index.
25    ///
26    /// Used by `ConcurrentEdgeStore` for cross-shard cleanup.
27    /// Also cleans up label indices since they are maintained by source shard.
28    pub fn remove_edge_outgoing_only(&mut self, edge_id: u64) {
29        if let Some(edge) = self.edges.remove(&edge_id) {
30            let source = edge.source();
31            self.purge_outgoing_index(edge_id, source);
32            self.purge_label_indices(edge_id, source, edge.label());
33            // Invalidate CSR snapshot (G1).
34            self.csr_snapshot = None;
35        }
36    }
37
38    /// Removes an edge by ID, only cleaning the incoming index.
39    ///
40    /// Used by `ConcurrentEdgeStore` for cross-shard cleanup.
41    pub fn remove_edge_incoming_only(&mut self, edge_id: u64) {
42        if let Some(edge) = self.edges.remove(&edge_id) {
43            self.purge_incoming_index(edge_id, edge.target());
44            // Invalidate CSR snapshot (G1).
45            self.csr_snapshot = None;
46        }
47    }
48
49    /// Removes all edges connected to a node (cascade delete).
50    ///
51    /// Removes both outgoing and incoming edges, cleaning up all indices
52    /// including label indices (EPIC-019 US-003).
53    pub fn remove_node_edges(&mut self, node_id: u64) {
54        // Collect edge IDs to remove (outgoing)
55        let outgoing_ids: Vec<u64> = self.outgoing.remove(&node_id).unwrap_or_default();
56
57        // Collect edge IDs to remove (incoming)
58        let incoming_ids: Vec<u64> = self.incoming.remove(&node_id).unwrap_or_default();
59
60        // Remove outgoing edges: clean incoming + label indices for each
61        for edge_id in outgoing_ids {
62            if let Some(edge) = self.edges.remove(&edge_id) {
63                self.purge_incoming_index(edge_id, edge.target());
64                self.purge_label_indices(edge_id, node_id, edge.label());
65            }
66        }
67
68        // Remove incoming edges: clean outgoing + label indices for each
69        for edge_id in incoming_ids {
70            if let Some(edge) = self.edges.remove(&edge_id) {
71                let source = edge.source();
72                self.purge_outgoing_index(edge_id, source);
73                self.purge_label_indices(edge_id, source, edge.label());
74            }
75        }
76
77        // Invalidate CSR snapshot (G1).
78        self.csr_snapshot = None;
79    }
80
81    /// Removes `edge_id` from the incoming index of `target_node`.
82    #[inline]
83    fn purge_incoming_index(&mut self, edge_id: u64, target_node: u64) {
84        if let Some(ids) = self.incoming.get_mut(&target_node) {
85            ids.retain(|&id| id != edge_id);
86        }
87    }
88
89    /// Removes `edge_id` from the outgoing index of `source_node`.
90    #[inline]
91    fn purge_outgoing_index(&mut self, edge_id: u64, source_node: u64) {
92        if let Some(ids) = self.outgoing.get_mut(&source_node) {
93            ids.retain(|&id| id != edge_id);
94        }
95    }
96
97    /// Removes `edge_id` from the `by_label` and `outgoing_by_label` indices (US-003).
98    #[inline]
99    fn purge_label_indices(&mut self, edge_id: u64, source_node: u64, label: &str) {
100        if let Some(ids) = self.by_label.get_mut(label) {
101            ids.retain(|&id| id != edge_id);
102        }
103        if let Some(ids) = self
104            .outgoing_by_label
105            .get_mut(&(source_node, label.to_string()))
106        {
107            ids.retain(|&id| id != edge_id);
108        }
109    }
110}