1use crate::graph::Direction;
23use crate::graph::lpg::CompareOp;
24use crate::graph::lpg::{Edge, Node};
25#[cfg(feature = "vector-index")]
26use crate::index::vector::DistanceMetric;
27use crate::statistics::Statistics;
28use arcstr::ArcStr;
29use grafeo_common::types::{EdgeId, EpochId, NodeId, PropertyKey, TransactionId, Value};
30use grafeo_common::utils::hash::FxHashMap;
31use std::sync::Arc;
32
33pub trait GraphStore: Send + Sync {
45 fn get_node(&self, id: NodeId) -> Option<Node>;
49
50 fn get_edge(&self, id: EdgeId) -> Option<Edge>;
52
53 fn get_node_versioned(
55 &self,
56 id: NodeId,
57 epoch: EpochId,
58 transaction_id: TransactionId,
59 ) -> Option<Node>;
60
61 fn get_edge_versioned(
63 &self,
64 id: EdgeId,
65 epoch: EpochId,
66 transaction_id: TransactionId,
67 ) -> Option<Edge>;
68
69 fn get_node_at_epoch(&self, id: NodeId, epoch: EpochId) -> Option<Node>;
75
76 fn get_edge_at_epoch(&self, id: EdgeId, epoch: EpochId) -> Option<Edge>;
78
79 fn get_node_property(&self, id: NodeId, key: &PropertyKey) -> Option<Value>;
83
84 fn get_edge_property(&self, id: EdgeId, key: &PropertyKey) -> Option<Value>;
86
87 fn get_node_property_batch(&self, ids: &[NodeId], key: &PropertyKey) -> Vec<Option<Value>>;
89
90 fn get_nodes_properties_batch(&self, ids: &[NodeId]) -> Vec<FxHashMap<PropertyKey, Value>>;
92
93 fn get_nodes_properties_selective_batch(
95 &self,
96 ids: &[NodeId],
97 keys: &[PropertyKey],
98 ) -> Vec<FxHashMap<PropertyKey, Value>>;
99
100 fn get_edges_properties_selective_batch(
102 &self,
103 ids: &[EdgeId],
104 keys: &[PropertyKey],
105 ) -> Vec<FxHashMap<PropertyKey, Value>>;
106
107 fn neighbors(&self, node: NodeId, direction: Direction) -> Vec<NodeId>;
114
115 fn edges_from(&self, node: NodeId, direction: Direction) -> Vec<(NodeId, EdgeId)>;
117
118 fn out_degree(&self, node: NodeId) -> usize;
120
121 fn in_degree(&self, node: NodeId) -> usize;
123
124 fn has_backward_adjacency(&self) -> bool;
126
127 fn node_ids(&self) -> Vec<NodeId>;
131
132 fn all_node_ids(&self) -> Vec<NodeId> {
138 self.node_ids()
140 }
141
142 fn nodes_by_label(&self, label: &str) -> Vec<NodeId>;
144
145 fn node_count(&self) -> usize;
147
148 fn edge_count(&self) -> usize;
150
151 fn edge_type(&self, id: EdgeId) -> Option<ArcStr>;
155
156 fn edge_type_versioned(
160 &self,
161 id: EdgeId,
162 epoch: EpochId,
163 transaction_id: TransactionId,
164 ) -> Option<ArcStr> {
165 let _ = (epoch, transaction_id);
166 self.edge_type(id)
167 }
168
169 fn has_property_index(&self, _property: &str) -> bool {
175 false
176 }
177
178 fn find_nodes_by_property(&self, property: &str, value: &Value) -> Vec<NodeId>;
182
183 fn find_nodes_by_properties(&self, conditions: &[(&str, Value)]) -> Vec<NodeId>;
185
186 fn find_nodes_in_range(
188 &self,
189 property: &str,
190 min: Option<&Value>,
191 max: Option<&Value>,
192 min_inclusive: bool,
193 max_inclusive: bool,
194 ) -> Vec<NodeId>;
195
196 fn node_property_might_match(
201 &self,
202 property: &PropertyKey,
203 op: CompareOp,
204 value: &Value,
205 ) -> bool;
206
207 fn edge_property_might_match(
209 &self,
210 property: &PropertyKey,
211 op: CompareOp,
212 value: &Value,
213 ) -> bool;
214
215 fn statistics(&self) -> Arc<Statistics>;
219
220 fn estimate_label_cardinality(&self, label: &str) -> f64;
222
223 fn estimate_avg_degree(&self, edge_type: &str, outgoing: bool) -> f64;
225
226 fn current_epoch(&self) -> EpochId;
230
231 fn all_labels(&self) -> Vec<String> {
235 Vec::new()
236 }
237
238 fn all_edge_types(&self) -> Vec<String> {
240 Vec::new()
241 }
242
243 fn all_property_keys(&self) -> Vec<String> {
245 Vec::new()
246 }
247
248 fn is_node_visible_at_epoch(&self, id: NodeId, epoch: EpochId) -> bool {
256 self.get_node_at_epoch(id, epoch).is_some()
257 }
258
259 fn is_node_visible_versioned(
262 &self,
263 id: NodeId,
264 epoch: EpochId,
265 transaction_id: TransactionId,
266 ) -> bool {
267 self.get_node_versioned(id, epoch, transaction_id).is_some()
268 }
269
270 fn is_edge_visible_at_epoch(&self, id: EdgeId, epoch: EpochId) -> bool {
276 self.get_edge_at_epoch(id, epoch).is_some()
277 }
278
279 fn is_edge_visible_versioned(
282 &self,
283 id: EdgeId,
284 epoch: EpochId,
285 transaction_id: TransactionId,
286 ) -> bool {
287 self.get_edge_versioned(id, epoch, transaction_id).is_some()
288 }
289
290 fn filter_visible_node_ids(&self, ids: &[NodeId], epoch: EpochId) -> Vec<NodeId> {
295 ids.iter()
296 .copied()
297 .filter(|id| self.is_node_visible_at_epoch(*id, epoch))
298 .collect()
299 }
300
301 fn filter_visible_node_ids_versioned(
303 &self,
304 ids: &[NodeId],
305 epoch: EpochId,
306 transaction_id: TransactionId,
307 ) -> Vec<NodeId> {
308 ids.iter()
309 .copied()
310 .filter(|id| self.is_node_visible_versioned(*id, epoch, transaction_id))
311 .collect()
312 }
313
314 fn get_node_history(&self, _id: NodeId) -> Vec<(EpochId, Option<EpochId>, Node)> {
323 Vec::new()
324 }
325
326 fn get_edge_history(&self, _id: EdgeId) -> Vec<(EpochId, Option<EpochId>, Edge)> {
333 Vec::new()
334 }
335}
336
337pub trait GraphStoreSearch: GraphStore {
352 #[cfg(feature = "text-index")]
356 #[must_use]
357 fn has_text_index(&self, _label: &str, _property: &str) -> bool {
358 false
359 }
360
361 #[cfg(feature = "text-index")]
367 fn score_text(
368 &self,
369 _node_id: NodeId,
370 _label: &str,
371 _property: &str,
372 _query: &str,
373 ) -> Option<f64> {
374 None
375 }
376
377 #[cfg(feature = "text-index")]
382 fn text_search(
383 &self,
384 _label: &str,
385 _property: &str,
386 _query: &str,
387 _k: usize,
388 ) -> Vec<(NodeId, f64)> {
389 Vec::new()
390 }
391
392 #[cfg(feature = "text-index")]
394 fn text_search_with_threshold(
395 &self,
396 _label: &str,
397 _property: &str,
398 _query: &str,
399 _threshold: f64,
400 ) -> Vec<(NodeId, f64)> {
401 Vec::new()
402 }
403
404 #[cfg(feature = "vector-index")]
408 #[must_use]
409 fn has_vector_index(&self, _label: &str, _property: &str) -> bool {
410 false
411 }
412
413 #[cfg(feature = "vector-index")]
420 fn vector_index_metric(&self, _label: &str, _property: &str) -> Option<DistanceMetric> {
421 None
422 }
423
424 #[cfg(feature = "vector-index")]
433 fn vector_search(
434 &self,
435 _label: Option<&str>,
436 _property: &str,
437 _query: &[f32],
438 _k: usize,
439 _metric: DistanceMetric,
440 ) -> Vec<(NodeId, f64)> {
441 Vec::new()
442 }
443
444 #[cfg(feature = "vector-index")]
446 fn vector_search_with_threshold(
447 &self,
448 _label: Option<&str>,
449 _property: &str,
450 _query: &[f32],
451 _threshold: f64,
452 _metric: DistanceMetric,
453 ) -> Vec<(NodeId, f64)> {
454 Vec::new()
455 }
456}
457
458pub trait GraphStoreMut: GraphStoreSearch {
464 fn create_node(&self, labels: &[&str]) -> NodeId;
468
469 fn create_node_versioned(
471 &self,
472 labels: &[&str],
473 epoch: EpochId,
474 transaction_id: TransactionId,
475 ) -> NodeId;
476
477 fn create_edge(&self, src: NodeId, dst: NodeId, edge_type: &str) -> EdgeId;
481
482 fn create_edge_versioned(
484 &self,
485 src: NodeId,
486 dst: NodeId,
487 edge_type: &str,
488 epoch: EpochId,
489 transaction_id: TransactionId,
490 ) -> EdgeId;
491
492 fn batch_create_edges(&self, edges: &[(NodeId, NodeId, &str)]) -> Vec<EdgeId>;
494
495 fn delete_node(&self, id: NodeId) -> bool;
499
500 fn delete_node_versioned(
502 &self,
503 id: NodeId,
504 epoch: EpochId,
505 transaction_id: TransactionId,
506 ) -> bool;
507
508 fn delete_node_edges(&self, node_id: NodeId);
510
511 fn delete_edge(&self, id: EdgeId) -> bool;
513
514 fn delete_edge_versioned(
516 &self,
517 id: EdgeId,
518 epoch: EpochId,
519 transaction_id: TransactionId,
520 ) -> bool;
521
522 fn set_node_property(&self, id: NodeId, key: &str, value: Value);
526
527 fn set_edge_property(&self, id: EdgeId, key: &str, value: Value);
529
530 fn set_node_property_versioned(
535 &self,
536 id: NodeId,
537 key: &str,
538 value: Value,
539 _transaction_id: TransactionId,
540 ) {
541 self.set_node_property(id, key, value);
542 }
543
544 fn set_edge_property_versioned(
549 &self,
550 id: EdgeId,
551 key: &str,
552 value: Value,
553 _transaction_id: TransactionId,
554 ) {
555 self.set_edge_property(id, key, value);
556 }
557
558 fn remove_node_property(&self, id: NodeId, key: &str) -> Option<Value>;
560
561 fn remove_edge_property(&self, id: EdgeId, key: &str) -> Option<Value>;
563
564 fn remove_node_property_versioned(
569 &self,
570 id: NodeId,
571 key: &str,
572 _transaction_id: TransactionId,
573 ) -> Option<Value> {
574 self.remove_node_property(id, key)
575 }
576
577 fn remove_edge_property_versioned(
582 &self,
583 id: EdgeId,
584 key: &str,
585 _transaction_id: TransactionId,
586 ) -> Option<Value> {
587 self.remove_edge_property(id, key)
588 }
589
590 fn add_label(&self, node_id: NodeId, label: &str) -> bool;
594
595 fn remove_label(&self, node_id: NodeId, label: &str) -> bool;
597
598 fn add_label_versioned(
602 &self,
603 node_id: NodeId,
604 label: &str,
605 _transaction_id: TransactionId,
606 ) -> bool {
607 self.add_label(node_id, label)
608 }
609
610 fn remove_label_versioned(
614 &self,
615 node_id: NodeId,
616 label: &str,
617 _transaction_id: TransactionId,
618 ) -> bool {
619 self.remove_label(node_id, label)
620 }
621
622 fn create_node_with_props(
630 &self,
631 labels: &[&str],
632 properties: &[(PropertyKey, Value)],
633 ) -> NodeId {
634 let id = self.create_node(labels);
635 for (key, value) in properties {
636 self.set_node_property(id, key.as_str(), value.clone());
637 }
638 id
639 }
640
641 fn create_edge_with_props(
647 &self,
648 src: NodeId,
649 dst: NodeId,
650 edge_type: &str,
651 properties: &[(PropertyKey, Value)],
652 ) -> EdgeId {
653 let id = self.create_edge(src, dst, edge_type);
654 for (key, value) in properties {
655 self.set_edge_property(id, key.as_str(), value.clone());
656 }
657 id
658 }
659}
660
661pub struct NullGraphStore;
668
669impl GraphStore for NullGraphStore {
670 fn get_node(&self, _: NodeId) -> Option<Node> {
671 None
672 }
673 fn get_edge(&self, _: EdgeId) -> Option<Edge> {
674 None
675 }
676 fn get_node_versioned(&self, _: NodeId, _: EpochId, _: TransactionId) -> Option<Node> {
677 None
678 }
679 fn get_edge_versioned(&self, _: EdgeId, _: EpochId, _: TransactionId) -> Option<Edge> {
680 None
681 }
682 fn get_node_at_epoch(&self, _: NodeId, _: EpochId) -> Option<Node> {
683 None
684 }
685 fn get_edge_at_epoch(&self, _: EdgeId, _: EpochId) -> Option<Edge> {
686 None
687 }
688 fn get_node_property(&self, _: NodeId, _: &PropertyKey) -> Option<Value> {
689 None
690 }
691 fn get_edge_property(&self, _: EdgeId, _: &PropertyKey) -> Option<Value> {
692 None
693 }
694 fn get_node_property_batch(&self, ids: &[NodeId], _: &PropertyKey) -> Vec<Option<Value>> {
695 vec![None; ids.len()]
696 }
697 fn get_nodes_properties_batch(&self, ids: &[NodeId]) -> Vec<FxHashMap<PropertyKey, Value>> {
698 vec![FxHashMap::default(); ids.len()]
699 }
700 fn get_nodes_properties_selective_batch(
701 &self,
702 ids: &[NodeId],
703 _: &[PropertyKey],
704 ) -> Vec<FxHashMap<PropertyKey, Value>> {
705 vec![FxHashMap::default(); ids.len()]
706 }
707 fn get_edges_properties_selective_batch(
708 &self,
709 ids: &[EdgeId],
710 _: &[PropertyKey],
711 ) -> Vec<FxHashMap<PropertyKey, Value>> {
712 vec![FxHashMap::default(); ids.len()]
713 }
714 fn neighbors(&self, _: NodeId, _: Direction) -> Vec<NodeId> {
715 Vec::new()
716 }
717 fn edges_from(&self, _: NodeId, _: Direction) -> Vec<(NodeId, EdgeId)> {
718 Vec::new()
719 }
720 fn out_degree(&self, _: NodeId) -> usize {
721 0
722 }
723 fn in_degree(&self, _: NodeId) -> usize {
724 0
725 }
726 fn has_backward_adjacency(&self) -> bool {
727 false
728 }
729 fn node_ids(&self) -> Vec<NodeId> {
730 Vec::new()
731 }
732 fn nodes_by_label(&self, _: &str) -> Vec<NodeId> {
733 Vec::new()
734 }
735 fn node_count(&self) -> usize {
736 0
737 }
738 fn edge_count(&self) -> usize {
739 0
740 }
741 fn edge_type(&self, _: EdgeId) -> Option<ArcStr> {
742 None
743 }
744 fn find_nodes_by_property(&self, _: &str, _: &Value) -> Vec<NodeId> {
745 Vec::new()
746 }
747 fn find_nodes_by_properties(&self, _: &[(&str, Value)]) -> Vec<NodeId> {
748 Vec::new()
749 }
750 fn find_nodes_in_range(
751 &self,
752 _: &str,
753 _: Option<&Value>,
754 _: Option<&Value>,
755 _: bool,
756 _: bool,
757 ) -> Vec<NodeId> {
758 Vec::new()
759 }
760 fn node_property_might_match(&self, _: &PropertyKey, _: CompareOp, _: &Value) -> bool {
761 false
762 }
763 fn edge_property_might_match(&self, _: &PropertyKey, _: CompareOp, _: &Value) -> bool {
764 false
765 }
766 fn statistics(&self) -> Arc<Statistics> {
767 Arc::new(Statistics::default())
768 }
769 fn estimate_label_cardinality(&self, _: &str) -> f64 {
770 0.0
771 }
772 fn estimate_avg_degree(&self, _: &str, _: bool) -> f64 {
773 0.0
774 }
775 fn current_epoch(&self) -> EpochId {
776 EpochId(0)
777 }
778}
779
780impl GraphStoreSearch for NullGraphStore {}
781
782#[cfg(test)]
783mod tests {
784 use super::*;
785 use std::sync::Mutex;
786
787 #[test]
788 fn null_graph_store_point_lookups() {
789 let store = NullGraphStore;
790 let nid = NodeId(1);
791 let eid = EdgeId(1);
792 let epoch = EpochId(0);
793 let txn = TransactionId(1);
794
795 assert!(store.get_node(nid).is_none());
796 assert!(store.get_edge(eid).is_none());
797 assert!(store.get_node_versioned(nid, epoch, txn).is_none());
798 assert!(store.get_edge_versioned(eid, epoch, txn).is_none());
799 assert!(store.get_node_at_epoch(nid, epoch).is_none());
800 assert!(store.get_edge_at_epoch(eid, epoch).is_none());
801 }
802
803 #[test]
804 fn null_graph_store_property_access() {
805 let store = NullGraphStore;
806 let nid = NodeId(1);
807 let eid = EdgeId(1);
808 let key = PropertyKey::from("name");
809
810 assert!(store.get_node_property(nid, &key).is_none());
811 assert!(store.get_edge_property(eid, &key).is_none());
812 assert_eq!(
813 store.get_node_property_batch(&[nid, NodeId(2)], &key),
814 vec![None, None]
815 );
816
817 let node_props = store.get_nodes_properties_batch(&[nid]);
818 assert_eq!(node_props.len(), 1);
819 assert!(node_props[0].is_empty());
820
821 let selective =
822 store.get_nodes_properties_selective_batch(&[nid], std::slice::from_ref(&key));
823 assert_eq!(selective.len(), 1);
824 assert!(selective[0].is_empty());
825
826 let edge_selective = store.get_edges_properties_selective_batch(&[eid], &[key]);
827 assert_eq!(edge_selective.len(), 1);
828 assert!(edge_selective[0].is_empty());
829 }
830
831 #[test]
832 fn null_graph_store_traversal() {
833 let store = NullGraphStore;
834 let nid = NodeId(1);
835
836 assert!(store.neighbors(nid, Direction::Outgoing).is_empty());
837 assert!(store.edges_from(nid, Direction::Incoming).is_empty());
838 assert_eq!(store.out_degree(nid), 0);
839 assert_eq!(store.in_degree(nid), 0);
840 assert!(!store.has_backward_adjacency());
841 }
842
843 #[test]
844 fn null_graph_store_scans_and_counts() {
845 let store = NullGraphStore;
846
847 assert!(store.node_ids().is_empty());
848 assert!(store.all_node_ids().is_empty());
849 assert!(store.nodes_by_label("Person").is_empty());
850 assert_eq!(store.node_count(), 0);
851 assert_eq!(store.edge_count(), 0);
852 }
853
854 #[test]
855 fn null_graph_store_metadata_and_schema() {
856 let store = NullGraphStore;
857 let eid = EdgeId(1);
858 let epoch = EpochId(0);
859 let txn = TransactionId(1);
860
861 assert!(store.edge_type(eid).is_none());
862 assert!(store.edge_type_versioned(eid, epoch, txn).is_none());
863 assert!(!store.has_property_index("name"));
864 assert!(store.all_labels().is_empty());
865 assert!(store.all_edge_types().is_empty());
866 assert!(store.all_property_keys().is_empty());
867 }
868
869 #[test]
870 fn null_graph_store_search() {
871 let store = NullGraphStore;
872 let key = PropertyKey::from("age");
873 let val = Value::Int64(30);
874
875 assert!(store.find_nodes_by_property("age", &val).is_empty());
876 assert!(
877 store
878 .find_nodes_by_properties(&[("age", val.clone())])
879 .is_empty()
880 );
881 assert!(
882 store
883 .find_nodes_in_range("age", Some(&val), None, true, false)
884 .is_empty()
885 );
886 assert!(!store.node_property_might_match(&key, CompareOp::Eq, &val));
887 assert!(!store.edge_property_might_match(&key, CompareOp::Eq, &val));
888 }
889
890 #[test]
891 fn null_graph_store_statistics() {
892 let store = NullGraphStore;
893
894 let _stats = store.statistics();
895 assert_eq!(store.estimate_label_cardinality("Person"), 0.0);
896 assert_eq!(store.estimate_avg_degree("KNOWS", true), 0.0);
897 assert_eq!(store.current_epoch(), EpochId(0));
898 }
899
900 #[test]
901 fn null_graph_store_visibility() {
902 let store = NullGraphStore;
903 let nid = NodeId(1);
904 let eid = EdgeId(1);
905 let epoch = EpochId(0);
906 let txn = TransactionId(1);
907
908 assert!(!store.is_node_visible_at_epoch(nid, epoch));
909 assert!(!store.is_node_visible_versioned(nid, epoch, txn));
910 assert!(!store.is_edge_visible_at_epoch(eid, epoch));
911 assert!(!store.is_edge_visible_versioned(eid, epoch, txn));
912
913 assert!(
914 store
915 .filter_visible_node_ids(&[nid, NodeId(2)], epoch)
916 .is_empty()
917 );
918 assert!(
919 store
920 .filter_visible_node_ids_versioned(&[nid], epoch, txn)
921 .is_empty()
922 );
923 }
924
925 #[test]
926 fn null_graph_store_history() {
927 let store = NullGraphStore;
928
929 assert!(store.get_node_history(NodeId(1)).is_empty());
930 assert!(store.get_edge_history(EdgeId(1)).is_empty());
931 }
932
933 #[derive(Default)]
937 struct TestMutStore {
938 inner: Mutex<TestMutInner>,
939 }
940
941 #[derive(Default)]
942 struct TestMutInner {
943 next_node: u64,
944 next_edge: u64,
945 nodes: Vec<Node>,
946 edges: Vec<Edge>,
947 }
948
949 impl TestMutStore {
950 fn new() -> Self {
951 Self::default()
952 }
953
954 fn find_node(&self, id: NodeId) -> Option<Node> {
955 self.inner
956 .lock()
957 .unwrap()
958 .nodes
959 .iter()
960 .find(|n| n.id == id)
961 .cloned()
962 }
963
964 fn find_edge(&self, id: EdgeId) -> Option<Edge> {
965 self.inner
966 .lock()
967 .unwrap()
968 .edges
969 .iter()
970 .find(|e| e.id == id)
971 .cloned()
972 }
973 }
974
975 impl GraphStore for TestMutStore {
976 fn get_node(&self, id: NodeId) -> Option<Node> {
977 self.find_node(id)
978 }
979 fn get_edge(&self, id: EdgeId) -> Option<Edge> {
980 self.find_edge(id)
981 }
982 fn get_node_versioned(&self, id: NodeId, _: EpochId, _: TransactionId) -> Option<Node> {
983 self.find_node(id)
984 }
985 fn get_edge_versioned(&self, id: EdgeId, _: EpochId, _: TransactionId) -> Option<Edge> {
986 self.find_edge(id)
987 }
988 fn get_node_at_epoch(&self, id: NodeId, _: EpochId) -> Option<Node> {
989 self.find_node(id)
990 }
991 fn get_edge_at_epoch(&self, id: EdgeId, _: EpochId) -> Option<Edge> {
992 self.find_edge(id)
993 }
994 fn get_node_property(&self, id: NodeId, key: &PropertyKey) -> Option<Value> {
995 self.find_node(id)
996 .and_then(|n| n.properties.get(key).cloned())
997 }
998 fn get_edge_property(&self, id: EdgeId, key: &PropertyKey) -> Option<Value> {
999 self.find_edge(id)
1000 .and_then(|e| e.properties.get(key).cloned())
1001 }
1002 fn get_node_property_batch(&self, ids: &[NodeId], key: &PropertyKey) -> Vec<Option<Value>> {
1003 ids.iter()
1004 .map(|id| self.get_node_property(*id, key))
1005 .collect()
1006 }
1007 fn get_nodes_properties_batch(&self, ids: &[NodeId]) -> Vec<FxHashMap<PropertyKey, Value>> {
1008 ids.iter()
1009 .map(|id| {
1010 let mut map = FxHashMap::default();
1011 if let Some(n) = self.find_node(*id) {
1012 for (k, v) in n.properties.iter() {
1013 map.insert(k.clone(), v.clone());
1014 }
1015 }
1016 map
1017 })
1018 .collect()
1019 }
1020 fn get_nodes_properties_selective_batch(
1021 &self,
1022 ids: &[NodeId],
1023 _: &[PropertyKey],
1024 ) -> Vec<FxHashMap<PropertyKey, Value>> {
1025 vec![FxHashMap::default(); ids.len()]
1026 }
1027 fn get_edges_properties_selective_batch(
1028 &self,
1029 ids: &[EdgeId],
1030 _: &[PropertyKey],
1031 ) -> Vec<FxHashMap<PropertyKey, Value>> {
1032 vec![FxHashMap::default(); ids.len()]
1033 }
1034 fn neighbors(&self, _: NodeId, _: Direction) -> Vec<NodeId> {
1035 Vec::new()
1036 }
1037 fn edges_from(&self, _: NodeId, _: Direction) -> Vec<(NodeId, EdgeId)> {
1038 Vec::new()
1039 }
1040 fn out_degree(&self, _: NodeId) -> usize {
1041 0
1042 }
1043 fn in_degree(&self, _: NodeId) -> usize {
1044 0
1045 }
1046 fn has_backward_adjacency(&self) -> bool {
1047 false
1048 }
1049 fn node_ids(&self) -> Vec<NodeId> {
1050 self.inner
1051 .lock()
1052 .unwrap()
1053 .nodes
1054 .iter()
1055 .map(|n| n.id)
1056 .collect()
1057 }
1058 fn nodes_by_label(&self, _: &str) -> Vec<NodeId> {
1059 Vec::new()
1060 }
1061 fn node_count(&self) -> usize {
1062 self.inner.lock().unwrap().nodes.len()
1063 }
1064 fn edge_count(&self) -> usize {
1065 self.inner.lock().unwrap().edges.len()
1066 }
1067 fn edge_type(&self, id: EdgeId) -> Option<ArcStr> {
1068 self.find_edge(id).map(|e| e.edge_type)
1069 }
1070 fn find_nodes_by_property(&self, _: &str, _: &Value) -> Vec<NodeId> {
1071 Vec::new()
1072 }
1073 fn find_nodes_by_properties(&self, _: &[(&str, Value)]) -> Vec<NodeId> {
1074 Vec::new()
1075 }
1076 fn find_nodes_in_range(
1077 &self,
1078 _: &str,
1079 _: Option<&Value>,
1080 _: Option<&Value>,
1081 _: bool,
1082 _: bool,
1083 ) -> Vec<NodeId> {
1084 Vec::new()
1085 }
1086 fn node_property_might_match(&self, _: &PropertyKey, _: CompareOp, _: &Value) -> bool {
1087 true
1088 }
1089 fn edge_property_might_match(&self, _: &PropertyKey, _: CompareOp, _: &Value) -> bool {
1090 true
1091 }
1092 fn statistics(&self) -> Arc<Statistics> {
1093 Arc::new(Statistics::default())
1094 }
1095 fn estimate_label_cardinality(&self, _: &str) -> f64 {
1096 0.0
1097 }
1098 fn estimate_avg_degree(&self, _: &str, _: bool) -> f64 {
1099 0.0
1100 }
1101 fn current_epoch(&self) -> EpochId {
1102 EpochId(0)
1103 }
1104 }
1105
1106 impl GraphStoreSearch for TestMutStore {}
1107
1108 impl GraphStoreMut for TestMutStore {
1109 fn create_node(&self, labels: &[&str]) -> NodeId {
1110 let mut inner = self.inner.lock().unwrap();
1111 inner.next_node += 1;
1112 let id = NodeId(inner.next_node);
1113 let mut node = Node::new(id);
1114 for label in labels {
1115 node.add_label(*label);
1116 }
1117 inner.nodes.push(node);
1118 id
1119 }
1120 fn create_node_versioned(&self, labels: &[&str], _: EpochId, _: TransactionId) -> NodeId {
1121 self.create_node(labels)
1122 }
1123 fn create_edge(&self, src: NodeId, dst: NodeId, edge_type: &str) -> EdgeId {
1124 let mut inner = self.inner.lock().unwrap();
1125 inner.next_edge += 1;
1126 let id = EdgeId(inner.next_edge);
1127 inner.edges.push(Edge::new(id, src, dst, edge_type));
1128 id
1129 }
1130 fn create_edge_versioned(
1131 &self,
1132 src: NodeId,
1133 dst: NodeId,
1134 edge_type: &str,
1135 _: EpochId,
1136 _: TransactionId,
1137 ) -> EdgeId {
1138 self.create_edge(src, dst, edge_type)
1139 }
1140 fn batch_create_edges(&self, edges: &[(NodeId, NodeId, &str)]) -> Vec<EdgeId> {
1141 edges
1142 .iter()
1143 .map(|(s, d, t)| self.create_edge(*s, *d, t))
1144 .collect()
1145 }
1146 fn delete_node(&self, id: NodeId) -> bool {
1147 let mut inner = self.inner.lock().unwrap();
1148 if let Some(pos) = inner.nodes.iter().position(|n| n.id == id) {
1149 inner.nodes.remove(pos);
1150 true
1151 } else {
1152 false
1153 }
1154 }
1155 fn delete_node_versioned(&self, id: NodeId, _: EpochId, _: TransactionId) -> bool {
1156 self.delete_node(id)
1157 }
1158 fn delete_node_edges(&self, node_id: NodeId) {
1159 let mut inner = self.inner.lock().unwrap();
1160 inner.edges.retain(|e| e.src != node_id && e.dst != node_id);
1161 }
1162 fn delete_edge(&self, id: EdgeId) -> bool {
1163 let mut inner = self.inner.lock().unwrap();
1164 if let Some(pos) = inner.edges.iter().position(|e| e.id == id) {
1165 inner.edges.remove(pos);
1166 true
1167 } else {
1168 false
1169 }
1170 }
1171 fn delete_edge_versioned(&self, id: EdgeId, _: EpochId, _: TransactionId) -> bool {
1172 self.delete_edge(id)
1173 }
1174 fn set_node_property(&self, id: NodeId, key: &str, value: Value) {
1175 let mut inner = self.inner.lock().unwrap();
1176 if let Some(node) = inner.nodes.iter_mut().find(|n| n.id == id) {
1177 node.set_property(key, value);
1178 }
1179 }
1180 fn set_edge_property(&self, id: EdgeId, key: &str, value: Value) {
1181 let mut inner = self.inner.lock().unwrap();
1182 if let Some(edge) = inner.edges.iter_mut().find(|e| e.id == id) {
1183 edge.set_property(key, value);
1184 }
1185 }
1186 fn remove_node_property(&self, id: NodeId, key: &str) -> Option<Value> {
1187 let mut inner = self.inner.lock().unwrap();
1188 inner
1189 .nodes
1190 .iter_mut()
1191 .find(|n| n.id == id)
1192 .and_then(|n| n.remove_property(key))
1193 }
1194 fn remove_edge_property(&self, id: EdgeId, key: &str) -> Option<Value> {
1195 let mut inner = self.inner.lock().unwrap();
1196 inner
1197 .edges
1198 .iter_mut()
1199 .find(|e| e.id == id)
1200 .and_then(|e| e.remove_property(key))
1201 }
1202 fn add_label(&self, node_id: NodeId, label: &str) -> bool {
1203 let mut inner = self.inner.lock().unwrap();
1204 if let Some(node) = inner.nodes.iter_mut().find(|n| n.id == node_id) {
1205 if node.has_label(label) {
1206 false
1207 } else {
1208 node.add_label(label);
1209 true
1210 }
1211 } else {
1212 false
1213 }
1214 }
1215 fn remove_label(&self, node_id: NodeId, label: &str) -> bool {
1216 let mut inner = self.inner.lock().unwrap();
1217 inner
1218 .nodes
1219 .iter_mut()
1220 .find(|n| n.id == node_id)
1221 .is_some_and(|n| n.remove_label(label))
1222 }
1223 }
1224
1225 #[test]
1226 fn test_mut_store_default_set_versioned_property_delegates() {
1227 let store = TestMutStore::new();
1228 let id = store.create_node(&["Person"]);
1229 let key = PropertyKey::from("name");
1230 let txn = TransactionId(7);
1231
1232 store.set_node_property_versioned(id, "name", Value::from("Vincent"), txn);
1234 assert_eq!(
1235 store.get_node_property(id, &key),
1236 Some(Value::from("Vincent"))
1237 );
1238
1239 let edge_id = {
1240 let src = store.create_node(&["Person"]);
1241 let dst = store.create_node(&["City"]);
1242 store.create_edge(src, dst, "LIVES_IN")
1243 };
1244 let since = PropertyKey::from("since");
1245 store.set_edge_property_versioned(edge_id, "since", Value::Int64(1994), txn);
1246 assert_eq!(
1247 store.get_edge_property(edge_id, &since),
1248 Some(Value::Int64(1994))
1249 );
1250 }
1251
1252 #[test]
1253 fn test_mut_store_default_remove_versioned_property_delegates() {
1254 let store = TestMutStore::new();
1255 let txn = TransactionId(11);
1256
1257 let node_id = store.create_node(&["Person"]);
1258 store.set_node_property(node_id, "city", Value::from("Amsterdam"));
1259 let removed = store.remove_node_property_versioned(node_id, "city", txn);
1260 assert_eq!(removed, Some(Value::from("Amsterdam")));
1261 assert!(
1262 store
1263 .get_node_property(node_id, &PropertyKey::from("city"))
1264 .is_none()
1265 );
1266
1267 let missing = store.remove_node_property_versioned(node_id, "absent", txn);
1268 assert!(missing.is_none());
1269
1270 let src = store.create_node(&["Person"]);
1271 let dst = store.create_node(&["Person"]);
1272 let edge_id = store.create_edge(src, dst, "KNOWS");
1273 store.set_edge_property(edge_id, "weight", Value::Int64(42));
1274 let removed_edge = store.remove_edge_property_versioned(edge_id, "weight", txn);
1275 assert_eq!(removed_edge, Some(Value::Int64(42)));
1276 let removed_again = store.remove_edge_property_versioned(edge_id, "weight", txn);
1277 assert!(removed_again.is_none());
1278 }
1279
1280 #[test]
1281 fn test_mut_store_default_label_versioned_delegates() {
1282 let store = TestMutStore::new();
1283 let txn = TransactionId(3);
1284 let id = store.create_node(&["Person"]);
1285
1286 assert!(store.add_label_versioned(id, "Director", txn));
1288 assert!(!store.add_label_versioned(id, "Director", txn));
1289
1290 assert!(store.remove_label_versioned(id, "Director", txn));
1292 assert!(!store.remove_label_versioned(id, "Director", txn));
1293
1294 let unknown = NodeId(9999);
1296 assert!(!store.add_label_versioned(unknown, "Ghost", txn));
1297 assert!(!store.remove_label_versioned(unknown, "Ghost", txn));
1298 }
1299
1300 #[test]
1301 fn test_mut_store_default_create_node_with_props() {
1302 let store = TestMutStore::new();
1303 let props = vec![
1304 (PropertyKey::from("name"), Value::from("Jules")),
1305 (PropertyKey::from("city"), Value::from("Paris")),
1306 ];
1307
1308 let id = store.create_node_with_props(&["Person"], &props);
1309 let node = store.get_node(id).expect("node should exist");
1310 assert!(node.has_label("Person"));
1311 assert_eq!(
1312 node.properties.get(&PropertyKey::from("name")),
1313 Some(&Value::from("Jules"))
1314 );
1315 assert_eq!(
1316 node.properties.get(&PropertyKey::from("city")),
1317 Some(&Value::from("Paris"))
1318 );
1319
1320 let bare = store.create_node_with_props(&["Person"], &[]);
1322 let bare_node = store.get_node(bare).expect("bare node should exist");
1323 assert!(bare_node.properties.is_empty());
1324 }
1325
1326 #[test]
1327 fn test_mut_store_default_create_edge_with_props() {
1328 let store = TestMutStore::new();
1329 let src = store.create_node_with_props(
1330 &["Person"],
1331 &[(PropertyKey::from("name"), Value::from("Mia"))],
1332 );
1333 let dst = store.create_node_with_props(
1334 &["City"],
1335 &[(PropertyKey::from("name"), Value::from("Berlin"))],
1336 );
1337 let props = vec![
1338 (PropertyKey::from("since"), Value::Int64(2021)),
1339 (PropertyKey::from("role"), Value::from("resident")),
1340 ];
1341
1342 let edge_id = store.create_edge_with_props(src, dst, "LIVES_IN", &props);
1343 let edge = store.get_edge(edge_id).expect("edge should exist");
1344 assert_eq!(edge.src, src);
1345 assert_eq!(edge.dst, dst);
1346 assert_eq!(edge.edge_type.as_str(), "LIVES_IN");
1347 assert_eq!(
1348 edge.properties.get(&PropertyKey::from("since")),
1349 Some(&Value::Int64(2021))
1350 );
1351 assert_eq!(
1352 edge.properties.get(&PropertyKey::from("role")),
1353 Some(&Value::from("resident"))
1354 );
1355
1356 assert_eq!(
1358 store
1359 .edge_type(edge_id)
1360 .as_ref()
1361 .map(arcstr::ArcStr::as_str),
1362 Some("LIVES_IN")
1363 );
1364
1365 let bare = store.create_edge_with_props(src, dst, "VISITED", &[]);
1367 let bare_edge = store.get_edge(bare).expect("bare edge should exist");
1368 assert!(bare_edge.properties.is_empty());
1369 }
1370
1371 #[test]
1372 fn test_mut_store_object_safe_dyn_dispatch() {
1373 let store: Arc<dyn GraphStoreSearch> = Arc::new(TestMutStore::new());
1375 assert_eq!(store.node_count(), 0);
1376 assert_eq!(store.edge_count(), 0);
1377 assert!(store.node_ids().is_empty());
1378 assert!(store.get_node(NodeId(1)).is_none());
1379 assert_eq!(store.current_epoch(), EpochId(0));
1380 }
1381}