1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
use std::collections::BTreeSet;
use selene_core::{EdgeId, NodeId};
use super::Mutator;
use crate::{GraphError, GraphResult};
impl<'tx, 'g> Mutator<'tx, 'g> {
/// Delete a set of node and edge references.
///
/// Nodes are deleted first through [`Self::delete_node`], so incident edges
/// keep the same cascade behavior and change ordering as a direct node
/// delete. Explicit edges already removed by a node cascade are skipped
/// during the edge pass. IDs that are mapped but already invalidated are
/// clean no-ops, matching set-oriented query deletion where duplicate rows
/// may reference the same graph element more than once. IDs that were never
/// allocated still return the same not-found errors as single-element
/// delete calls. The full set is validated before the first row is removed,
/// so a mixed set cannot partially mutate the transaction before surfacing a
/// missing-id error.
pub fn delete_elements(
&mut self,
nodes: BTreeSet<NodeId>,
edges: BTreeSet<EdgeId>,
) -> GraphResult<()> {
self.validate_delete_set(&nodes, &edges)?;
for node in nodes {
if self.node_can_be_deleted(node)? {
self.delete_node(node)?;
}
}
for edge in edges {
if self.edge_can_be_deleted(edge)? {
self.delete_edge(edge)?;
}
}
Ok(())
}
fn validate_delete_set(
&self,
nodes: &BTreeSet<NodeId>,
edges: &BTreeSet<EdgeId>,
) -> GraphResult<()> {
for node in nodes {
self.node_can_be_deleted(*node)?;
}
for edge in edges {
self.edge_can_be_deleted(*edge)?;
}
Ok(())
}
fn node_can_be_deleted(&self, id: NodeId) -> GraphResult<bool> {
let graph = self.txn.read();
let row = graph
.row_for_node_id(id)
.ok_or(GraphError::NodeNotFound { id })?
.get();
if row as usize >= graph.node_store.len() {
return Err(GraphError::NodeNotFound { id });
}
Ok(graph.node_store.is_alive(row))
}
fn edge_can_be_deleted(&self, id: EdgeId) -> GraphResult<bool> {
let graph = self.txn.read();
let row = graph
.row_for_edge_id(id)
.ok_or(GraphError::EdgeNotFound { id })?
.get();
if row as usize >= graph.edge_store.len() {
return Err(GraphError::EdgeNotFound { id });
}
Ok(graph.edge_store.is_alive(row))
}
}