haystack_core/ontology/
taxonomy.rs1use parking_lot::RwLock;
4use std::collections::{HashMap, HashSet, VecDeque};
5
6pub struct TaxonomyTree {
12 parents: HashMap<String, Vec<String>>,
14 children: HashMap<String, Vec<String>>,
16 mandatory_cache: RwLock<HashMap<String, HashSet<String>>>,
18}
19
20impl TaxonomyTree {
21 pub fn new() -> Self {
23 Self {
24 parents: HashMap::new(),
25 children: HashMap::new(),
26 mandatory_cache: RwLock::new(HashMap::new()),
27 }
28 }
29
30 pub fn add(&mut self, name: &str, supertypes: &[String]) {
32 self.parents.entry(name.to_string()).or_default();
33 for parent in supertypes {
34 let parents_list = self.parents.entry(name.to_string()).or_default();
35 if !parents_list.contains(parent) {
36 parents_list.push(parent.clone());
37 }
38 let children_list = self.children.entry(parent.clone()).or_default();
39 if !children_list.contains(&name.to_string()) {
40 children_list.push(name.to_string());
41 }
42 self.parents.entry(parent.clone()).or_default();
44 }
45 self.mandatory_cache.write().clear();
47 }
48
49 pub fn is_subtype(&self, child: &str, parent: &str) -> bool {
54 if child == parent {
55 return true;
56 }
57 let mut visited: HashSet<&str> = HashSet::new();
58 let mut queue: VecDeque<&str> = VecDeque::new();
59 queue.push_back(child);
60
61 while let Some(current) = queue.pop_front() {
62 if !visited.insert(current) {
63 continue;
64 }
65 if let Some(parents) = self.parents.get(current) {
66 for p in parents {
67 if p == parent {
68 return true;
69 }
70 queue.push_back(p.as_str());
71 }
72 }
73 }
74 false
75 }
76
77 pub fn supertypes_of(&self, name: &str) -> Vec<String> {
81 let mut result: Vec<String> = Vec::new();
82 let mut visited: HashSet<String> = HashSet::new();
83 let mut queue: VecDeque<String> = VecDeque::new();
84
85 if let Some(parents) = self.parents.get(name) {
86 for p in parents {
87 queue.push_back(p.clone());
88 }
89 }
90
91 while let Some(current) = queue.pop_front() {
92 if !visited.insert(current.clone()) {
93 continue;
94 }
95 result.push(current.clone());
96 if let Some(parents) = self.parents.get(¤t) {
97 for p in parents {
98 queue.push_back(p.clone());
99 }
100 }
101 }
102 result
103 }
104
105 pub fn subtypes_of(&self, name: &str) -> Vec<String> {
107 self.children.get(name).cloned().unwrap_or_default()
108 }
109
110 pub fn all_subtypes(&self, name: &str) -> Vec<String> {
112 let mut result: Vec<String> = Vec::new();
113 let mut visited: HashSet<String> = HashSet::new();
114 let mut queue: VecDeque<String> = VecDeque::new();
115
116 if let Some(children) = self.children.get(name) {
117 for c in children {
118 queue.push_back(c.clone());
119 }
120 }
121
122 while let Some(current) = queue.pop_front() {
123 if !visited.insert(current.clone()) {
124 continue;
125 }
126 result.push(current.clone());
127 if let Some(children) = self.children.get(¤t) {
128 for c in children {
129 queue.push_back(c.clone());
130 }
131 }
132 }
133 result
134 }
135
136 pub fn mandatory_tags(&self, name: &str, mandatory_defs: &HashSet<String>) -> HashSet<String> {
141 if let Some(cached) = self.mandatory_cache.read().get(name) {
142 return cached.clone();
143 }
144
145 let mut tags: HashSet<String> = HashSet::new();
146
147 if mandatory_defs.contains(name) {
149 tags.insert(name.to_string());
150 }
151
152 let supertypes = self.supertypes_of(name);
154 for sup in &supertypes {
155 if mandatory_defs.contains(sup) {
156 tags.insert(sup.clone());
157 }
158 }
159
160 self.mandatory_cache
161 .write()
162 .insert(name.to_string(), tags.clone());
163 tags
164 }
165
166 pub fn contains(&self, name: &str) -> bool {
168 self.parents.contains_key(name)
169 }
170
171 pub fn len(&self) -> usize {
173 self.parents.len()
174 }
175
176 pub fn is_empty(&self) -> bool {
178 self.parents.is_empty()
179 }
180
181 pub fn clear_cache(&self) {
183 self.mandatory_cache.write().clear();
184 }
185}
186
187impl Default for TaxonomyTree {
188 fn default() -> Self {
189 Self::new()
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 fn build_tree() -> TaxonomyTree {
198 let mut tree = TaxonomyTree::new();
199 tree.add("marker", &[]);
207 tree.add("entity", &["marker".to_string()]);
208 tree.add("equip", &["entity".to_string()]);
209 tree.add("ahu", &["equip".to_string()]);
210 tree.add("meter", &["equip".to_string()]);
211 tree.add("point", &["entity".to_string()]);
212 tree.add("val", &["marker".to_string()]);
213 tree
214 }
215
216 #[test]
217 fn is_subtype_self() {
218 let tree = build_tree();
219 assert!(tree.is_subtype("ahu", "ahu"));
220 }
221
222 #[test]
223 fn is_subtype_direct_parent() {
224 let tree = build_tree();
225 assert!(tree.is_subtype("ahu", "equip"));
226 }
227
228 #[test]
229 fn is_subtype_ancestor() {
230 let tree = build_tree();
231 assert!(tree.is_subtype("ahu", "entity"));
232 assert!(tree.is_subtype("ahu", "marker"));
233 }
234
235 #[test]
236 fn is_subtype_false_for_unrelated() {
237 let tree = build_tree();
238 assert!(!tree.is_subtype("ahu", "point"));
239 assert!(!tree.is_subtype("ahu", "val"));
240 }
241
242 #[test]
243 fn is_subtype_false_for_child() {
244 let tree = build_tree();
245 assert!(!tree.is_subtype("equip", "ahu"));
247 }
248
249 #[test]
250 fn supertypes_of_bfs_order() {
251 let tree = build_tree();
252 let supers = tree.supertypes_of("ahu");
253 assert_eq!(supers, vec!["equip", "entity", "marker"]);
255 }
256
257 #[test]
258 fn supertypes_of_root() {
259 let tree = build_tree();
260 let supers = tree.supertypes_of("marker");
261 assert!(supers.is_empty());
262 }
263
264 #[test]
265 fn subtypes_of_direct_children() {
266 let tree = build_tree();
267 let mut children = tree.subtypes_of("equip");
268 children.sort();
269 assert_eq!(children, vec!["ahu", "meter"]);
270 }
271
272 #[test]
273 fn subtypes_of_leaf() {
274 let tree = build_tree();
275 let children = tree.subtypes_of("ahu");
276 assert!(children.is_empty());
277 }
278
279 #[test]
280 fn all_subtypes_full_tree() {
281 let tree = build_tree();
282 let mut descendants = tree.all_subtypes("entity");
283 descendants.sort();
284 assert_eq!(descendants, vec!["ahu", "equip", "meter", "point"]);
285 }
286
287 #[test]
288 fn all_subtypes_leaf() {
289 let tree = build_tree();
290 let descendants = tree.all_subtypes("ahu");
291 assert!(descendants.is_empty());
292 }
293
294 #[test]
295 fn mandatory_tags_basic() {
296 let tree = build_tree();
297 let mut mandatory_defs = HashSet::new();
298 mandatory_defs.insert("equip".to_string());
299 mandatory_defs.insert("entity".to_string());
300
301 let tags = tree.mandatory_tags("ahu", &mandatory_defs);
302 assert!(tags.contains("equip"));
303 assert!(tags.contains("entity"));
304 assert!(!tags.contains("marker"));
305 assert!(!tags.contains("ahu"));
306 }
307
308 #[test]
309 fn mandatory_tags_self_mandatory() {
310 let tree = build_tree();
311 let mut mandatory_defs = HashSet::new();
312 mandatory_defs.insert("ahu".to_string());
313
314 let tags = tree.mandatory_tags("ahu", &mandatory_defs);
315 assert!(tags.contains("ahu"));
316 }
317
318 #[test]
319 fn mandatory_tags_caching() {
320 let tree = build_tree();
321 let mandatory_defs: HashSet<String> = ["equip".to_string()].into();
322
323 let tags1 = tree.mandatory_tags("ahu", &mandatory_defs);
324 let tags2 = tree.mandatory_tags("ahu", &mandatory_defs);
325 assert_eq!(tags1, tags2);
326 }
327
328 #[test]
329 fn contains_and_len() {
330 let tree = build_tree();
331 assert!(tree.contains("ahu"));
332 assert!(tree.contains("marker"));
333 assert!(!tree.contains("nonexistent"));
334 assert_eq!(tree.len(), 7);
335 }
336
337 #[test]
338 fn empty_tree() {
339 let tree = TaxonomyTree::new();
340 assert!(tree.is_empty());
341 assert_eq!(tree.len(), 0);
342 }
343
344 #[test]
345 fn multiple_inheritance() {
346 let mut tree = TaxonomyTree::new();
347 tree.add("a", &[]);
348 tree.add("b", &[]);
349 tree.add("c", &["a".to_string(), "b".to_string()]);
350
351 assert!(tree.is_subtype("c", "a"));
352 assert!(tree.is_subtype("c", "b"));
353
354 let supers = tree.supertypes_of("c");
355 assert_eq!(supers.len(), 2);
356 assert!(supers.contains(&"a".to_string()));
357 assert!(supers.contains(&"b".to_string()));
358 }
359}