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_relationships_by_property(
532 &self,
533 rel_type: Option<&str>,
534 key: &str,
535 value: &PropertyValue,
536 ) -> Vec<RelationshipRecord>
537 where
538 Self: Sized,
539 {
540 let ids = match rel_type {
541 Some(rel_type) => self.rel_ids_by_type(rel_type),
542 None => self.all_rel_ids(),
543 };
544
545 ids.into_iter()
546 .filter_map(|id| {
547 let matches = self
548 .with_relationship(id, |r| r.properties.get(key) == Some(value))
549 .unwrap_or(false);
550 if matches {
551 self.relationship(id)
552 } else {
553 None
554 }
555 })
556 .collect()
557 }
558
559 fn node_exists_with_label_and_property(
560 &self,
561 label: &str,
562 key: &str,
563 value: &PropertyValue,
564 ) -> bool
565 where
566 Self: Sized,
567 {
568 self.node_ids_by_label(label).into_iter().any(|id| {
569 self.with_node(id, |n| n.properties.get(key) == Some(value))
570 .unwrap_or(false)
571 })
572 }
573
574 fn relationship_exists_with_type_and_property(
575 &self,
576 rel_type: &str,
577 key: &str,
578 value: &PropertyValue,
579 ) -> bool
580 where
581 Self: Sized,
582 {
583 self.rel_ids_by_type(rel_type).into_iter().any(|id| {
584 self.with_relationship(id, |r| r.properties.get(key) == Some(value))
585 .unwrap_or(false)
586 })
587 }
588}
589
590pub trait GraphCatalog {
598 fn node_count(&self) -> usize;
599 fn relationship_count(&self) -> usize;
600 fn has_label_name(&self, label: &str) -> bool;
601 fn has_relationship_type_name(&self, rel_type: &str) -> bool;
602 fn has_property_key(&self, key: &str) -> bool;
603}
604
605impl<T: GraphStorage> GraphCatalog for T {
606 fn node_count(&self) -> usize {
607 GraphStorage::node_count(self)
608 }
609 fn relationship_count(&self) -> usize {
610 GraphStorage::relationship_count(self)
611 }
612 fn has_label_name(&self, label: &str) -> bool {
613 GraphStorage::has_label_name(self, label)
614 }
615 fn has_relationship_type_name(&self, rel_type: &str) -> bool {
616 GraphStorage::has_relationship_type_name(self, rel_type)
617 }
618 fn has_property_key(&self, key: &str) -> bool {
619 GraphStorage::has_property_key(self, key)
620 }
621}
622
623pub trait BorrowedGraphStorage: GraphStorage {
634 fn node_ref(&self, id: NodeId) -> Option<&NodeRecord>;
635 fn relationship_ref(&self, id: RelationshipId) -> Option<&RelationshipRecord>;
636}
637
638pub trait GraphStorageMut: GraphStorage {
648 fn create_node(&mut self, labels: Vec<String>, properties: Properties) -> NodeRecord;
651
652 fn create_relationship(
653 &mut self,
654 src: NodeId,
655 dst: NodeId,
656 rel_type: &str,
657 properties: Properties,
658 ) -> Option<RelationshipRecord>;
659
660 fn set_node_property(&mut self, node_id: NodeId, key: String, value: PropertyValue) -> bool;
663
664 fn remove_node_property(&mut self, node_id: NodeId, key: &str) -> bool;
665
666 fn add_node_label(&mut self, node_id: NodeId, label: &str) -> bool;
667 fn remove_node_label(&mut self, node_id: NodeId, label: &str) -> bool;
668
669 fn set_relationship_property(
672 &mut self,
673 rel_id: RelationshipId,
674 key: String,
675 value: PropertyValue,
676 ) -> bool;
677
678 fn remove_relationship_property(&mut self, rel_id: RelationshipId, key: &str) -> bool;
679
680 fn delete_relationship(&mut self, rel_id: RelationshipId) -> bool;
683
684 fn delete_node(&mut self, node_id: NodeId) -> bool;
686
687 fn detach_delete_node(&mut self, node_id: NodeId) -> bool;
689
690 fn clear(&mut self);
699
700 fn replace_node_properties(&mut self, node_id: NodeId, properties: Properties) -> bool
703 where
704 Self: Sized,
705 {
706 if !self.contains_node(node_id) {
707 return false;
708 }
709
710 let existing_keys = match self.node_properties(node_id) {
711 Some(props) => props.into_keys().collect::<Vec<_>>(),
712 None => return false,
713 };
714
715 for key in existing_keys {
716 self.remove_node_property(node_id, &key);
717 }
718
719 for (k, v) in properties {
720 self.set_node_property(node_id, k, v);
721 }
722
723 true
724 }
725
726 fn merge_node_properties(&mut self, node_id: NodeId, properties: Properties) -> bool {
727 if !self.contains_node(node_id) {
728 return false;
729 }
730
731 for (k, v) in properties {
732 self.set_node_property(node_id, k, v);
733 }
734
735 true
736 }
737
738 fn set_node_labels(&mut self, node_id: NodeId, labels: Vec<String>) -> bool
739 where
740 Self: Sized,
741 {
742 if !self.contains_node(node_id) {
743 return false;
744 }
745
746 let current = match self.node_labels(node_id) {
747 Some(labels) => labels,
748 None => return false,
749 };
750
751 for label in ¤t {
752 self.remove_node_label(node_id, label);
753 }
754
755 for label in &labels {
756 self.add_node_label(node_id, label);
757 }
758
759 true
760 }
761
762 fn replace_relationship_properties(
763 &mut self,
764 rel_id: RelationshipId,
765 properties: Properties,
766 ) -> bool
767 where
768 Self: Sized,
769 {
770 if !self.contains_relationship(rel_id) {
771 return false;
772 }
773
774 let existing_keys = match self.relationship_properties(rel_id) {
775 Some(props) => props.into_keys().collect::<Vec<_>>(),
776 None => return false,
777 };
778
779 for key in existing_keys {
780 self.remove_relationship_property(rel_id, &key);
781 }
782
783 for (k, v) in properties {
784 self.set_relationship_property(rel_id, k, v);
785 }
786
787 true
788 }
789
790 fn merge_relationship_properties(
791 &mut self,
792 rel_id: RelationshipId,
793 properties: Properties,
794 ) -> bool {
795 if !self.contains_relationship(rel_id) {
796 return false;
797 }
798
799 for (k, v) in properties {
800 self.set_relationship_property(rel_id, k, v);
801 }
802
803 true
804 }
805
806 fn delete_relationships_of(&mut self, node_id: NodeId, direction: Direction) -> usize {
807 let rel_ids = self.relationship_ids_of(node_id, direction);
808
809 let mut deleted = 0;
810 for rel_id in rel_ids {
811 if self.delete_relationship(rel_id) {
812 deleted += 1;
813 }
814 }
815 deleted
816 }
817
818 fn get_or_create_node(
819 &mut self,
820 labels: Vec<String>,
821 match_key: &str,
822 match_value: &PropertyValue,
823 init_properties: Properties,
824 ) -> NodeRecord
825 where
826 Self: Sized,
827 {
828 for label in &labels {
829 let matches = self.find_nodes_by_property(Some(label), match_key, match_value);
830 if let Some(node) = matches.into_iter().next() {
831 return node;
832 }
833 }
834
835 self.create_node(labels, init_properties)
836 }
837}