1use std::collections::BTreeSet;
9
10use lora_ast::Direction;
11
12use crate::types::{
13 ExpandedRelationship, NodeId, NodeRecord, Properties, PropertyValue, RelationshipId,
14 RelationshipRecord,
15};
16
17pub trait GraphStorage {
33 fn contains_node(&self, id: NodeId) -> bool;
37
38 fn node(&self, id: NodeId) -> Option<NodeRecord>;
42
43 fn all_node_ids(&self) -> Vec<NodeId>;
45
46 fn node_ids_by_label(&self, label: &str) -> Vec<NodeId>;
49
50 fn contains_relationship(&self, id: RelationshipId) -> bool;
53
54 fn relationship(&self, id: RelationshipId) -> Option<RelationshipRecord>;
55
56 fn all_rel_ids(&self) -> Vec<RelationshipId>;
57
58 fn rel_ids_by_type(&self, rel_type: &str) -> Vec<RelationshipId>;
59
60 fn relationship_endpoints(&self, id: RelationshipId) -> Option<(NodeId, NodeId)>;
64
65 fn expand_ids(
71 &self,
72 node_id: NodeId,
73 direction: Direction,
74 types: &[String],
75 ) -> Vec<(RelationshipId, NodeId)>;
76
77 fn all_labels(&self) -> Vec<String>;
80 fn all_relationship_types(&self) -> Vec<String>;
81
82 fn with_node<F, R>(&self, id: NodeId, f: F) -> Option<R>
89 where
90 F: FnOnce(&NodeRecord) -> R,
91 Self: Sized,
92 {
93 self.node(id).as_ref().map(f)
94 }
95
96 fn with_relationship<F, R>(&self, id: RelationshipId, f: F) -> Option<R>
97 where
98 F: FnOnce(&RelationshipRecord) -> R,
99 Self: Sized,
100 {
101 self.relationship(id).as_ref().map(f)
102 }
103
104 fn has_node(&self, id: NodeId) -> bool {
107 self.contains_node(id)
108 }
109
110 fn has_relationship(&self, id: RelationshipId) -> bool {
111 self.contains_relationship(id)
112 }
113
114 fn node_count(&self) -> usize {
115 self.all_node_ids().len()
116 }
117
118 fn relationship_count(&self) -> usize {
119 self.all_rel_ids().len()
120 }
121
122 fn all_nodes(&self) -> Vec<NodeRecord> {
130 self.all_node_ids()
131 .into_iter()
132 .filter_map(|id| self.node(id))
133 .collect()
134 }
135
136 fn nodes_by_label(&self, label: &str) -> Vec<NodeRecord> {
137 self.node_ids_by_label(label)
138 .into_iter()
139 .filter_map(|id| self.node(id))
140 .collect()
141 }
142
143 fn all_relationships(&self) -> Vec<RelationshipRecord> {
144 self.all_rel_ids()
145 .into_iter()
146 .filter_map(|id| self.relationship(id))
147 .collect()
148 }
149
150 fn relationships_by_type(&self, rel_type: &str) -> Vec<RelationshipRecord> {
151 self.rel_ids_by_type(rel_type)
152 .into_iter()
153 .filter_map(|id| self.relationship(id))
154 .collect()
155 }
156
157 fn relationship_ids_of(&self, node_id: NodeId, direction: Direction) -> Vec<RelationshipId> {
160 self.expand_ids(node_id, direction, &[])
161 .into_iter()
162 .map(|(rel_id, _)| rel_id)
163 .collect()
164 }
165
166 fn outgoing_relationships(&self, node_id: NodeId) -> Vec<RelationshipRecord> {
167 self.relationship_ids_of(node_id, Direction::Right)
168 .into_iter()
169 .filter_map(|id| self.relationship(id))
170 .collect()
171 }
172
173 fn incoming_relationships(&self, node_id: NodeId) -> Vec<RelationshipRecord> {
174 self.relationship_ids_of(node_id, Direction::Left)
175 .into_iter()
176 .filter_map(|id| self.relationship(id))
177 .collect()
178 }
179
180 fn relationships_of(&self, node_id: NodeId, direction: Direction) -> Vec<RelationshipRecord> {
181 self.relationship_ids_of(node_id, direction)
182 .into_iter()
183 .filter_map(|id| self.relationship(id))
184 .collect()
185 }
186
187 fn degree(&self, node_id: NodeId, direction: Direction) -> usize {
188 self.expand_ids(node_id, direction, &[]).len()
189 }
190
191 fn is_isolated(&self, node_id: NodeId) -> bool {
192 self.degree(node_id, Direction::Undirected) == 0
193 }
194
195 fn expand(
196 &self,
197 node_id: NodeId,
198 direction: Direction,
199 types: &[String],
200 ) -> Vec<(RelationshipRecord, NodeRecord)> {
201 self.expand_ids(node_id, direction, types)
202 .into_iter()
203 .filter_map(|(rid, nid)| {
204 let rel = self.relationship(rid)?;
205 let node = self.node(nid)?;
206 Some((rel, node))
207 })
208 .collect()
209 }
210
211 fn expand_detailed(
212 &self,
213 node_id: NodeId,
214 direction: Direction,
215 types: &[String],
216 ) -> Vec<ExpandedRelationship> {
217 self.expand(node_id, direction, types)
218 .into_iter()
219 .map(|(relationship, other_node)| ExpandedRelationship {
220 relationship,
221 other_node,
222 })
223 .collect()
224 }
225
226 fn neighbors(
227 &self,
228 node_id: NodeId,
229 direction: Direction,
230 types: &[String],
231 ) -> Vec<NodeRecord> {
232 self.expand_ids(node_id, direction, types)
233 .into_iter()
234 .filter_map(|(_, nid)| self.node(nid))
235 .collect()
236 }
237
238 fn node_has_label(&self, node_id: NodeId, label: &str) -> bool
241 where
242 Self: Sized,
243 {
244 self.with_node(node_id, |n| n.labels.iter().any(|l| l == label))
245 .unwrap_or(false)
246 }
247
248 fn node_labels(&self, node_id: NodeId) -> Option<Vec<String>>
249 where
250 Self: Sized,
251 {
252 self.with_node(node_id, |n| n.labels.clone())
253 }
254
255 fn node_properties(&self, node_id: NodeId) -> Option<Properties>
256 where
257 Self: Sized,
258 {
259 self.with_node(node_id, |n| n.properties.clone())
260 }
261
262 fn node_property(&self, node_id: NodeId, key: &str) -> Option<PropertyValue>
263 where
264 Self: Sized,
265 {
266 self.with_node(node_id, |n| n.properties.get(key).cloned())
267 .flatten()
268 }
269
270 fn relationship_type(&self, rel_id: RelationshipId) -> Option<String>
273 where
274 Self: Sized,
275 {
276 self.with_relationship(rel_id, |r| r.rel_type.clone())
277 }
278
279 fn relationship_properties(&self, rel_id: RelationshipId) -> Option<Properties>
280 where
281 Self: Sized,
282 {
283 self.with_relationship(rel_id, |r| r.properties.clone())
284 }
285
286 fn relationship_property(&self, rel_id: RelationshipId, key: &str) -> Option<PropertyValue>
287 where
288 Self: Sized,
289 {
290 self.with_relationship(rel_id, |r| r.properties.get(key).cloned())
291 .flatten()
292 }
293
294 fn relationship_source(&self, rel_id: RelationshipId) -> Option<NodeId> {
295 self.relationship_endpoints(rel_id).map(|(s, _)| s)
296 }
297
298 fn relationship_target(&self, rel_id: RelationshipId) -> Option<NodeId> {
299 self.relationship_endpoints(rel_id).map(|(_, d)| d)
300 }
301
302 fn other_node(&self, rel_id: RelationshipId, node_id: NodeId) -> Option<NodeId> {
303 let (src, dst) = self.relationship_endpoints(rel_id)?;
304 if src == node_id {
305 Some(dst)
306 } else if dst == node_id {
307 Some(src)
308 } else {
309 None
310 }
311 }
312
313 fn has_label_name(&self, label: &str) -> bool {
316 self.all_labels().iter().any(|l| l == label)
317 }
318
319 fn has_relationship_type_name(&self, rel_type: &str) -> bool {
320 self.all_relationship_types().iter().any(|t| t == rel_type)
321 }
322
323 fn all_node_property_keys(&self) -> Vec<String>
324 where
325 Self: Sized,
326 {
327 let mut keys = BTreeSet::new();
328 for id in self.all_node_ids() {
329 self.with_node(id, |n| {
330 for key in n.properties.keys() {
331 keys.insert(key.clone());
332 }
333 });
334 }
335 keys.into_iter().collect()
336 }
337
338 fn all_relationship_property_keys(&self) -> Vec<String>
339 where
340 Self: Sized,
341 {
342 let mut keys = BTreeSet::new();
343 for id in self.all_rel_ids() {
344 self.with_relationship(id, |r| {
345 for key in r.properties.keys() {
346 keys.insert(key.clone());
347 }
348 });
349 }
350 keys.into_iter().collect()
351 }
352
353 fn all_property_keys(&self) -> Vec<String>
354 where
355 Self: Sized,
356 {
357 let mut keys = BTreeSet::new();
358 for key in self.all_node_property_keys() {
359 keys.insert(key);
360 }
361 for key in self.all_relationship_property_keys() {
362 keys.insert(key);
363 }
364 keys.into_iter().collect()
365 }
366
367 fn has_property_key(&self, key: &str) -> bool
368 where
369 Self: Sized,
370 {
371 self.all_node_property_keys().iter().any(|k| k == key)
372 || self
373 .all_relationship_property_keys()
374 .iter()
375 .any(|k| k == key)
376 }
377
378 fn label_property_keys(&self, label: &str) -> Vec<String>
379 where
380 Self: Sized,
381 {
382 let mut keys = BTreeSet::new();
383 for id in self.node_ids_by_label(label) {
384 self.with_node(id, |n| {
385 for key in n.properties.keys() {
386 keys.insert(key.clone());
387 }
388 });
389 }
390 keys.into_iter().collect()
391 }
392
393 fn rel_type_property_keys(&self, rel_type: &str) -> Vec<String>
394 where
395 Self: Sized,
396 {
397 let mut keys = BTreeSet::new();
398 for id in self.rel_ids_by_type(rel_type) {
399 self.with_relationship(id, |r| {
400 for key in r.properties.keys() {
401 keys.insert(key.clone());
402 }
403 });
404 }
405 keys.into_iter().collect()
406 }
407
408 fn label_has_property_key(&self, label: &str, key: &str) -> bool
409 where
410 Self: Sized,
411 {
412 self.node_ids_by_label(label).into_iter().any(|id| {
413 self.with_node(id, |n| n.properties.contains_key(key))
414 .unwrap_or(false)
415 })
416 }
417
418 fn rel_type_has_property_key(&self, rel_type: &str, key: &str) -> bool
419 where
420 Self: Sized,
421 {
422 self.rel_ids_by_type(rel_type).into_iter().any(|id| {
423 self.with_relationship(id, |r| r.properties.contains_key(key))
424 .unwrap_or(false)
425 })
426 }
427
428 fn find_nodes_by_property(
431 &self,
432 label: Option<&str>,
433 key: &str,
434 value: &PropertyValue,
435 ) -> Vec<NodeRecord>
436 where
437 Self: Sized,
438 {
439 let ids = match label {
440 Some(label) => self.node_ids_by_label(label),
441 None => self.all_node_ids(),
442 };
443
444 ids.into_iter()
445 .filter_map(|id| {
446 let matches = self
447 .with_node(id, |n| n.properties.get(key) == Some(value))
448 .unwrap_or(false);
449 if matches {
450 self.node(id)
451 } else {
452 None
453 }
454 })
455 .collect()
456 }
457
458 fn find_node_ids_by_property(
459 &self,
460 label: Option<&str>,
461 key: &str,
462 value: &PropertyValue,
463 ) -> Vec<NodeId>
464 where
465 Self: Sized,
466 {
467 self.find_nodes_by_property(label, key, value)
468 .into_iter()
469 .map(|n| n.id)
470 .collect()
471 }
472
473 fn find_relationships_by_property(
474 &self,
475 rel_type: Option<&str>,
476 key: &str,
477 value: &PropertyValue,
478 ) -> Vec<RelationshipRecord>
479 where
480 Self: Sized,
481 {
482 let ids = match rel_type {
483 Some(rel_type) => self.rel_ids_by_type(rel_type),
484 None => self.all_rel_ids(),
485 };
486
487 ids.into_iter()
488 .filter_map(|id| {
489 let matches = self
490 .with_relationship(id, |r| r.properties.get(key) == Some(value))
491 .unwrap_or(false);
492 if matches {
493 self.relationship(id)
494 } else {
495 None
496 }
497 })
498 .collect()
499 }
500
501 fn find_relationship_ids_by_property(
502 &self,
503 rel_type: Option<&str>,
504 key: &str,
505 value: &PropertyValue,
506 ) -> Vec<RelationshipId>
507 where
508 Self: Sized,
509 {
510 self.find_relationships_by_property(rel_type, key, value)
511 .into_iter()
512 .map(|r| r.id)
513 .collect()
514 }
515
516 fn node_exists_with_label_and_property(
517 &self,
518 label: &str,
519 key: &str,
520 value: &PropertyValue,
521 ) -> bool
522 where
523 Self: Sized,
524 {
525 self.node_ids_by_label(label).into_iter().any(|id| {
526 self.with_node(id, |n| n.properties.get(key) == Some(value))
527 .unwrap_or(false)
528 })
529 }
530
531 fn relationship_exists_with_type_and_property(
532 &self,
533 rel_type: &str,
534 key: &str,
535 value: &PropertyValue,
536 ) -> bool
537 where
538 Self: Sized,
539 {
540 self.rel_ids_by_type(rel_type).into_iter().any(|id| {
541 self.with_relationship(id, |r| r.properties.get(key) == Some(value))
542 .unwrap_or(false)
543 })
544 }
545}
546
547pub trait GraphCatalog {
555 fn node_count(&self) -> usize;
556 fn relationship_count(&self) -> usize;
557 fn has_label_name(&self, label: &str) -> bool;
558 fn has_relationship_type_name(&self, rel_type: &str) -> bool;
559 fn has_property_key(&self, key: &str) -> bool;
560}
561
562impl<T: GraphStorage> GraphCatalog for T {
563 fn node_count(&self) -> usize {
564 GraphStorage::node_count(self)
565 }
566 fn relationship_count(&self) -> usize {
567 GraphStorage::relationship_count(self)
568 }
569 fn has_label_name(&self, label: &str) -> bool {
570 GraphStorage::has_label_name(self, label)
571 }
572 fn has_relationship_type_name(&self, rel_type: &str) -> bool {
573 GraphStorage::has_relationship_type_name(self, rel_type)
574 }
575 fn has_property_key(&self, key: &str) -> bool {
576 GraphStorage::has_property_key(self, key)
577 }
578}
579
580pub trait BorrowedGraphStorage: GraphStorage {
591 fn node_ref(&self, id: NodeId) -> Option<&NodeRecord>;
592 fn relationship_ref(&self, id: RelationshipId) -> Option<&RelationshipRecord>;
593
594 fn node_refs(&self) -> Box<dyn Iterator<Item = &NodeRecord> + '_> {
595 Box::new(
596 self.all_node_ids()
597 .into_iter()
598 .filter_map(|id| self.node_ref(id)),
599 )
600 }
601
602 fn node_refs_by_label(&self, label: &str) -> Box<dyn Iterator<Item = &NodeRecord> + '_> {
603 Box::new(
604 self.node_ids_by_label(label)
605 .into_iter()
606 .filter_map(|id| self.node_ref(id)),
607 )
608 }
609
610 fn relationship_refs(&self) -> Box<dyn Iterator<Item = &RelationshipRecord> + '_> {
611 Box::new(
612 self.all_rel_ids()
613 .into_iter()
614 .filter_map(|id| self.relationship_ref(id)),
615 )
616 }
617
618 fn relationship_refs_by_type(
619 &self,
620 rel_type: &str,
621 ) -> Box<dyn Iterator<Item = &RelationshipRecord> + '_> {
622 Box::new(
623 self.rel_ids_by_type(rel_type)
624 .into_iter()
625 .filter_map(|id| self.relationship_ref(id)),
626 )
627 }
628}
629
630pub trait GraphStorageMut: GraphStorage {
640 fn create_node(&mut self, labels: Vec<String>, properties: Properties) -> NodeRecord;
643
644 fn create_relationship(
645 &mut self,
646 src: NodeId,
647 dst: NodeId,
648 rel_type: &str,
649 properties: Properties,
650 ) -> Option<RelationshipRecord>;
651
652 fn set_node_property(&mut self, node_id: NodeId, key: String, value: PropertyValue) -> bool;
655
656 fn remove_node_property(&mut self, node_id: NodeId, key: &str) -> bool;
657
658 fn add_node_label(&mut self, node_id: NodeId, label: &str) -> bool;
659 fn remove_node_label(&mut self, node_id: NodeId, label: &str) -> bool;
660
661 fn set_relationship_property(
664 &mut self,
665 rel_id: RelationshipId,
666 key: String,
667 value: PropertyValue,
668 ) -> bool;
669
670 fn remove_relationship_property(&mut self, rel_id: RelationshipId, key: &str) -> bool;
671
672 fn delete_relationship(&mut self, rel_id: RelationshipId) -> bool;
675
676 fn delete_node(&mut self, node_id: NodeId) -> bool;
678
679 fn detach_delete_node(&mut self, node_id: NodeId) -> bool;
681
682 fn clear(&mut self);
691
692 fn replace_node_properties(&mut self, node_id: NodeId, properties: Properties) -> bool
695 where
696 Self: Sized,
697 {
698 if !self.contains_node(node_id) {
699 return false;
700 }
701
702 let existing_keys = match self.node_properties(node_id) {
703 Some(props) => props.into_keys().collect::<Vec<_>>(),
704 None => return false,
705 };
706
707 for key in existing_keys {
708 self.remove_node_property(node_id, &key);
709 }
710
711 for (k, v) in properties {
712 self.set_node_property(node_id, k, v);
713 }
714
715 true
716 }
717
718 fn merge_node_properties(&mut self, node_id: NodeId, properties: Properties) -> bool {
719 if !self.contains_node(node_id) {
720 return false;
721 }
722
723 for (k, v) in properties {
724 self.set_node_property(node_id, k, v);
725 }
726
727 true
728 }
729
730 fn set_node_labels(&mut self, node_id: NodeId, labels: Vec<String>) -> bool
731 where
732 Self: Sized,
733 {
734 if !self.contains_node(node_id) {
735 return false;
736 }
737
738 let current = match self.node_labels(node_id) {
739 Some(labels) => labels,
740 None => return false,
741 };
742
743 for label in ¤t {
744 self.remove_node_label(node_id, label);
745 }
746
747 for label in &labels {
748 self.add_node_label(node_id, label);
749 }
750
751 true
752 }
753
754 fn replace_relationship_properties(
755 &mut self,
756 rel_id: RelationshipId,
757 properties: Properties,
758 ) -> bool
759 where
760 Self: Sized,
761 {
762 if !self.contains_relationship(rel_id) {
763 return false;
764 }
765
766 let existing_keys = match self.relationship_properties(rel_id) {
767 Some(props) => props.into_keys().collect::<Vec<_>>(),
768 None => return false,
769 };
770
771 for key in existing_keys {
772 self.remove_relationship_property(rel_id, &key);
773 }
774
775 for (k, v) in properties {
776 self.set_relationship_property(rel_id, k, v);
777 }
778
779 true
780 }
781
782 fn merge_relationship_properties(
783 &mut self,
784 rel_id: RelationshipId,
785 properties: Properties,
786 ) -> bool {
787 if !self.contains_relationship(rel_id) {
788 return false;
789 }
790
791 for (k, v) in properties {
792 self.set_relationship_property(rel_id, k, v);
793 }
794
795 true
796 }
797
798 fn delete_relationships_of(&mut self, node_id: NodeId, direction: Direction) -> usize {
799 let rel_ids = self.relationship_ids_of(node_id, direction);
800
801 let mut deleted = 0;
802 for rel_id in rel_ids {
803 if self.delete_relationship(rel_id) {
804 deleted += 1;
805 }
806 }
807 deleted
808 }
809
810 fn get_or_create_node(
811 &mut self,
812 labels: Vec<String>,
813 match_key: &str,
814 match_value: &PropertyValue,
815 init_properties: Properties,
816 ) -> NodeRecord
817 where
818 Self: Sized,
819 {
820 for label in &labels {
821 let matches = self.find_nodes_by_property(Some(label), match_key, match_value);
822 if let Some(node) = matches.into_iter().next() {
823 return node;
824 }
825 }
826
827 self.create_node(labels, init_properties)
828 }
829}