1use crate::graph::Direction;
23use crate::graph::lpg::CompareOp;
24use crate::graph::lpg::{Edge, Node};
25use crate::statistics::Statistics;
26use arcstr::ArcStr;
27use grafeo_common::types::{EdgeId, EpochId, NodeId, PropertyKey, TransactionId, Value};
28use grafeo_common::utils::hash::FxHashMap;
29use std::sync::Arc;
30
31pub trait GraphStore: Send + Sync {
43 fn get_node(&self, id: NodeId) -> Option<Node>;
47
48 fn get_edge(&self, id: EdgeId) -> Option<Edge>;
50
51 fn get_node_versioned(
53 &self,
54 id: NodeId,
55 epoch: EpochId,
56 transaction_id: TransactionId,
57 ) -> Option<Node>;
58
59 fn get_edge_versioned(
61 &self,
62 id: EdgeId,
63 epoch: EpochId,
64 transaction_id: TransactionId,
65 ) -> Option<Edge>;
66
67 fn get_node_at_epoch(&self, id: NodeId, epoch: EpochId) -> Option<Node>;
73
74 fn get_edge_at_epoch(&self, id: EdgeId, epoch: EpochId) -> Option<Edge>;
76
77 fn get_node_property(&self, id: NodeId, key: &PropertyKey) -> Option<Value>;
81
82 fn get_edge_property(&self, id: EdgeId, key: &PropertyKey) -> Option<Value>;
84
85 fn get_node_property_batch(&self, ids: &[NodeId], key: &PropertyKey) -> Vec<Option<Value>>;
87
88 fn get_nodes_properties_batch(&self, ids: &[NodeId]) -> Vec<FxHashMap<PropertyKey, Value>>;
90
91 fn get_nodes_properties_selective_batch(
93 &self,
94 ids: &[NodeId],
95 keys: &[PropertyKey],
96 ) -> Vec<FxHashMap<PropertyKey, Value>>;
97
98 fn get_edges_properties_selective_batch(
100 &self,
101 ids: &[EdgeId],
102 keys: &[PropertyKey],
103 ) -> Vec<FxHashMap<PropertyKey, Value>>;
104
105 fn neighbors(&self, node: NodeId, direction: Direction) -> Vec<NodeId>;
112
113 fn edges_from(&self, node: NodeId, direction: Direction) -> Vec<(NodeId, EdgeId)>;
115
116 fn out_degree(&self, node: NodeId) -> usize;
118
119 fn in_degree(&self, node: NodeId) -> usize;
121
122 fn has_backward_adjacency(&self) -> bool;
124
125 fn node_ids(&self) -> Vec<NodeId>;
129
130 fn all_node_ids(&self) -> Vec<NodeId> {
136 self.node_ids()
138 }
139
140 fn nodes_by_label(&self, label: &str) -> Vec<NodeId>;
142
143 fn node_count(&self) -> usize;
145
146 fn edge_count(&self) -> usize;
148
149 fn edge_type(&self, id: EdgeId) -> Option<ArcStr>;
153
154 fn edge_type_versioned(
158 &self,
159 id: EdgeId,
160 epoch: EpochId,
161 transaction_id: TransactionId,
162 ) -> Option<ArcStr> {
163 let _ = (epoch, transaction_id);
164 self.edge_type(id)
165 }
166
167 fn has_property_index(&self, _property: &str) -> bool {
173 false
174 }
175
176 fn find_nodes_by_property(&self, property: &str, value: &Value) -> Vec<NodeId>;
180
181 fn find_nodes_by_properties(&self, conditions: &[(&str, Value)]) -> Vec<NodeId>;
183
184 fn find_nodes_in_range(
186 &self,
187 property: &str,
188 min: Option<&Value>,
189 max: Option<&Value>,
190 min_inclusive: bool,
191 max_inclusive: bool,
192 ) -> Vec<NodeId>;
193
194 fn node_property_might_match(
199 &self,
200 property: &PropertyKey,
201 op: CompareOp,
202 value: &Value,
203 ) -> bool;
204
205 fn edge_property_might_match(
207 &self,
208 property: &PropertyKey,
209 op: CompareOp,
210 value: &Value,
211 ) -> bool;
212
213 fn statistics(&self) -> Arc<Statistics>;
217
218 fn estimate_label_cardinality(&self, label: &str) -> f64;
220
221 fn estimate_avg_degree(&self, edge_type: &str, outgoing: bool) -> f64;
223
224 fn current_epoch(&self) -> EpochId;
228
229 fn all_labels(&self) -> Vec<String> {
233 Vec::new()
234 }
235
236 fn all_edge_types(&self) -> Vec<String> {
238 Vec::new()
239 }
240
241 fn all_property_keys(&self) -> Vec<String> {
243 Vec::new()
244 }
245
246 fn is_node_visible_at_epoch(&self, id: NodeId, epoch: EpochId) -> bool {
254 self.get_node_at_epoch(id, epoch).is_some()
255 }
256
257 fn is_node_visible_versioned(
260 &self,
261 id: NodeId,
262 epoch: EpochId,
263 transaction_id: TransactionId,
264 ) -> bool {
265 self.get_node_versioned(id, epoch, transaction_id).is_some()
266 }
267
268 fn is_edge_visible_at_epoch(&self, id: EdgeId, epoch: EpochId) -> bool {
274 self.get_edge_at_epoch(id, epoch).is_some()
275 }
276
277 fn is_edge_visible_versioned(
280 &self,
281 id: EdgeId,
282 epoch: EpochId,
283 transaction_id: TransactionId,
284 ) -> bool {
285 self.get_edge_versioned(id, epoch, transaction_id).is_some()
286 }
287
288 fn filter_visible_node_ids(&self, ids: &[NodeId], epoch: EpochId) -> Vec<NodeId> {
293 ids.iter()
294 .copied()
295 .filter(|id| self.is_node_visible_at_epoch(*id, epoch))
296 .collect()
297 }
298
299 fn filter_visible_node_ids_versioned(
301 &self,
302 ids: &[NodeId],
303 epoch: EpochId,
304 transaction_id: TransactionId,
305 ) -> Vec<NodeId> {
306 ids.iter()
307 .copied()
308 .filter(|id| self.is_node_visible_versioned(*id, epoch, transaction_id))
309 .collect()
310 }
311
312 fn get_node_history(&self, _id: NodeId) -> Vec<(EpochId, Option<EpochId>, Node)> {
321 Vec::new()
322 }
323
324 fn get_edge_history(&self, _id: EdgeId) -> Vec<(EpochId, Option<EpochId>, Edge)> {
331 Vec::new()
332 }
333}
334
335pub trait GraphStoreMut: GraphStore {
341 fn create_node(&self, labels: &[&str]) -> NodeId;
345
346 fn create_node_versioned(
348 &self,
349 labels: &[&str],
350 epoch: EpochId,
351 transaction_id: TransactionId,
352 ) -> NodeId;
353
354 fn create_edge(&self, src: NodeId, dst: NodeId, edge_type: &str) -> EdgeId;
358
359 fn create_edge_versioned(
361 &self,
362 src: NodeId,
363 dst: NodeId,
364 edge_type: &str,
365 epoch: EpochId,
366 transaction_id: TransactionId,
367 ) -> EdgeId;
368
369 fn batch_create_edges(&self, edges: &[(NodeId, NodeId, &str)]) -> Vec<EdgeId>;
371
372 fn delete_node(&self, id: NodeId) -> bool;
376
377 fn delete_node_versioned(
379 &self,
380 id: NodeId,
381 epoch: EpochId,
382 transaction_id: TransactionId,
383 ) -> bool;
384
385 fn delete_node_edges(&self, node_id: NodeId);
387
388 fn delete_edge(&self, id: EdgeId) -> bool;
390
391 fn delete_edge_versioned(
393 &self,
394 id: EdgeId,
395 epoch: EpochId,
396 transaction_id: TransactionId,
397 ) -> bool;
398
399 fn set_node_property(&self, id: NodeId, key: &str, value: Value);
403
404 fn set_edge_property(&self, id: EdgeId, key: &str, value: Value);
406
407 fn set_node_property_versioned(
412 &self,
413 id: NodeId,
414 key: &str,
415 value: Value,
416 _transaction_id: TransactionId,
417 ) {
418 self.set_node_property(id, key, value);
419 }
420
421 fn set_edge_property_versioned(
426 &self,
427 id: EdgeId,
428 key: &str,
429 value: Value,
430 _transaction_id: TransactionId,
431 ) {
432 self.set_edge_property(id, key, value);
433 }
434
435 fn remove_node_property(&self, id: NodeId, key: &str) -> Option<Value>;
437
438 fn remove_edge_property(&self, id: EdgeId, key: &str) -> Option<Value>;
440
441 fn remove_node_property_versioned(
446 &self,
447 id: NodeId,
448 key: &str,
449 _transaction_id: TransactionId,
450 ) -> Option<Value> {
451 self.remove_node_property(id, key)
452 }
453
454 fn remove_edge_property_versioned(
459 &self,
460 id: EdgeId,
461 key: &str,
462 _transaction_id: TransactionId,
463 ) -> Option<Value> {
464 self.remove_edge_property(id, key)
465 }
466
467 fn add_label(&self, node_id: NodeId, label: &str) -> bool;
471
472 fn remove_label(&self, node_id: NodeId, label: &str) -> bool;
474
475 fn add_label_versioned(
479 &self,
480 node_id: NodeId,
481 label: &str,
482 _transaction_id: TransactionId,
483 ) -> bool {
484 self.add_label(node_id, label)
485 }
486
487 fn remove_label_versioned(
491 &self,
492 node_id: NodeId,
493 label: &str,
494 _transaction_id: TransactionId,
495 ) -> bool {
496 self.remove_label(node_id, label)
497 }
498
499 fn create_node_with_props(
507 &self,
508 labels: &[&str],
509 properties: &[(PropertyKey, Value)],
510 ) -> NodeId {
511 let id = self.create_node(labels);
512 for (key, value) in properties {
513 self.set_node_property(id, key.as_str(), value.clone());
514 }
515 id
516 }
517
518 fn create_edge_with_props(
524 &self,
525 src: NodeId,
526 dst: NodeId,
527 edge_type: &str,
528 properties: &[(PropertyKey, Value)],
529 ) -> EdgeId {
530 let id = self.create_edge(src, dst, edge_type);
531 for (key, value) in properties {
532 self.set_edge_property(id, key.as_str(), value.clone());
533 }
534 id
535 }
536}
537
538pub struct NullGraphStore;
545
546impl GraphStore for NullGraphStore {
547 fn get_node(&self, _: NodeId) -> Option<Node> {
548 None
549 }
550 fn get_edge(&self, _: EdgeId) -> Option<Edge> {
551 None
552 }
553 fn get_node_versioned(&self, _: NodeId, _: EpochId, _: TransactionId) -> Option<Node> {
554 None
555 }
556 fn get_edge_versioned(&self, _: EdgeId, _: EpochId, _: TransactionId) -> Option<Edge> {
557 None
558 }
559 fn get_node_at_epoch(&self, _: NodeId, _: EpochId) -> Option<Node> {
560 None
561 }
562 fn get_edge_at_epoch(&self, _: EdgeId, _: EpochId) -> Option<Edge> {
563 None
564 }
565 fn get_node_property(&self, _: NodeId, _: &PropertyKey) -> Option<Value> {
566 None
567 }
568 fn get_edge_property(&self, _: EdgeId, _: &PropertyKey) -> Option<Value> {
569 None
570 }
571 fn get_node_property_batch(&self, ids: &[NodeId], _: &PropertyKey) -> Vec<Option<Value>> {
572 vec![None; ids.len()]
573 }
574 fn get_nodes_properties_batch(&self, ids: &[NodeId]) -> Vec<FxHashMap<PropertyKey, Value>> {
575 vec![FxHashMap::default(); ids.len()]
576 }
577 fn get_nodes_properties_selective_batch(
578 &self,
579 ids: &[NodeId],
580 _: &[PropertyKey],
581 ) -> Vec<FxHashMap<PropertyKey, Value>> {
582 vec![FxHashMap::default(); ids.len()]
583 }
584 fn get_edges_properties_selective_batch(
585 &self,
586 ids: &[EdgeId],
587 _: &[PropertyKey],
588 ) -> Vec<FxHashMap<PropertyKey, Value>> {
589 vec![FxHashMap::default(); ids.len()]
590 }
591 fn neighbors(&self, _: NodeId, _: Direction) -> Vec<NodeId> {
592 Vec::new()
593 }
594 fn edges_from(&self, _: NodeId, _: Direction) -> Vec<(NodeId, EdgeId)> {
595 Vec::new()
596 }
597 fn out_degree(&self, _: NodeId) -> usize {
598 0
599 }
600 fn in_degree(&self, _: NodeId) -> usize {
601 0
602 }
603 fn has_backward_adjacency(&self) -> bool {
604 false
605 }
606 fn node_ids(&self) -> Vec<NodeId> {
607 Vec::new()
608 }
609 fn nodes_by_label(&self, _: &str) -> Vec<NodeId> {
610 Vec::new()
611 }
612 fn node_count(&self) -> usize {
613 0
614 }
615 fn edge_count(&self) -> usize {
616 0
617 }
618 fn edge_type(&self, _: EdgeId) -> Option<ArcStr> {
619 None
620 }
621 fn find_nodes_by_property(&self, _: &str, _: &Value) -> Vec<NodeId> {
622 Vec::new()
623 }
624 fn find_nodes_by_properties(&self, _: &[(&str, Value)]) -> Vec<NodeId> {
625 Vec::new()
626 }
627 fn find_nodes_in_range(
628 &self,
629 _: &str,
630 _: Option<&Value>,
631 _: Option<&Value>,
632 _: bool,
633 _: bool,
634 ) -> Vec<NodeId> {
635 Vec::new()
636 }
637 fn node_property_might_match(&self, _: &PropertyKey, _: CompareOp, _: &Value) -> bool {
638 false
639 }
640 fn edge_property_might_match(&self, _: &PropertyKey, _: CompareOp, _: &Value) -> bool {
641 false
642 }
643 fn statistics(&self) -> Arc<Statistics> {
644 Arc::new(Statistics::default())
645 }
646 fn estimate_label_cardinality(&self, _: &str) -> f64 {
647 0.0
648 }
649 fn estimate_avg_degree(&self, _: &str, _: bool) -> f64 {
650 0.0
651 }
652 fn current_epoch(&self) -> EpochId {
653 EpochId(0)
654 }
655}
656
657#[cfg(test)]
658mod tests {
659 use super::*;
660
661 #[test]
662 fn null_graph_store_point_lookups() {
663 let store = NullGraphStore;
664 let nid = NodeId(1);
665 let eid = EdgeId(1);
666 let epoch = EpochId(0);
667 let txn = TransactionId(1);
668
669 assert!(store.get_node(nid).is_none());
670 assert!(store.get_edge(eid).is_none());
671 assert!(store.get_node_versioned(nid, epoch, txn).is_none());
672 assert!(store.get_edge_versioned(eid, epoch, txn).is_none());
673 assert!(store.get_node_at_epoch(nid, epoch).is_none());
674 assert!(store.get_edge_at_epoch(eid, epoch).is_none());
675 }
676
677 #[test]
678 fn null_graph_store_property_access() {
679 let store = NullGraphStore;
680 let nid = NodeId(1);
681 let eid = EdgeId(1);
682 let key = PropertyKey::from("name");
683
684 assert!(store.get_node_property(nid, &key).is_none());
685 assert!(store.get_edge_property(eid, &key).is_none());
686 assert_eq!(
687 store.get_node_property_batch(&[nid, NodeId(2)], &key),
688 vec![None, None]
689 );
690
691 let node_props = store.get_nodes_properties_batch(&[nid]);
692 assert_eq!(node_props.len(), 1);
693 assert!(node_props[0].is_empty());
694
695 let selective =
696 store.get_nodes_properties_selective_batch(&[nid], std::slice::from_ref(&key));
697 assert_eq!(selective.len(), 1);
698 assert!(selective[0].is_empty());
699
700 let edge_selective = store.get_edges_properties_selective_batch(&[eid], &[key]);
701 assert_eq!(edge_selective.len(), 1);
702 assert!(edge_selective[0].is_empty());
703 }
704
705 #[test]
706 fn null_graph_store_traversal() {
707 let store = NullGraphStore;
708 let nid = NodeId(1);
709
710 assert!(store.neighbors(nid, Direction::Outgoing).is_empty());
711 assert!(store.edges_from(nid, Direction::Incoming).is_empty());
712 assert_eq!(store.out_degree(nid), 0);
713 assert_eq!(store.in_degree(nid), 0);
714 assert!(!store.has_backward_adjacency());
715 }
716
717 #[test]
718 fn null_graph_store_scans_and_counts() {
719 let store = NullGraphStore;
720
721 assert!(store.node_ids().is_empty());
722 assert!(store.all_node_ids().is_empty());
723 assert!(store.nodes_by_label("Person").is_empty());
724 assert_eq!(store.node_count(), 0);
725 assert_eq!(store.edge_count(), 0);
726 }
727
728 #[test]
729 fn null_graph_store_metadata_and_schema() {
730 let store = NullGraphStore;
731 let eid = EdgeId(1);
732 let epoch = EpochId(0);
733 let txn = TransactionId(1);
734
735 assert!(store.edge_type(eid).is_none());
736 assert!(store.edge_type_versioned(eid, epoch, txn).is_none());
737 assert!(!store.has_property_index("name"));
738 assert!(store.all_labels().is_empty());
739 assert!(store.all_edge_types().is_empty());
740 assert!(store.all_property_keys().is_empty());
741 }
742
743 #[test]
744 fn null_graph_store_search() {
745 let store = NullGraphStore;
746 let key = PropertyKey::from("age");
747 let val = Value::Int64(30);
748
749 assert!(store.find_nodes_by_property("age", &val).is_empty());
750 assert!(
751 store
752 .find_nodes_by_properties(&[("age", val.clone())])
753 .is_empty()
754 );
755 assert!(
756 store
757 .find_nodes_in_range("age", Some(&val), None, true, false)
758 .is_empty()
759 );
760 assert!(!store.node_property_might_match(&key, CompareOp::Eq, &val));
761 assert!(!store.edge_property_might_match(&key, CompareOp::Eq, &val));
762 }
763
764 #[test]
765 fn null_graph_store_statistics() {
766 let store = NullGraphStore;
767
768 let _stats = store.statistics();
769 assert_eq!(store.estimate_label_cardinality("Person"), 0.0);
770 assert_eq!(store.estimate_avg_degree("KNOWS", true), 0.0);
771 assert_eq!(store.current_epoch(), EpochId(0));
772 }
773
774 #[test]
775 fn null_graph_store_visibility() {
776 let store = NullGraphStore;
777 let nid = NodeId(1);
778 let eid = EdgeId(1);
779 let epoch = EpochId(0);
780 let txn = TransactionId(1);
781
782 assert!(!store.is_node_visible_at_epoch(nid, epoch));
783 assert!(!store.is_node_visible_versioned(nid, epoch, txn));
784 assert!(!store.is_edge_visible_at_epoch(eid, epoch));
785 assert!(!store.is_edge_visible_versioned(eid, epoch, txn));
786
787 assert!(
788 store
789 .filter_visible_node_ids(&[nid, NodeId(2)], epoch)
790 .is_empty()
791 );
792 assert!(
793 store
794 .filter_visible_node_ids_versioned(&[nid], epoch, txn)
795 .is_empty()
796 );
797 }
798
799 #[test]
800 fn null_graph_store_history() {
801 let store = NullGraphStore;
802
803 assert!(store.get_node_history(NodeId(1)).is_empty());
804 assert!(store.get_edge_history(EdgeId(1)).is_empty());
805 }
806}