mirror of
https://github.com/ruvnet/RuVector.git
synced 2026-05-31 05:13:39 +00:00
feat(ruvector-graph): add delete_edges_batch for single-transaction edge deletion
GraphDB and GraphStorage: add delete_edges_batch(ids: &[EdgeId]) -> Result<usize> - Single transaction for all deletes (vs N transactions in sequential delete_edge loop) - Returns count of edges actually deleted (skips IDs not found) - Updates edge_type_index and adjacency_index in single pass - All 17 edge tests pass
This commit is contained in:
parent
9e08b74c1a
commit
e04b780c66
3 changed files with 112 additions and 0 deletions
|
|
@ -218,6 +218,32 @@ impl GraphDB {
|
|||
}
|
||||
}
|
||||
|
||||
/// Delete multiple edges (batch)
|
||||
pub fn delete_edges_batch(&self, ids: &[EdgeId]) -> Result<usize> {
|
||||
let mut deleted = 0;
|
||||
let mut edges_to_update = Vec::with_capacity(ids.len());
|
||||
|
||||
for id in ids {
|
||||
let key: &str = id.as_ref();
|
||||
if let Some((_, edge)) = self.edges.remove(key) {
|
||||
edges_to_update.push(edge);
|
||||
deleted += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for edge in &edges_to_update {
|
||||
self.edge_type_index.remove_edge(edge);
|
||||
self.adjacency_index.remove_edge(edge);
|
||||
}
|
||||
|
||||
#[cfg(feature = "storage")]
|
||||
if let Some(storage) = &self.storage {
|
||||
storage.delete_edges_batch(ids)?;
|
||||
}
|
||||
|
||||
Ok(deleted)
|
||||
}
|
||||
|
||||
/// Get edges by type
|
||||
pub fn get_edges_by_type(&self, edge_type: &str) -> Vec<Edge> {
|
||||
self.edge_type_index
|
||||
|
|
|
|||
|
|
@ -261,6 +261,22 @@ impl GraphStorage {
|
|||
Ok(deleted)
|
||||
}
|
||||
|
||||
pub fn delete_edges_batch(&self, ids: &[EdgeId]) -> Result<usize> {
|
||||
let write_txn = self.db.begin_write()?;
|
||||
let mut deleted = 0;
|
||||
{
|
||||
let mut table = write_txn.open_table(EDGES_TABLE)?;
|
||||
for id in ids {
|
||||
if table.remove(id.as_str())?.is_some() {
|
||||
deleted += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write_txn.commit()?;
|
||||
Ok(deleted)
|
||||
}
|
||||
|
||||
/// Get all edge IDs
|
||||
pub fn all_edge_ids(&self) -> Result<Vec<EdgeId>> {
|
||||
let read_txn = self.db.begin_read()?;
|
||||
|
|
|
|||
|
|
@ -405,3 +405,73 @@ fn test_get_edges_for_nodes() {
|
|||
let empty = db.get_edges_for_nodes(&[]);
|
||||
assert_eq!(empty.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_edges_batch_basic() {
|
||||
let db = GraphDB::new();
|
||||
|
||||
db.create_node(Node::new("a".to_string(), vec![], Properties::new())).unwrap();
|
||||
db.create_node(Node::new("b".to_string(), vec![], Properties::new())).unwrap();
|
||||
|
||||
for i in 0..5 {
|
||||
let edge = Edge::new(
|
||||
format!("e{}", i),
|
||||
"a".to_string(),
|
||||
"b".to_string(),
|
||||
"LINKS".to_string(),
|
||||
Properties::new(),
|
||||
);
|
||||
db.create_edge(edge).unwrap();
|
||||
}
|
||||
|
||||
let ids = vec!["e0".to_string(), "e2".to_string(), "e4".to_string()];
|
||||
let deleted = db.delete_edges_batch(&ids).unwrap();
|
||||
assert_eq!(deleted, 3);
|
||||
|
||||
assert!(db.get_edge("e0").is_none());
|
||||
assert!(db.get_edge("e2").is_none());
|
||||
assert!(db.get_edge("e4").is_none());
|
||||
assert!(db.get_edge("e1").is_some());
|
||||
assert!(db.get_edge("e3").is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_edges_batch_partial_not_found() {
|
||||
let db = GraphDB::new();
|
||||
|
||||
db.create_node(Node::new("x".to_string(), vec![], Properties::new())).unwrap();
|
||||
db.create_node(Node::new("y".to_string(), vec![], Properties::new())).unwrap();
|
||||
|
||||
let edge = Edge::new("e1".to_string(), "x".to_string(), "y".to_string(), "TO".to_string(), Properties::new());
|
||||
db.create_edge(edge).unwrap();
|
||||
|
||||
let ids = vec!["e1".to_string(), "does_not_exist".to_string()];
|
||||
let deleted = db.delete_edges_batch(&ids).unwrap();
|
||||
assert_eq!(deleted, 1);
|
||||
|
||||
assert!(db.get_edge("e1").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_edges_batch_updates_indexes() {
|
||||
let db = GraphDB::new();
|
||||
|
||||
db.create_node(Node::new("src".to_string(), vec![], Properties::new())).unwrap();
|
||||
db.create_node(Node::new("dst".to_string(), vec![], Properties::new())).unwrap();
|
||||
|
||||
let edge = Edge::new("edge1".to_string(), "src".to_string(), "dst".to_string(), "T".to_string(), Properties::new());
|
||||
db.create_edge(edge).unwrap();
|
||||
|
||||
assert!(db.get_edges_for_nodes(&["src".to_string()]).len() == 1);
|
||||
|
||||
db.delete_edges_batch(&["edge1".to_string()]).unwrap();
|
||||
|
||||
assert!(db.get_edges_for_nodes(&["src".to_string()]).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_edges_batch_empty() {
|
||||
let db = GraphDB::new();
|
||||
let deleted = db.delete_edges_batch(&[]).unwrap();
|
||||
assert_eq!(deleted, 0);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue