Materialize a subgraph as edge tuples within max_depth. `max_visited` caps the number of nodes visited to prevent supernode fan-out explosion. Pass [`DEFAULT_MAX_VISITED`] for the standard limit.
(
&self,
start_nodes: &[&str],
label_filter: Option<&str>,
max_depth: usize,
max_visited: usize,
)
| 300 | /// `max_visited` caps the number of nodes visited to prevent supernode fan-out |
| 301 | /// explosion. Pass [`DEFAULT_MAX_VISITED`] for the standard limit. |
| 302 | pub fn subgraph( |
| 303 | &self, |
| 304 | start_nodes: &[&str], |
| 305 | label_filter: Option<&str>, |
| 306 | max_depth: usize, |
| 307 | max_visited: usize, |
| 308 | ) -> Vec<(String, String, String)> { |
| 309 | let label_id = label_filter.and_then(|l| self.label_id(l)); |
| 310 | let mut visited: HashSet<u32> = HashSet::new(); |
| 311 | let mut queue: VecDeque<(u32, usize)> = VecDeque::new(); |
| 312 | let mut edges = Vec::new(); |
| 313 | |
| 314 | for &node in start_nodes { |
| 315 | if let Some(&id) = self.node_to_id.get(node) |
| 316 | && visited.insert(id) |
| 317 | { |
| 318 | queue.push_back((id, 0)); |
| 319 | } |
| 320 | } |
| 321 | |
| 322 | while let Some((node_id, depth)) = queue.pop_front() { |
| 323 | if depth >= max_depth || visited.len() >= max_visited { |
| 324 | continue; |
| 325 | } |
| 326 | self.record_access(node_id); |
| 327 | for (lid, dst) in self.dense_iter_out(node_id) { |
| 328 | if label_id.is_none_or(|f| f == lid) { |
| 329 | edges.push(( |
| 330 | self.id_to_node[node_id as usize].clone(), |
| 331 | self.label_name(lid).to_string(), |
| 332 | self.id_to_node[dst as usize].clone(), |
| 333 | )); |
| 334 | if visited.len() < max_visited && visited.insert(dst) { |
| 335 | queue.push_back((dst, depth + 1)); |
| 336 | } |
| 337 | } |
| 338 | } |
| 339 | } |
| 340 | |
| 341 | edges |
| 342 | } |
| 343 | } |
| 344 | |
| 345 | #[cfg(test)] |