1use super::types::CsrIndex;
6use crate::csr::rebuild::journal::{CsrWriteOp, OpOutcome};
7
8impl CsrIndex {
9 pub fn add_edge(&mut self, src: &str, label: &str, dst: &str) -> Result<(), crate::GraphError> {
17 self.add_edge_internal(src, label, dst, "", 1.0, false)
18 }
19
20 pub fn add_edge_in_collection(
28 &mut self,
29 src: &str,
30 label: &str,
31 dst: &str,
32 collection: &str,
33 ) -> Result<(), crate::GraphError> {
34 self.add_edge_internal(src, label, dst, collection, 1.0, false)
35 }
36
37 pub fn add_edge_weighted_in_collection(
40 &mut self,
41 src: &str,
42 label: &str,
43 dst: &str,
44 collection: &str,
45 weight: f64,
46 ) -> Result<(), crate::GraphError> {
47 self.add_edge_internal(src, label, dst, collection, weight, weight != 1.0)
48 }
49
50 pub fn add_edge_weighted(
56 &mut self,
57 src: &str,
58 label: &str,
59 dst: &str,
60 weight: f64,
61 ) -> Result<(), crate::GraphError> {
62 self.add_edge_internal(src, label, dst, "", weight, weight != 1.0)
63 }
64
65 fn add_edge_internal(
66 &mut self,
67 src: &str,
68 label: &str,
69 dst: &str,
70 collection: &str,
71 weight: f64,
72 force_weights: bool,
73 ) -> Result<(), crate::GraphError> {
74 let result = self.apply_add_edge(src, label, dst, collection, weight, force_weights);
75 self.journal_record(
76 || CsrWriteOp::AddEdge {
77 src: src.to_string(),
78 label: label.to_string(),
79 dst: dst.to_string(),
80 collection: collection.to_string(),
81 weight,
82 force_weights,
83 },
84 OpOutcome::of(&result),
85 );
86 result
87 }
88
89 pub(crate) fn apply_add_edge(
91 &mut self,
92 src: &str,
93 label: &str,
94 dst: &str,
95 collection: &str,
96 weight: f64,
97 force_weights: bool,
98 ) -> Result<(), crate::GraphError> {
99 let src_id = self.ensure_node(src)?;
100 let dst_id = self.ensure_node(dst)?;
101 let label_id = self.ensure_label(label)?;
102 let collection_id = self.ensure_collection(collection);
103
104 let out = &self.buffer_out[src_id as usize];
108 let out_colls = &self.buffer_out_collections[src_id as usize];
109 if out
110 .iter()
111 .zip(out_colls.iter())
112 .any(|(&(l, d), &c)| l == label_id && d == dst_id && c == collection_id)
113 {
114 return Ok(());
115 }
116 if self.dense_has_edge(src_id, label_id, dst_id, collection_id) {
118 self.deleted_edges
119 .remove(&(src_id, label_id, dst_id, collection_id));
120 return Ok(());
121 }
122
123 if force_weights && !self.has_weights {
125 self.enable_weights();
126 }
127
128 self.buffer_out[src_id as usize].push((label_id, dst_id));
129 self.buffer_in[dst_id as usize].push((label_id, src_id));
130 self.buffer_out_collections[src_id as usize].push(collection_id);
131 self.buffer_in_collections[dst_id as usize].push(collection_id);
132
133 if self.has_weights {
134 self.buffer_out_weights[src_id as usize].push(weight);
135 self.buffer_in_weights[dst_id as usize].push(weight);
136 }
137
138 self.deleted_edges
141 .remove(&(src_id, label_id, dst_id, collection_id));
142 Ok(())
143 }
144
145 pub fn remove_edge(&mut self, src: &str, label: &str, dst: &str) {
152 self.remove_edge_in_collection(src, label, dst, "");
153 }
154
155 pub fn remove_edge_in_collection(
160 &mut self,
161 src: &str,
162 label: &str,
163 dst: &str,
164 collection: &str,
165 ) {
166 self.apply_remove_edge(src, label, dst, collection);
167 self.journal_record(
168 || CsrWriteOp::RemoveEdge {
169 src: src.to_string(),
170 label: label.to_string(),
171 dst: dst.to_string(),
172 collection: collection.to_string(),
173 },
174 OpOutcome::Applied,
175 );
176 }
177
178 pub(crate) fn apply_remove_edge(
180 &mut self,
181 src: &str,
182 label: &str,
183 dst: &str,
184 collection: &str,
185 ) {
186 let (Some(&src_id), Some(&dst_id)) = (self.node_to_id.get(src), self.node_to_id.get(dst))
187 else {
188 return;
189 };
190 let Some(&label_id) = self.label_to_id.get(label) else {
191 return;
192 };
193 let Some(&collection_id) = self.collection_to_id.get(collection) else {
194 return;
195 };
196
197 let out_buf = &self.buffer_out[src_id as usize];
199 let out_colls = &self.buffer_out_collections[src_id as usize];
200 if let Some(pos) = out_buf
201 .iter()
202 .zip(out_colls.iter())
203 .position(|(&(l, d), &c)| l == label_id && d == dst_id && c == collection_id)
204 {
205 self.buffer_out[src_id as usize].swap_remove(pos);
206 self.buffer_out_collections[src_id as usize].swap_remove(pos);
207 if self.has_weights {
208 self.buffer_out_weights[src_id as usize].swap_remove(pos);
209 }
210 }
211 let in_buf = &self.buffer_in[dst_id as usize];
212 let in_colls = &self.buffer_in_collections[dst_id as usize];
213 if let Some(pos) = in_buf
214 .iter()
215 .zip(in_colls.iter())
216 .position(|(&(l, s), &c)| l == label_id && s == src_id && c == collection_id)
217 {
218 self.buffer_in[dst_id as usize].swap_remove(pos);
219 self.buffer_in_collections[dst_id as usize].swap_remove(pos);
220 if self.has_weights {
221 self.buffer_in_weights[dst_id as usize].swap_remove(pos);
222 }
223 }
224
225 if self.dense_has_edge(src_id, label_id, dst_id, collection_id) {
227 self.deleted_edges
228 .insert((src_id, label_id, dst_id, collection_id));
229 }
230 }
231
232 pub fn remove_node_edges(&mut self, node: &str) -> usize {
234 let removed = self.apply_remove_node_edges(node);
235 self.journal_record(
236 || CsrWriteOp::RemoveNodeEdges {
237 node: node.to_string(),
238 },
239 OpOutcome::Applied,
240 );
241 removed
242 }
243
244 pub(crate) fn apply_remove_node_edges(&mut self, node: &str) -> usize {
246 let Some(&node_id) = self.node_to_id.get(node) else {
247 return 0;
248 };
249 let mut removed = 0;
250
251 let out_edges = self.dense_iter_out_coll(node_id);
255 for (label_id, dst_id, coll_id) in &out_edges {
256 let in_buf = &self.buffer_in[*dst_id as usize];
257 let in_colls = &self.buffer_in_collections[*dst_id as usize];
258 if let Some(pos) = in_buf
259 .iter()
260 .zip(in_colls.iter())
261 .position(|(&(l, s), &c)| l == *label_id && s == node_id && c == *coll_id)
262 {
263 self.buffer_in[*dst_id as usize].swap_remove(pos);
264 self.buffer_in_collections[*dst_id as usize].swap_remove(pos);
265 if self.has_weights {
266 self.buffer_in_weights[*dst_id as usize].swap_remove(pos);
267 }
268 }
269 self.deleted_edges
270 .insert((node_id, *label_id, *dst_id, *coll_id));
271 removed += 1;
272 }
273 self.buffer_out[node_id as usize].clear();
274 self.buffer_out_collections[node_id as usize].clear();
275 if self.has_weights {
276 self.buffer_out_weights[node_id as usize].clear();
277 }
278
279 let in_edges = self.dense_iter_in_coll(node_id);
282 for (label_id, src_id, coll_id) in &in_edges {
283 let out_buf = &self.buffer_out[*src_id as usize];
284 let out_colls = &self.buffer_out_collections[*src_id as usize];
285 if let Some(pos) = out_buf
286 .iter()
287 .zip(out_colls.iter())
288 .position(|(&(l, d), &c)| l == *label_id && d == node_id && c == *coll_id)
289 {
290 self.buffer_out[*src_id as usize].swap_remove(pos);
291 self.buffer_out_collections[*src_id as usize].swap_remove(pos);
292 if self.has_weights {
293 self.buffer_out_weights[*src_id as usize].swap_remove(pos);
294 }
295 }
296 self.deleted_edges
297 .insert((*src_id, *label_id, node_id, *coll_id));
298 removed += 1;
299 }
300 self.buffer_in[node_id as usize].clear();
301 self.buffer_in_collections[node_id as usize].clear();
302 if self.has_weights {
303 self.buffer_in_weights[node_id as usize].clear();
304 }
305
306 removed
307 }
308
309 pub fn remove_nodes_with_prefix(&mut self, prefix: &str) {
314 let matching_nodes: Vec<String> = self
315 .node_to_id
316 .keys()
317 .filter(|k| k.starts_with(prefix))
318 .cloned()
319 .collect();
320 for node in &matching_nodes {
321 self.remove_node_edges(node);
322 }
323 }
324}