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