Find the shortest path between two nodes. Returns the path as a list of node IDs (`from` first, `to` last), or `None` if no path exists within `max_depth` hops. Default: forward breadth-first search built on `graph_traverse`. Each frontier expansion calls `graph_traverse(node, 1, edge_filter)` to discover outgoing neighbors. Inherits the underlying impl's edge direction semantics. Implementation
(
&self,
collection: &str,
from: &NodeId,
to: &NodeId,
max_depth: u8,
edge_filter: Option<&EdgeFilter>,
)
| 334 | /// performance — round-tripping per-hop is O(path_length) wire |
| 335 | /// hops. |
| 336 | async fn graph_shortest_path( |
| 337 | &self, |
| 338 | collection: &str, |
| 339 | from: &NodeId, |
| 340 | to: &NodeId, |
| 341 | max_depth: u8, |
| 342 | edge_filter: Option<&EdgeFilter>, |
| 343 | ) -> NodeDbResult<Option<Vec<NodeId>>> { |
| 344 | if from == to { |
| 345 | return Ok(Some(vec![from.clone()])); |
| 346 | } |
| 347 | if max_depth == 0 { |
| 348 | return Ok(None); |
| 349 | } |
| 350 | |
| 351 | // Map of `node -> parent` used to reconstruct the path once the |
| 352 | // target is reached. The source has no parent entry. |
| 353 | let mut parent: std::collections::HashMap<NodeId, NodeId> = |
| 354 | std::collections::HashMap::new(); |
| 355 | let mut frontier: Vec<NodeId> = vec![from.clone()]; |
| 356 | |
| 357 | for _ in 0..max_depth { |
| 358 | let mut next_frontier: Vec<NodeId> = Vec::new(); |
| 359 | for node in &frontier { |
| 360 | let sg = self |
| 361 | .graph_traverse(collection, node, 1, edge_filter) |
| 362 | .await?; |
| 363 | for edge in &sg.edges { |
| 364 | // Only follow edges originating from the current |
| 365 | // node — `graph_traverse` may include adjacent |
| 366 | // edges that don't extend the BFS frontier. |
| 367 | if &edge.from != node { |
| 368 | continue; |
| 369 | } |
| 370 | let dst = &edge.to; |
| 371 | if dst == from || parent.contains_key(dst) { |
| 372 | continue; |
| 373 | } |
| 374 | parent.insert(dst.clone(), node.clone()); |
| 375 | if dst == to { |
| 376 | let mut path = vec![to.clone()]; |
| 377 | let mut cur = to.clone(); |
| 378 | while &cur != from { |
| 379 | let p = parent |
| 380 | .get(&cur) |
| 381 | .expect("BFS reached `to` so all ancestors are tracked") |
| 382 | .clone(); |
| 383 | path.push(p.clone()); |
| 384 | cur = p; |
| 385 | } |
| 386 | path.reverse(); |
| 387 | return Ok(Some(path)); |
| 388 | } |
| 389 | next_frontier.push(dst.clone()); |
| 390 | } |
| 391 | } |
| 392 | if next_frontier.is_empty() { |
| 393 | return Ok(None); |
nothing calls this directly
no test coverage detected