nodedb_graph/csr/index/
lookup.rs1use super::types::{CsrIndex, Direction};
5
6impl CsrIndex {
7 pub fn neighbors(
9 &self,
10 node: &str,
11 label_filter: Option<&str>,
12 direction: Direction,
13 ) -> Vec<(String, String)> {
14 let Some(&node_id) = self.node_to_id.get(node) else {
15 return Vec::new();
16 };
17 self.record_access(node_id);
18 let label_id = label_filter.and_then(|l| self.label_to_id.get(l).copied());
19
20 let mut result = Vec::new();
21
22 if matches!(direction, Direction::Out | Direction::Both) {
23 for (lid, dst) in self.iter_out_edges(node_id) {
24 if label_id.is_none_or(|f| f == lid) {
25 result.push((
26 self.id_to_label[lid as usize].clone(),
27 self.id_to_node[dst as usize].clone(),
28 ));
29 }
30 }
31 }
32 if matches!(direction, Direction::In | Direction::Both) {
33 for (lid, src) in self.iter_in_edges(node_id) {
34 if label_id.is_none_or(|f| f == lid) {
35 result.push((
36 self.id_to_label[lid as usize].clone(),
37 self.id_to_node[src as usize].clone(),
38 ));
39 }
40 }
41 }
42
43 result
44 }
45
46 pub fn neighbors_multi(
48 &self,
49 node: &str,
50 label_filters: &[&str],
51 direction: Direction,
52 ) -> Vec<(String, String)> {
53 let Some(&node_id) = self.node_to_id.get(node) else {
54 return Vec::new();
55 };
56 self.record_access(node_id);
57 let label_ids: Vec<u32> = label_filters
58 .iter()
59 .filter_map(|l| self.label_to_id.get(*l).copied())
60 .collect();
61 let match_label = |lid: u32| label_ids.is_empty() || label_ids.contains(&lid);
62
63 let mut result = Vec::new();
64
65 if matches!(direction, Direction::Out | Direction::Both) {
66 for (lid, dst) in self.iter_out_edges(node_id) {
67 if match_label(lid) {
68 result.push((
69 self.id_to_label[lid as usize].clone(),
70 self.id_to_node[dst as usize].clone(),
71 ));
72 }
73 }
74 }
75 if matches!(direction, Direction::In | Direction::Both) {
76 for (lid, src) in self.iter_in_edges(node_id) {
77 if match_label(lid) {
78 result.push((
79 self.id_to_label[lid as usize].clone(),
80 self.id_to_node[src as usize].clone(),
81 ));
82 }
83 }
84 }
85
86 result
87 }
88
89 pub fn add_node(&mut self, name: &str) -> u32 {
92 self.ensure_node(name)
93 }
94
95 pub fn node_count(&self) -> usize {
96 self.id_to_node.len()
97 }
98
99 pub fn contains_node(&self, node: &str) -> bool {
100 self.node_to_id.contains_key(node)
101 }
102
103 pub fn node_name(&self, dense_id: u32) -> &str {
105 &self.id_to_node[dense_id as usize]
106 }
107
108 pub fn node_id(&self, name: &str) -> Option<u32> {
110 self.node_to_id.get(name).copied()
111 }
112
113 pub fn label_name(&self, label_id: u32) -> &str {
115 &self.id_to_label[label_id as usize]
116 }
117
118 pub fn label_id(&self, name: &str) -> Option<u32> {
120 self.label_to_id.get(name).copied()
121 }
122
123 pub fn out_degree(&self, node_id: u32) -> usize {
125 self.iter_out_edges(node_id).count()
126 }
127
128 pub fn in_degree(&self, node_id: u32) -> usize {
130 self.iter_in_edges(node_id).count()
131 }
132
133 pub fn edge_count(&self) -> usize {
135 let n = self.id_to_node.len();
136 (0..n).map(|i| self.out_degree(i as u32)).sum()
137 }
138
139 pub(crate) fn build_dense(edges: &[Vec<(u32, u32)>]) -> (Vec<u32>, Vec<u32>, Vec<u32>) {
143 let n = edges.len();
144 let total: usize = edges.iter().map(|e| e.len()).sum();
145 let mut offsets = Vec::with_capacity(n + 1);
146 let mut targets = Vec::with_capacity(total);
147 let mut labels = Vec::with_capacity(total);
148
149 let mut offset = 0u32;
150 for node_edges in edges {
151 offsets.push(offset);
152 for &(lid, target) in node_edges {
153 targets.push(target);
154 labels.push(lid);
155 }
156 offset += node_edges.len() as u32;
157 }
158 offsets.push(offset);
159
160 (offsets, targets, labels)
161 }
162
163 pub(crate) fn dense_has_edge(&self, src: u32, label: u32, dst: u32) -> bool {
165 for (lid, target) in self.dense_out_edges(src) {
166 if lid == label && target == dst {
167 return true;
168 }
169 }
170 false
171 }
172
173 pub(crate) fn dense_out_edges(&self, node: u32) -> impl Iterator<Item = (u32, u32)> + '_ {
175 let idx = node as usize;
176 if idx + 1 >= self.out_offsets.len() {
177 return Vec::new().into_iter();
178 }
179 let start = self.out_offsets[idx] as usize;
180 let end = self.out_offsets[idx + 1] as usize;
181 (start..end)
182 .map(move |i| (self.out_labels[i], self.out_targets[i]))
183 .collect::<Vec<_>>()
184 .into_iter()
185 }
186
187 pub(crate) fn dense_in_edges(&self, node: u32) -> impl Iterator<Item = (u32, u32)> + '_ {
189 let idx = node as usize;
190 if idx + 1 >= self.in_offsets.len() {
191 return Vec::new().into_iter();
192 }
193 let start = self.in_offsets[idx] as usize;
194 let end = self.in_offsets[idx + 1] as usize;
195 (start..end)
196 .map(move |i| (self.in_labels[i], self.in_targets[i]))
197 .collect::<Vec<_>>()
198 .into_iter()
199 }
200
201 pub fn iter_out_edges(&self, node: u32) -> impl Iterator<Item = (u32, u32)> + '_ {
203 let idx = node as usize;
204 let dense = self
205 .dense_out_edges(node)
206 .filter(move |&(lid, dst)| !self.deleted_edges.contains(&(node, lid, dst)));
207 let buffer = if idx < self.buffer_out.len() {
208 self.buffer_out[idx].to_vec()
209 } else {
210 Vec::new()
211 };
212 dense.chain(buffer)
213 }
214
215 pub fn iter_in_edges(&self, node: u32) -> impl Iterator<Item = (u32, u32)> + '_ {
217 let idx = node as usize;
218 let dense = self
219 .dense_in_edges(node)
220 .filter(move |&(lid, src)| !self.deleted_edges.contains(&(src, lid, node)));
221 let buffer = if idx < self.buffer_in.len() {
222 self.buffer_in[idx].to_vec()
223 } else {
224 Vec::new()
225 };
226 dense.chain(buffer)
227 }
228}