BFS traversal. Returns all reachable node IDs within max_depth hops. `max_visited` caps the number of nodes visited to prevent supernode fan-out explosion. Pass [`DEFAULT_MAX_VISITED`] for the standard limit. `frontier_bitmap`: when `Some`, only nodes whose surrogate is present in the bitmap are eligible as traversal targets. Start nodes are not gated — only newly discovered frontier nodes are c
(
&self,
start_nodes: &[&str],
label_filter: Option<&str>,
direction: Direction,
max_depth: usize,
max_visited: usize,
frontier_bitmap: Option<&
| 26 | /// bitmap are eligible as traversal targets. Start nodes are not gated — only |
| 27 | /// newly discovered frontier nodes are checked. |
| 28 | pub fn traverse_bfs( |
| 29 | &self, |
| 30 | start_nodes: &[&str], |
| 31 | label_filter: Option<&str>, |
| 32 | direction: Direction, |
| 33 | max_depth: usize, |
| 34 | max_visited: usize, |
| 35 | frontier_bitmap: Option<&nodedb_types::SurrogateBitmap>, |
| 36 | ) -> Vec<String> { |
| 37 | let label_id = label_filter.and_then(|l| self.label_id(l)); |
| 38 | let mut visited: HashSet<u32> = HashSet::new(); |
| 39 | let mut queue: VecDeque<(u32, usize)> = VecDeque::new(); |
| 40 | |
| 41 | for &node in start_nodes { |
| 42 | if let Some(&id) = self.node_to_id.get(node) |
| 43 | && visited.insert(id) |
| 44 | { |
| 45 | queue.push_back((id, 0)); |
| 46 | } |
| 47 | } |
| 48 | |
| 49 | while let Some((node_id, depth)) = queue.pop_front() { |
| 50 | if depth >= max_depth || visited.len() >= max_visited { |
| 51 | continue; |
| 52 | } |
| 53 | |
| 54 | // Track access for hot/cold partition decisions. |
| 55 | self.record_access(node_id); |
| 56 | |
| 57 | if matches!(direction, Direction::Out | Direction::Both) { |
| 58 | for (lid, dst) in self.dense_iter_out(node_id) { |
| 59 | if label_id.is_none_or(|f| f == lid) |
| 60 | && visited.len() < max_visited |
| 61 | && frontier_bitmap.is_none_or(|bm| { |
| 62 | bm.contains(nodedb_types::Surrogate::new(self.node_surrogate_raw(dst))) |
| 63 | }) |
| 64 | && visited.insert(dst) |
| 65 | { |
| 66 | self.prefetch_node(dst); |
| 67 | queue.push_back((dst, depth + 1)); |
| 68 | } |
| 69 | } |
| 70 | } |
| 71 | if matches!(direction, Direction::In | Direction::Both) { |
| 72 | for (lid, src) in self.dense_iter_in(node_id) { |
| 73 | if label_id.is_none_or(|f| f == lid) |
| 74 | && visited.len() < max_visited |
| 75 | && frontier_bitmap.is_none_or(|bm| { |
| 76 | bm.contains(nodedb_types::Surrogate::new(self.node_surrogate_raw(src))) |
| 77 | }) |
| 78 | && visited.insert(src) |
| 79 | { |
| 80 | self.prefetch_node(src); |
| 81 | queue.push_back((src, depth + 1)); |
| 82 | } |
| 83 | } |
| 84 | } |
| 85 | } |