1use crate::binary::LoraBinary;
2use crate::spatial::LoraPoint;
3use crate::temporal::{
4 LoraDate, LoraDateTime, LoraDuration, LoraLocalDateTime, LoraLocalTime, LoraTime,
5};
6use crate::vector::LoraVector;
7use lora_ast::Direction;
8use serde::{Deserialize, Serialize};
9use std::collections::{BTreeMap, BTreeSet};
10
11pub type NodeId = u64;
12pub type RelationshipId = u64;
13
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15pub enum PropertyValue {
16 Null,
17 Bool(bool),
18 Int(i64),
19 Float(f64),
20 String(String),
21 Binary(LoraBinary),
22 List(Vec<PropertyValue>),
23 Map(BTreeMap<String, PropertyValue>),
24 Date(LoraDate),
25 Time(LoraTime),
26 LocalTime(LoraLocalTime),
27 DateTime(LoraDateTime),
28 LocalDateTime(LoraLocalDateTime),
29 Duration(LoraDuration),
30 Point(LoraPoint),
31 Vector(LoraVector),
32}
33
34pub type Properties = BTreeMap<String, PropertyValue>;
35
36#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
37pub struct NodeRecord {
38 pub id: NodeId,
39 pub labels: Vec<String>,
40 pub properties: Properties,
41}
42
43impl NodeRecord {
44 pub fn has_label(&self, label: &str) -> bool {
45 self.labels.iter().any(|l| l == label)
46 }
47
48 pub fn property(&self, key: &str) -> Option<&PropertyValue> {
49 self.properties.get(key)
50 }
51}
52
53#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
54pub struct RelationshipRecord {
55 pub id: RelationshipId,
56 pub src: NodeId,
57 pub dst: NodeId,
58 pub rel_type: String,
59 pub properties: Properties,
60}
61
62impl RelationshipRecord {
63 pub fn property(&self, key: &str) -> Option<&PropertyValue> {
64 self.properties.get(key)
65 }
66
67 pub fn other_node(&self, node_id: NodeId) -> Option<NodeId> {
68 if self.src == node_id {
69 Some(self.dst)
70 } else if self.dst == node_id {
71 Some(self.src)
72 } else {
73 None
74 }
75 }
76
77 pub fn matches_direction_from(&self, node_id: NodeId, direction: Direction) -> bool {
78 match direction {
79 Direction::Right => self.src == node_id,
80 Direction::Left => self.dst == node_id,
81 Direction::Undirected => self.src == node_id || self.dst == node_id,
82 }
83 }
84}
85
86#[derive(Debug, Clone, PartialEq)]
87pub struct ExpandedRelationship {
88 pub relationship: RelationshipRecord,
89 pub other_node: NodeRecord,
90}
91
92pub trait GraphStorage {
108 fn contains_node(&self, id: NodeId) -> bool;
112
113 fn node(&self, id: NodeId) -> Option<NodeRecord>;
117
118 fn all_node_ids(&self) -> Vec<NodeId>;
120
121 fn node_ids_by_label(&self, label: &str) -> Vec<NodeId>;
124
125 fn contains_relationship(&self, id: RelationshipId) -> bool;
128
129 fn relationship(&self, id: RelationshipId) -> Option<RelationshipRecord>;
130
131 fn all_rel_ids(&self) -> Vec<RelationshipId>;
132
133 fn rel_ids_by_type(&self, rel_type: &str) -> Vec<RelationshipId>;
134
135 fn relationship_endpoints(&self, id: RelationshipId) -> Option<(NodeId, NodeId)>;
139
140 fn expand_ids(
146 &self,
147 node_id: NodeId,
148 direction: Direction,
149 types: &[String],
150 ) -> Vec<(RelationshipId, NodeId)>;
151
152 fn all_labels(&self) -> Vec<String>;
155 fn all_relationship_types(&self) -> Vec<String>;
156
157 fn with_node<F, R>(&self, id: NodeId, f: F) -> Option<R>
164 where
165 F: FnOnce(&NodeRecord) -> R,
166 Self: Sized,
167 {
168 self.node(id).as_ref().map(f)
169 }
170
171 fn with_relationship<F, R>(&self, id: RelationshipId, f: F) -> Option<R>
172 where
173 F: FnOnce(&RelationshipRecord) -> R,
174 Self: Sized,
175 {
176 self.relationship(id).as_ref().map(f)
177 }
178
179 fn has_node(&self, id: NodeId) -> bool {
182 self.contains_node(id)
183 }
184
185 fn has_relationship(&self, id: RelationshipId) -> bool {
186 self.contains_relationship(id)
187 }
188
189 fn node_count(&self) -> usize {
190 self.all_node_ids().len()
191 }
192
193 fn relationship_count(&self) -> usize {
194 self.all_rel_ids().len()
195 }
196
197 fn all_nodes(&self) -> Vec<NodeRecord> {
205 self.all_node_ids()
206 .into_iter()
207 .filter_map(|id| self.node(id))
208 .collect()
209 }
210
211 fn nodes_by_label(&self, label: &str) -> Vec<NodeRecord> {
212 self.node_ids_by_label(label)
213 .into_iter()
214 .filter_map(|id| self.node(id))
215 .collect()
216 }
217
218 fn all_relationships(&self) -> Vec<RelationshipRecord> {
219 self.all_rel_ids()
220 .into_iter()
221 .filter_map(|id| self.relationship(id))
222 .collect()
223 }
224
225 fn relationships_by_type(&self, rel_type: &str) -> Vec<RelationshipRecord> {
226 self.rel_ids_by_type(rel_type)
227 .into_iter()
228 .filter_map(|id| self.relationship(id))
229 .collect()
230 }
231
232 fn relationship_ids_of(&self, node_id: NodeId, direction: Direction) -> Vec<RelationshipId> {
235 self.expand_ids(node_id, direction, &[])
236 .into_iter()
237 .map(|(rel_id, _)| rel_id)
238 .collect()
239 }
240
241 fn outgoing_relationships(&self, node_id: NodeId) -> Vec<RelationshipRecord> {
242 self.relationship_ids_of(node_id, Direction::Right)
243 .into_iter()
244 .filter_map(|id| self.relationship(id))
245 .collect()
246 }
247
248 fn incoming_relationships(&self, node_id: NodeId) -> Vec<RelationshipRecord> {
249 self.relationship_ids_of(node_id, Direction::Left)
250 .into_iter()
251 .filter_map(|id| self.relationship(id))
252 .collect()
253 }
254
255 fn relationships_of(&self, node_id: NodeId, direction: Direction) -> Vec<RelationshipRecord> {
256 self.relationship_ids_of(node_id, direction)
257 .into_iter()
258 .filter_map(|id| self.relationship(id))
259 .collect()
260 }
261
262 fn degree(&self, node_id: NodeId, direction: Direction) -> usize {
263 self.expand_ids(node_id, direction, &[]).len()
264 }
265
266 fn is_isolated(&self, node_id: NodeId) -> bool {
267 self.degree(node_id, Direction::Undirected) == 0
268 }
269
270 fn expand(
271 &self,
272 node_id: NodeId,
273 direction: Direction,
274 types: &[String],
275 ) -> Vec<(RelationshipRecord, NodeRecord)> {
276 self.expand_ids(node_id, direction, types)
277 .into_iter()
278 .filter_map(|(rid, nid)| {
279 let rel = self.relationship(rid)?;
280 let node = self.node(nid)?;
281 Some((rel, node))
282 })
283 .collect()
284 }
285
286 fn expand_detailed(
287 &self,
288 node_id: NodeId,
289 direction: Direction,
290 types: &[String],
291 ) -> Vec<ExpandedRelationship> {
292 self.expand(node_id, direction, types)
293 .into_iter()
294 .map(|(relationship, other_node)| ExpandedRelationship {
295 relationship,
296 other_node,
297 })
298 .collect()
299 }
300
301 fn neighbors(
302 &self,
303 node_id: NodeId,
304 direction: Direction,
305 types: &[String],
306 ) -> Vec<NodeRecord> {
307 self.expand_ids(node_id, direction, types)
308 .into_iter()
309 .filter_map(|(_, nid)| self.node(nid))
310 .collect()
311 }
312
313 fn node_has_label(&self, node_id: NodeId, label: &str) -> bool
316 where
317 Self: Sized,
318 {
319 self.with_node(node_id, |n| n.labels.iter().any(|l| l == label))
320 .unwrap_or(false)
321 }
322
323 fn node_labels(&self, node_id: NodeId) -> Option<Vec<String>>
324 where
325 Self: Sized,
326 {
327 self.with_node(node_id, |n| n.labels.clone())
328 }
329
330 fn node_properties(&self, node_id: NodeId) -> Option<Properties>
331 where
332 Self: Sized,
333 {
334 self.with_node(node_id, |n| n.properties.clone())
335 }
336
337 fn node_property(&self, node_id: NodeId, key: &str) -> Option<PropertyValue>
338 where
339 Self: Sized,
340 {
341 self.with_node(node_id, |n| n.properties.get(key).cloned())
342 .flatten()
343 }
344
345 fn relationship_type(&self, rel_id: RelationshipId) -> Option<String>
348 where
349 Self: Sized,
350 {
351 self.with_relationship(rel_id, |r| r.rel_type.clone())
352 }
353
354 fn relationship_properties(&self, rel_id: RelationshipId) -> Option<Properties>
355 where
356 Self: Sized,
357 {
358 self.with_relationship(rel_id, |r| r.properties.clone())
359 }
360
361 fn relationship_property(&self, rel_id: RelationshipId, key: &str) -> Option<PropertyValue>
362 where
363 Self: Sized,
364 {
365 self.with_relationship(rel_id, |r| r.properties.get(key).cloned())
366 .flatten()
367 }
368
369 fn relationship_source(&self, rel_id: RelationshipId) -> Option<NodeId> {
370 self.relationship_endpoints(rel_id).map(|(s, _)| s)
371 }
372
373 fn relationship_target(&self, rel_id: RelationshipId) -> Option<NodeId> {
374 self.relationship_endpoints(rel_id).map(|(_, d)| d)
375 }
376
377 fn other_node(&self, rel_id: RelationshipId, node_id: NodeId) -> Option<NodeId> {
378 let (src, dst) = self.relationship_endpoints(rel_id)?;
379 if src == node_id {
380 Some(dst)
381 } else if dst == node_id {
382 Some(src)
383 } else {
384 None
385 }
386 }
387
388 fn has_label_name(&self, label: &str) -> bool {
391 self.all_labels().iter().any(|l| l == label)
392 }
393
394 fn has_relationship_type_name(&self, rel_type: &str) -> bool {
395 self.all_relationship_types().iter().any(|t| t == rel_type)
396 }
397
398 fn all_node_property_keys(&self) -> Vec<String>
399 where
400 Self: Sized,
401 {
402 let mut keys = BTreeSet::new();
403 for id in self.all_node_ids() {
404 self.with_node(id, |n| {
405 for key in n.properties.keys() {
406 keys.insert(key.clone());
407 }
408 });
409 }
410 keys.into_iter().collect()
411 }
412
413 fn all_relationship_property_keys(&self) -> Vec<String>
414 where
415 Self: Sized,
416 {
417 let mut keys = BTreeSet::new();
418 for id in self.all_rel_ids() {
419 self.with_relationship(id, |r| {
420 for key in r.properties.keys() {
421 keys.insert(key.clone());
422 }
423 });
424 }
425 keys.into_iter().collect()
426 }
427
428 fn all_property_keys(&self) -> Vec<String>
429 where
430 Self: Sized,
431 {
432 let mut keys = BTreeSet::new();
433 for key in self.all_node_property_keys() {
434 keys.insert(key);
435 }
436 for key in self.all_relationship_property_keys() {
437 keys.insert(key);
438 }
439 keys.into_iter().collect()
440 }
441
442 fn has_property_key(&self, key: &str) -> bool
443 where
444 Self: Sized,
445 {
446 self.all_node_property_keys().iter().any(|k| k == key)
447 || self
448 .all_relationship_property_keys()
449 .iter()
450 .any(|k| k == key)
451 }
452
453 fn label_property_keys(&self, label: &str) -> Vec<String>
454 where
455 Self: Sized,
456 {
457 let mut keys = BTreeSet::new();
458 for id in self.node_ids_by_label(label) {
459 self.with_node(id, |n| {
460 for key in n.properties.keys() {
461 keys.insert(key.clone());
462 }
463 });
464 }
465 keys.into_iter().collect()
466 }
467
468 fn rel_type_property_keys(&self, rel_type: &str) -> Vec<String>
469 where
470 Self: Sized,
471 {
472 let mut keys = BTreeSet::new();
473 for id in self.rel_ids_by_type(rel_type) {
474 self.with_relationship(id, |r| {
475 for key in r.properties.keys() {
476 keys.insert(key.clone());
477 }
478 });
479 }
480 keys.into_iter().collect()
481 }
482
483 fn label_has_property_key(&self, label: &str, key: &str) -> bool
484 where
485 Self: Sized,
486 {
487 self.node_ids_by_label(label).into_iter().any(|id| {
488 self.with_node(id, |n| n.properties.contains_key(key))
489 .unwrap_or(false)
490 })
491 }
492
493 fn rel_type_has_property_key(&self, rel_type: &str, key: &str) -> bool
494 where
495 Self: Sized,
496 {
497 self.rel_ids_by_type(rel_type).into_iter().any(|id| {
498 self.with_relationship(id, |r| r.properties.contains_key(key))
499 .unwrap_or(false)
500 })
501 }
502
503 fn find_nodes_by_property(
506 &self,
507 label: Option<&str>,
508 key: &str,
509 value: &PropertyValue,
510 ) -> Vec<NodeRecord>
511 where
512 Self: Sized,
513 {
514 let ids = match label {
515 Some(label) => self.node_ids_by_label(label),
516 None => self.all_node_ids(),
517 };
518
519 ids.into_iter()
520 .filter_map(|id| {
521 let matches = self
522 .with_node(id, |n| n.properties.get(key) == Some(value))
523 .unwrap_or(false);
524 if matches {
525 self.node(id)
526 } else {
527 None
528 }
529 })
530 .collect()
531 }
532
533 fn find_node_ids_by_property(
534 &self,
535 label: Option<&str>,
536 key: &str,
537 value: &PropertyValue,
538 ) -> Vec<NodeId>
539 where
540 Self: Sized,
541 {
542 self.find_nodes_by_property(label, key, value)
543 .into_iter()
544 .map(|n| n.id)
545 .collect()
546 }
547
548 fn find_relationships_by_property(
549 &self,
550 rel_type: Option<&str>,
551 key: &str,
552 value: &PropertyValue,
553 ) -> Vec<RelationshipRecord>
554 where
555 Self: Sized,
556 {
557 let ids = match rel_type {
558 Some(rel_type) => self.rel_ids_by_type(rel_type),
559 None => self.all_rel_ids(),
560 };
561
562 ids.into_iter()
563 .filter_map(|id| {
564 let matches = self
565 .with_relationship(id, |r| r.properties.get(key) == Some(value))
566 .unwrap_or(false);
567 if matches {
568 self.relationship(id)
569 } else {
570 None
571 }
572 })
573 .collect()
574 }
575
576 fn find_relationship_ids_by_property(
577 &self,
578 rel_type: Option<&str>,
579 key: &str,
580 value: &PropertyValue,
581 ) -> Vec<RelationshipId>
582 where
583 Self: Sized,
584 {
585 self.find_relationships_by_property(rel_type, key, value)
586 .into_iter()
587 .map(|r| r.id)
588 .collect()
589 }
590
591 fn node_exists_with_label_and_property(
592 &self,
593 label: &str,
594 key: &str,
595 value: &PropertyValue,
596 ) -> bool
597 where
598 Self: Sized,
599 {
600 self.node_ids_by_label(label).into_iter().any(|id| {
601 self.with_node(id, |n| n.properties.get(key) == Some(value))
602 .unwrap_or(false)
603 })
604 }
605
606 fn relationship_exists_with_type_and_property(
607 &self,
608 rel_type: &str,
609 key: &str,
610 value: &PropertyValue,
611 ) -> bool
612 where
613 Self: Sized,
614 {
615 self.rel_ids_by_type(rel_type).into_iter().any(|id| {
616 self.with_relationship(id, |r| r.properties.get(key) == Some(value))
617 .unwrap_or(false)
618 })
619 }
620}
621
622pub trait GraphCatalog {
630 fn node_count(&self) -> usize;
631 fn relationship_count(&self) -> usize;
632 fn has_label_name(&self, label: &str) -> bool;
633 fn has_relationship_type_name(&self, rel_type: &str) -> bool;
634 fn has_property_key(&self, key: &str) -> bool;
635}
636
637impl<T: GraphStorage> GraphCatalog for T {
638 fn node_count(&self) -> usize {
639 GraphStorage::node_count(self)
640 }
641 fn relationship_count(&self) -> usize {
642 GraphStorage::relationship_count(self)
643 }
644 fn has_label_name(&self, label: &str) -> bool {
645 GraphStorage::has_label_name(self, label)
646 }
647 fn has_relationship_type_name(&self, rel_type: &str) -> bool {
648 GraphStorage::has_relationship_type_name(self, rel_type)
649 }
650 fn has_property_key(&self, key: &str) -> bool {
651 GraphStorage::has_property_key(self, key)
652 }
653}
654
655pub trait BorrowedGraphStorage: GraphStorage {
666 fn node_ref(&self, id: NodeId) -> Option<&NodeRecord>;
667 fn relationship_ref(&self, id: RelationshipId) -> Option<&RelationshipRecord>;
668
669 fn node_refs(&self) -> Box<dyn Iterator<Item = &NodeRecord> + '_> {
670 Box::new(
671 self.all_node_ids()
672 .into_iter()
673 .filter_map(|id| self.node_ref(id)),
674 )
675 }
676
677 fn node_refs_by_label(&self, label: &str) -> Box<dyn Iterator<Item = &NodeRecord> + '_> {
678 Box::new(
679 self.node_ids_by_label(label)
680 .into_iter()
681 .filter_map(|id| self.node_ref(id)),
682 )
683 }
684
685 fn relationship_refs(&self) -> Box<dyn Iterator<Item = &RelationshipRecord> + '_> {
686 Box::new(
687 self.all_rel_ids()
688 .into_iter()
689 .filter_map(|id| self.relationship_ref(id)),
690 )
691 }
692
693 fn relationship_refs_by_type(
694 &self,
695 rel_type: &str,
696 ) -> Box<dyn Iterator<Item = &RelationshipRecord> + '_> {
697 Box::new(
698 self.rel_ids_by_type(rel_type)
699 .into_iter()
700 .filter_map(|id| self.relationship_ref(id)),
701 )
702 }
703}
704
705pub trait GraphStorageMut: GraphStorage {
715 fn create_node(&mut self, labels: Vec<String>, properties: Properties) -> NodeRecord;
718
719 fn create_relationship(
720 &mut self,
721 src: NodeId,
722 dst: NodeId,
723 rel_type: &str,
724 properties: Properties,
725 ) -> Option<RelationshipRecord>;
726
727 fn set_node_property(&mut self, node_id: NodeId, key: String, value: PropertyValue) -> bool;
730
731 fn remove_node_property(&mut self, node_id: NodeId, key: &str) -> bool;
732
733 fn add_node_label(&mut self, node_id: NodeId, label: &str) -> bool;
734 fn remove_node_label(&mut self, node_id: NodeId, label: &str) -> bool;
735
736 fn set_relationship_property(
739 &mut self,
740 rel_id: RelationshipId,
741 key: String,
742 value: PropertyValue,
743 ) -> bool;
744
745 fn remove_relationship_property(&mut self, rel_id: RelationshipId, key: &str) -> bool;
746
747 fn delete_relationship(&mut self, rel_id: RelationshipId) -> bool;
750
751 fn delete_node(&mut self, node_id: NodeId) -> bool;
753
754 fn detach_delete_node(&mut self, node_id: NodeId) -> bool;
756
757 fn clear(&mut self);
766
767 fn replace_node_properties(&mut self, node_id: NodeId, properties: Properties) -> bool
770 where
771 Self: Sized,
772 {
773 if !self.contains_node(node_id) {
774 return false;
775 }
776
777 let existing_keys = match self.node_properties(node_id) {
778 Some(props) => props.into_keys().collect::<Vec<_>>(),
779 None => return false,
780 };
781
782 for key in existing_keys {
783 self.remove_node_property(node_id, &key);
784 }
785
786 for (k, v) in properties {
787 self.set_node_property(node_id, k, v);
788 }
789
790 true
791 }
792
793 fn merge_node_properties(&mut self, node_id: NodeId, properties: Properties) -> bool {
794 if !self.contains_node(node_id) {
795 return false;
796 }
797
798 for (k, v) in properties {
799 self.set_node_property(node_id, k, v);
800 }
801
802 true
803 }
804
805 fn set_node_labels(&mut self, node_id: NodeId, labels: Vec<String>) -> bool
806 where
807 Self: Sized,
808 {
809 if !self.contains_node(node_id) {
810 return false;
811 }
812
813 let current = match self.node_labels(node_id) {
814 Some(labels) => labels,
815 None => return false,
816 };
817
818 for label in ¤t {
819 self.remove_node_label(node_id, label);
820 }
821
822 for label in &labels {
823 self.add_node_label(node_id, label);
824 }
825
826 true
827 }
828
829 fn replace_relationship_properties(
830 &mut self,
831 rel_id: RelationshipId,
832 properties: Properties,
833 ) -> bool
834 where
835 Self: Sized,
836 {
837 if !self.contains_relationship(rel_id) {
838 return false;
839 }
840
841 let existing_keys = match self.relationship_properties(rel_id) {
842 Some(props) => props.into_keys().collect::<Vec<_>>(),
843 None => return false,
844 };
845
846 for key in existing_keys {
847 self.remove_relationship_property(rel_id, &key);
848 }
849
850 for (k, v) in properties {
851 self.set_relationship_property(rel_id, k, v);
852 }
853
854 true
855 }
856
857 fn merge_relationship_properties(
858 &mut self,
859 rel_id: RelationshipId,
860 properties: Properties,
861 ) -> bool {
862 if !self.contains_relationship(rel_id) {
863 return false;
864 }
865
866 for (k, v) in properties {
867 self.set_relationship_property(rel_id, k, v);
868 }
869
870 true
871 }
872
873 fn delete_relationships_of(&mut self, node_id: NodeId, direction: Direction) -> usize {
874 let rel_ids = self.relationship_ids_of(node_id, direction);
875
876 let mut deleted = 0;
877 for rel_id in rel_ids {
878 if self.delete_relationship(rel_id) {
879 deleted += 1;
880 }
881 }
882 deleted
883 }
884
885 fn get_or_create_node(
886 &mut self,
887 labels: Vec<String>,
888 match_key: &str,
889 match_value: &PropertyValue,
890 init_properties: Properties,
891 ) -> NodeRecord
892 where
893 Self: Sized,
894 {
895 for label in &labels {
896 let matches = self.find_nodes_by_property(Some(label), match_key, match_value);
897 if let Some(node) = matches.into_iter().next() {
898 return node;
899 }
900 }
901
902 self.create_node(labels, init_properties)
903 }
904}