1use super::catalog::GraphCatalog;
81use super::edge::Edge;
82use super::node::Node;
83use super::property::{PropertyMap, PropertyValue};
84use super::types::{EdgeId, EdgeType, Label, NodeId};
85use crate::agent::{tools::WebSearchTool, AgentRuntime};
86use crate::graph::storage::ColumnStore;
87use crate::index::IndexManager;
88use crate::vector::{DistanceMetric, VectorIndexManager, VectorResult};
89use std::collections::{HashMap, HashSet};
90use std::sync::Arc;
91use thiserror::Error;
92use tokio::sync::mpsc::{unbounded_channel, UnboundedSender};
93
94mod chrono {
96 pub struct Utc;
97 impl Utc {
98 pub fn now() -> DateTime {
99 DateTime
100 }
101 }
102 pub struct DateTime;
103 impl DateTime {
104 pub fn timestamp_millis(&self) -> i64 {
105 std::time::SystemTime::now()
107 .duration_since(std::time::UNIX_EPOCH)
108 .unwrap()
109 .as_millis() as i64
110 }
111 }
112}
113
114#[derive(Error, Debug, PartialEq)]
116pub enum GraphError {
117 #[error("Node {0} not found")]
118 NodeNotFound(NodeId),
119
120 #[error("Edge {0} not found")]
121 EdgeNotFound(EdgeId),
122
123 #[error("Node {0} already exists")]
124 NodeAlreadyExists(NodeId),
125
126 #[error("Edge {0} already exists")]
127 EdgeAlreadyExists(EdgeId),
128
129 #[error("Invalid edge: source node {0} does not exist")]
130 InvalidEdgeSource(NodeId),
131
132 #[error("Invalid edge: target node {0} does not exist")]
133 InvalidEdgeTarget(NodeId),
134
135 #[error("Constraint violation: {0}")]
136 ConstraintViolation(String),
137}
138
139pub type GraphResult<T> = Result<T, GraphError>;
140
141#[derive(Debug, Clone)]
171pub struct GraphStatistics {
172 pub total_nodes: usize,
174 pub total_edges: usize,
176 pub label_counts: HashMap<Label, usize>,
178 pub edge_type_counts: HashMap<EdgeType, usize>,
180 pub avg_out_degree: f64,
182 pub property_stats: HashMap<(Label, String), PropertyStats>,
184}
185
186#[derive(Debug, Clone)]
188pub struct PropertyStats {
189 pub null_fraction: f64,
191 pub distinct_count: usize,
193 pub selectivity: f64,
195}
196
197impl GraphStatistics {
198 pub fn estimate_label_scan(&self, label: &Label) -> usize {
200 self.label_counts
201 .get(label)
202 .copied()
203 .unwrap_or(self.total_nodes)
204 }
205
206 pub fn estimate_expand(&self, edge_type: Option<&EdgeType>) -> f64 {
208 match edge_type {
209 Some(et) => self.edge_type_counts.get(et).copied().unwrap_or(0) as f64,
210 None => self.total_edges as f64,
211 }
212 }
213
214 pub fn estimate_equality_selectivity(&self, label: &Label, property: &str) -> f64 {
216 self.property_stats
217 .get(&(label.clone(), property.to_string()))
218 .map(|ps| ps.selectivity)
219 .unwrap_or(0.1) }
221
222 pub fn format(&self) -> String {
224 let mut result = String::new();
225 result.push_str("Graph Statistics:\n");
226 result.push_str(&format!(" Total nodes: {}\n", self.total_nodes));
227 result.push_str(&format!(" Total edges: {}\n", self.total_edges));
228 result.push_str(&format!(" Avg out-degree: {:.2}\n", self.avg_out_degree));
229 result.push_str(" Labels:\n");
230 let mut labels: Vec<_> = self.label_counts.iter().collect();
231 labels.sort_by(|a, b| b.1.cmp(a.1));
232 for (label, count) in labels {
233 result.push_str(&format!(" :{} = {} nodes\n", label.as_str(), count));
234 }
235 result.push_str(" Edge types:\n");
236 let mut types: Vec<_> = self.edge_type_counts.iter().collect();
237 types.sort_by(|a, b| b.1.cmp(a.1));
238 for (etype, count) in types {
239 result.push_str(&format!(" :{} = {} edges\n", etype.as_str(), count));
240 }
241 result
242 }
243}
244
245#[derive(Debug)]
269pub struct GraphStore {
270 nodes: Vec<Vec<Node>>,
272
273 edges: Vec<Vec<Edge>>,
275
276 outgoing: Vec<Vec<(NodeId, EdgeId)>>,
278
279 incoming: Vec<Vec<(NodeId, EdgeId)>>,
281
282 pub current_version: u64,
284
285 free_node_ids: Vec<u64>,
287
288 free_edge_ids: Vec<u64>,
290
291 label_index: HashMap<Label, HashSet<NodeId>>,
293
294 edge_type_index: HashMap<EdgeType, HashSet<EdgeId>>,
296
297 pub vector_index: Arc<VectorIndexManager>,
299
300 pub property_index: Arc<IndexManager>,
302
303 pub node_columns: ColumnStore,
305
306 pub edge_columns: ColumnStore,
308
309 pub index_sender: Option<UnboundedSender<crate::graph::event::IndexEvent>>,
311
312 next_node_id: u64,
314
315 next_edge_id: u64,
317
318 catalog: GraphCatalog,
320}
321
322impl GraphStore {
323 pub fn new() -> Self {
325 GraphStore {
326 nodes: Vec::with_capacity(1024),
327 edges: Vec::with_capacity(4096),
328 outgoing: Vec::with_capacity(1024),
329 incoming: Vec::with_capacity(1024),
330 current_version: 1,
331 free_node_ids: Vec::new(),
332 free_edge_ids: Vec::new(),
333 label_index: HashMap::new(),
334 edge_type_index: HashMap::new(),
335 vector_index: Arc::new(VectorIndexManager::new()),
336 property_index: Arc::new(IndexManager::new()),
337 node_columns: ColumnStore::new(),
338 edge_columns: ColumnStore::new(),
339 index_sender: None,
340 next_node_id: 1,
341 next_edge_id: 1,
342 catalog: GraphCatalog::new(),
343 }
344 }
345
346 pub fn with_async_indexing() -> (
348 Self,
349 tokio::sync::mpsc::UnboundedReceiver<crate::graph::event::IndexEvent>,
350 ) {
351 let (tx, rx) = unbounded_channel();
352 let mut store = Self::new();
353 store.index_sender = Some(tx);
354 (store, rx)
355 }
356
357 pub async fn start_background_indexer(
359 mut receiver: tokio::sync::mpsc::UnboundedReceiver<crate::graph::event::IndexEvent>,
360 vector_index: Arc<VectorIndexManager>,
361 property_index: Arc<IndexManager>,
362 tenant_manager: Arc<crate::persistence::TenantManager>,
363 ) {
364 use crate::graph::event::IndexEvent::*;
365
366 while let Some(event) = receiver.recv().await {
367 match event {
368 NodeCreated {
369 tenant_id,
370 id,
371 labels,
372 properties,
373 } => {
374 for (key, value) in &properties {
375 if let PropertyValue::Vector(vec) = value {
376 for label in &labels {
377 let _ = vector_index.add_vector(label.as_str(), key, id, vec);
378 }
379 }
380 for label in &labels {
381 property_index.index_insert(label, key, value.clone(), id);
382 }
383
384 if let PropertyValue::String(text) = value {
386 if let Ok(tenant) = tenant_manager.get_tenant(&tenant_id) {
387 if let Some(config) = tenant.embed_config {
388 for label in &labels {
389 if let Some(keys) =
390 config.embedding_policies.get(label.as_str())
391 {
392 if keys.contains(key) {
393 let vector_index_clone = Arc::clone(&vector_index);
395 let label_str = label.as_str().to_string();
396 let key_clone = key.clone();
397 let text_clone = text.clone();
398 let config_clone = config.clone();
399
400 tokio::spawn(async move {
401 if let Ok(pipeline) =
402 crate::embed::EmbedPipeline::new(
403 config_clone,
404 )
405 {
406 if let Ok(chunks) =
407 pipeline.process_text(&text_clone).await
408 {
409 for chunk in chunks {
410 let _ = vector_index_clone
411 .add_vector(
412 &label_str,
413 &key_clone,
414 id,
415 &chunk.embedding,
416 );
417 }
418 }
419 }
420 });
421 }
422 }
423 }
424 }
425 }
426 }
427 }
428
429 if let Ok(tenant) = tenant_manager.get_tenant(&tenant_id) {
431 if let Some(agent_config) = tenant.agent_config {
432 if agent_config.enabled {
433 for label in &labels {
434 if let Some(trigger_prompt) =
435 agent_config.policies.get(label.as_str())
436 {
437 let context =
439 format!("Node: {} {:?}", label.as_str(), properties);
440 let task = trigger_prompt.clone();
441 let mut runtime = AgentRuntime::new(agent_config.clone());
442 let label_str = label.as_str().to_string();
443
444 if let Some(api_key) = &agent_config.api_key {
446 runtime.register_tool(Arc::new(WebSearchTool::new(
447 api_key.clone(),
448 )));
449 } else {
450 runtime.register_tool(Arc::new(WebSearchTool::new(
452 "mock".to_string(),
453 )));
454 }
455
456 tokio::spawn(async move {
457 if let Ok(result) =
458 runtime.process_trigger(&task, &context).await
459 {
460 println!(
461 "Agent Action [{}] -> {}",
462 label_str, result
463 );
464 }
466 });
467 }
468 }
469 }
470 }
471 }
472 }
473 NodeDeleted {
474 tenant_id: _,
475 id,
476 labels,
477 properties,
478 } => {
479 for (key, value) in properties {
480 for label in &labels {
481 property_index.index_remove(label, &key, &value, id);
482 }
483 }
484 }
485 PropertySet {
486 tenant_id,
487 id,
488 labels,
489 key,
490 old_value,
491 new_value,
492 } => {
493 if let Some(old) = old_value {
494 for label in &labels {
495 property_index.index_remove(label, &key, &old, id);
496 }
497 }
498 for label in &labels {
499 property_index.index_insert(label, &key, new_value.clone(), id);
500 }
501 if let PropertyValue::Vector(vec) = &new_value {
502 for label in &labels {
503 let _ = vector_index.add_vector(label.as_str(), &key, id, vec);
504 }
505 }
506
507 if let PropertyValue::String(text) = &new_value {
509 if let Ok(tenant) = tenant_manager.get_tenant(&tenant_id) {
510 if let Some(config) = tenant.embed_config {
511 for label in &labels {
512 if let Some(keys) =
513 config.embedding_policies.get(label.as_str())
514 {
515 if keys.contains(&key) {
516 let vector_index_clone = Arc::clone(&vector_index);
517 let label_str = label.as_str().to_string();
518 let key_clone = key.clone();
519 let text_clone = text.clone();
520 let config_clone = config.clone();
521
522 tokio::spawn(async move {
523 if let Ok(pipeline) =
524 crate::embed::EmbedPipeline::new(config_clone)
525 {
526 if let Ok(chunks) =
527 pipeline.process_text(&text_clone).await
528 {
529 if let Some(first) = chunks.first() {
530 let _ = vector_index_clone.add_vector(
531 &label_str,
532 &key_clone,
533 id,
534 &first.embedding,
535 );
536 }
537 }
538 }
539 });
540 }
541 }
542 }
543 }
544 }
545 }
546
547 if let Ok(tenant) = tenant_manager.get_tenant(&tenant_id) {
549 if let Some(agent_config) = tenant.agent_config {
550 if agent_config.enabled {
551 for label in &labels {
552 if let Some(trigger_prompt) =
553 agent_config.policies.get(label.as_str())
554 {
555 let context = format!(
556 "Node: {} (Property Updated: {})",
557 label.as_str(),
558 key
559 );
560 let task = trigger_prompt.clone();
561 let mut runtime = AgentRuntime::new(agent_config.clone());
562 let label_str = label.as_str().to_string();
563
564 if let Some(api_key) = &agent_config.api_key {
565 runtime.register_tool(Arc::new(WebSearchTool::new(
566 api_key.clone(),
567 )));
568 } else {
569 runtime.register_tool(Arc::new(WebSearchTool::new(
570 "mock".to_string(),
571 )));
572 }
573
574 tokio::spawn(async move {
575 if let Ok(result) =
576 runtime.process_trigger(&task, &context).await
577 {
578 println!(
579 "Agent Action [{}] -> {}",
580 label_str, result
581 );
582 }
583 });
584 }
585 }
586 }
587 }
588 }
589 }
590 LabelAdded {
591 tenant_id,
592 id,
593 label,
594 properties,
595 } => {
596 for (key, value) in properties {
597 if let PropertyValue::Vector(vec) = &value {
598 let _ = vector_index.add_vector(label.as_str(), &key, id, vec);
599 }
600 property_index.index_insert(&label, &key, value.clone(), id);
601
602 if let PropertyValue::String(text) = &value {
604 if let Ok(tenant) = tenant_manager.get_tenant(&tenant_id) {
605 if let Some(config) = tenant.embed_config {
606 if let Some(keys) =
607 config.embedding_policies.get(label.as_str())
608 {
609 if keys.contains(&key) {
610 let vector_index_clone = Arc::clone(&vector_index);
611 let label_str = label.as_str().to_string();
612 let key_clone = key.clone();
613 let text_clone = text.clone();
614 let config_clone = config.clone();
615
616 tokio::spawn(async move {
617 if let Ok(pipeline) =
618 crate::embed::EmbedPipeline::new(config_clone)
619 {
620 if let Ok(chunks) =
621 pipeline.process_text(&text_clone).await
622 {
623 if let Some(first) = chunks.first() {
624 let _ = vector_index_clone.add_vector(
625 &label_str,
626 &key_clone,
627 id,
628 &first.embedding,
629 );
630 }
631 }
632 }
633 });
634 }
635 }
636 }
637 }
638 }
639 }
640 }
641 }
642 }
643 }
644
645 pub fn create_node(&mut self, label: impl Into<Label>) -> NodeId {
647 let node_id_u64 = if let Some(id) = self.free_node_ids.pop() {
648 id
649 } else {
650 let id = self.next_node_id;
651 self.next_node_id += 1;
652 id
653 };
654 let node_id = NodeId::new(node_id_u64);
655 let idx = node_id_u64 as usize;
656
657 let label = label.into();
658 let mut node = Node::new(node_id, label.clone());
659 node.version = self.current_version;
660
661 self.label_index
663 .entry(label.clone())
664 .or_default()
665 .insert(node_id);
666
667 self.catalog.on_label_added(&label);
669
670 if idx >= self.nodes.len() {
672 self.nodes.resize(idx + 1, Vec::new());
673 self.outgoing.resize(idx + 1, Vec::new());
674 self.incoming.resize(idx + 1, Vec::new());
675 }
676
677 let event = crate::graph::event::IndexEvent::NodeCreated {
678 tenant_id: "default".to_string(),
679 id: node_id,
680 labels: node.labels.iter().cloned().collect(),
681 properties: node.properties.clone(),
682 };
683
684 if let Some(sender) = &self.index_sender {
685 let _ = sender.send(event);
686 } else {
687 self.handle_index_event(event, None);
688 }
689
690 self.nodes[idx].push(node);
691 node_id
692 }
693
694 pub fn create_node_with_properties(
696 &mut self,
697 tenant_id: &str,
698 labels: Vec<Label>,
699 properties: PropertyMap,
700 ) -> GraphResult<NodeId> {
701 for label in &labels {
703 for (key, value) in &properties {
704 if self.property_index.has_unique_constraint(label, key) {
705 if let Some(existing_ids) = self.property_index.lookup(label, key, value) {
706 if !existing_ids.is_empty() {
707 return Err(GraphError::ConstraintViolation(format!(
708 "Node already exists with label '{}' and {} = {:?}",
709 label.as_str(),
710 key,
711 value
712 )));
713 }
714 }
715 }
716 }
717 }
718
719 let node_id_u64 = if let Some(id) = self.free_node_ids.pop() {
720 id
721 } else {
722 let id = self.next_node_id;
723 self.next_node_id += 1;
724 id
725 };
726 let node_id = NodeId::new(node_id_u64);
727 let idx = node_id_u64 as usize;
728
729 for (key, value) in &properties {
731 self.node_columns.set_property(idx, key, value.clone());
732 }
733
734 let mut node = Node::new_with_properties(node_id, labels.clone(), properties);
735 node.version = self.current_version;
736
737 for label in &labels {
739 self.label_index
740 .entry(label.clone())
741 .or_default()
742 .insert(node_id);
743 self.catalog.on_label_added(label);
745 }
746
747 if idx >= self.nodes.len() {
749 self.nodes.resize(idx + 1, Vec::new());
750 self.outgoing.resize(idx + 1, Vec::new());
751 self.incoming.resize(idx + 1, Vec::new());
752 }
753
754 let event = crate::graph::event::IndexEvent::NodeCreated {
755 tenant_id: tenant_id.to_string(),
756 id: node_id,
757 labels: node.labels.iter().cloned().collect(),
758 properties: node.properties.clone(),
759 };
760
761 if let Some(sender) = &self.index_sender {
762 let _ = sender.send(event);
763 } else {
764 self.handle_index_event(event, None);
765 }
766
767 self.nodes[idx].push(node);
768 Ok(node_id)
769 }
770
771 pub fn get_node_at_version(&self, id: NodeId, version: u64) -> Option<&Node> {
773 let idx = id.as_u64() as usize;
774 let versions = self.nodes.get(idx)?;
775
776 versions.iter().rev().find(|n| n.version <= version)
779 }
780
781 pub fn get_node(&self, id: NodeId) -> Option<&Node> {
783 self.get_node_at_version(id, self.current_version)
784 }
785
786 pub fn get_node_mut(&mut self, id: NodeId) -> Option<&mut Node> {
788 self.nodes
789 .get_mut(id.as_u64() as usize)
790 .and_then(|v| v.last_mut())
791 }
792
793 pub fn has_node(&self, id: NodeId) -> bool {
795 self.get_node(id).is_some()
796 }
797
798 pub fn set_node_property(
800 &mut self,
801 tenant_id: &str,
802 node_id: NodeId,
803 key: impl Into<String>,
804 value: impl Into<PropertyValue>,
805 ) -> GraphResult<()> {
806 let key_str = key.into();
807 let val = value.into();
808 let idx = node_id.as_u64() as usize;
809
810 if let Some(node_versions) = self.nodes.get(idx) {
812 if let Some(node) = node_versions.last() {
813 for label in &node.labels {
814 if self.property_index.has_unique_constraint(label, &key_str) {
815 if let Some(existing_ids) =
817 self.property_index.lookup(label, &key_str, &val)
818 {
819 for existing_id in existing_ids {
820 if existing_id != node_id {
821 return Err(GraphError::ConstraintViolation(format!(
822 "Node already exists with label '{}' and {} = {:?}",
823 label.as_str(),
824 key_str,
825 val
826 )));
827 }
828 }
829 }
830 }
831 }
832 }
833 }
834
835 self.node_columns.set_property(idx, &key_str, val.clone());
837
838 let versions = self
840 .nodes
841 .get_mut(idx)
842 .ok_or(GraphError::NodeNotFound(node_id))?;
843 let latest_node = versions.last().ok_or(GraphError::NodeNotFound(node_id))?;
844
845 let old_val;
846
847 if latest_node.version < self.current_version {
848 let mut new_node = latest_node.clone();
850 new_node.version = self.current_version;
851 new_node.updated_at = chrono::Utc::now().timestamp_millis();
852 old_val = new_node.set_property(key_str.clone(), val.clone());
853 versions.push(new_node);
854 } else {
855 let node = versions.last_mut().unwrap();
857 old_val = node.set_property(key_str.clone(), val.clone());
858 }
859
860 let event = crate::graph::event::IndexEvent::PropertySet {
861 tenant_id: tenant_id.to_string(),
862 id: node_id,
863 labels: versions.last().unwrap().labels.iter().cloned().collect(),
864 key: key_str,
865 old_value: old_val,
866 new_value: val,
867 };
868
869 if let Some(sender) = &self.index_sender {
870 let _ = sender.send(event);
871 } else {
872 self.handle_index_event(event, None);
873 }
874
875 Ok(())
876 }
877
878 pub fn delete_node(&mut self, tenant_id: &str, id: NodeId) -> GraphResult<Node> {
880 let idx = id.as_u64() as usize;
881 let latest_node = self
882 .get_node(id)
883 .ok_or(GraphError::NodeNotFound(id))?
884 .clone();
885
886 self.free_node_ids.push(id.as_u64());
892
893 for label in &latest_node.labels {
899 if let Some(node_set) = self.label_index.get_mut(label) {
900 node_set.remove(&id);
901 }
902 self.catalog.on_label_removed(label);
903 }
904
905 let event = crate::graph::event::IndexEvent::NodeDeleted {
906 tenant_id: tenant_id.to_string(),
907 id,
908 labels: latest_node.labels.iter().cloned().collect(),
909 properties: latest_node.properties.clone(),
910 };
911
912 if let Some(sender) = &self.index_sender {
913 let _ = sender.send(event);
914 } else {
915 self.handle_index_event(event, None);
916 }
917
918 let node = self.nodes[idx].pop().unwrap();
921
922 let outgoing_edges: Vec<EdgeId> = std::mem::take(&mut self.outgoing[idx])
924 .into_iter()
925 .map(|(_, eid)| eid)
926 .collect();
927 let incoming_edges: Vec<EdgeId> = std::mem::take(&mut self.incoming[idx])
928 .into_iter()
929 .map(|(_, eid)| eid)
930 .collect();
931
932 for edge_id in outgoing_edges.iter().chain(incoming_edges.iter()) {
933 let _ = self.delete_edge(*edge_id);
934 }
935
936 Ok(node)
937 }
938
939 pub fn add_label_to_node(
945 &mut self,
946 tenant_id: &str,
947 node_id: NodeId,
948 label: impl Into<Label>,
949 ) -> GraphResult<()> {
950 let label = label.into();
951 let idx = node_id.as_u64() as usize;
952
953 let node = self
955 .nodes
956 .get_mut(idx)
957 .and_then(|v| v.last_mut())
958 .ok_or(GraphError::NodeNotFound(node_id))?;
959 node.add_label(label.clone());
960
961 self.label_index
963 .entry(label.clone())
964 .or_default()
965 .insert(node_id);
966
967 self.catalog.on_label_added(&label);
969
970 let event = crate::graph::event::IndexEvent::LabelAdded {
971 tenant_id: tenant_id.to_string(),
972 id: node_id,
973 label: label.clone(),
974 properties: node.properties.clone(),
975 };
976
977 if let Some(sender) = &self.index_sender {
978 let _ = sender.send(event);
979 } else {
980 self.handle_index_event(event, None);
981 }
982
983 Ok(())
984 }
985
986 pub fn create_edge(
988 &mut self,
989 source: NodeId,
990 target: NodeId,
991 edge_type: impl Into<EdgeType>,
992 ) -> GraphResult<EdgeId> {
993 if !self.has_node(source) {
995 return Err(GraphError::InvalidEdgeSource(source));
996 }
997 if !self.has_node(target) {
998 return Err(GraphError::InvalidEdgeTarget(target));
999 }
1000
1001 let edge_id_u64 = if let Some(id) = self.free_edge_ids.pop() {
1002 id
1003 } else {
1004 let id = self.next_edge_id;
1005 self.next_edge_id += 1;
1006 id
1007 };
1008 let edge_id = EdgeId::new(edge_id_u64);
1009 let idx = edge_id_u64 as usize;
1010
1011 let edge_type = edge_type.into();
1012 let mut edge = Edge::new(edge_id, source, target, edge_type.clone());
1013 edge.version = self.current_version;
1014
1015 {
1017 let out_list = &mut self.outgoing[source.as_u64() as usize];
1018 let pos = out_list
1019 .binary_search_by_key(&target, |(nid, _)| *nid)
1020 .unwrap_or_else(|p| p);
1021 out_list.insert(pos, (target, edge_id));
1022 }
1023 {
1024 let in_list = &mut self.incoming[target.as_u64() as usize];
1025 let pos = in_list
1026 .binary_search_by_key(&source, |(nid, _)| *nid)
1027 .unwrap_or_else(|p| p);
1028 in_list.insert(pos, (source, edge_id));
1029 }
1030
1031 if idx >= self.edges.len() {
1033 self.edges.resize(idx + 1, Vec::new());
1034 }
1035
1036 self.edge_type_index
1038 .entry(edge_type.clone())
1039 .or_default()
1040 .insert(edge_id);
1041
1042 let src_labels: Vec<Label> = self
1044 .get_node(source)
1045 .map(|n| n.labels.iter().cloned().collect())
1046 .unwrap_or_default();
1047 let tgt_labels: Vec<Label> = self
1048 .get_node(target)
1049 .map(|n| n.labels.iter().cloned().collect())
1050 .unwrap_or_default();
1051 self.catalog
1052 .on_edge_created(source, &src_labels, &edge_type, target, &tgt_labels);
1053
1054 self.edges[idx].push(edge);
1055 Ok(edge_id)
1056 }
1057
1058 pub fn create_edge_with_properties(
1060 &mut self,
1061 source: NodeId,
1062 target: NodeId,
1063 edge_type: impl Into<EdgeType>,
1064 properties: PropertyMap,
1065 ) -> GraphResult<EdgeId> {
1066 if !self.has_node(source) {
1068 return Err(GraphError::InvalidEdgeSource(source));
1069 }
1070 if !self.has_node(target) {
1071 return Err(GraphError::InvalidEdgeTarget(target));
1072 }
1073
1074 let edge_id_u64 = if let Some(id) = self.free_edge_ids.pop() {
1075 id
1076 } else {
1077 let id = self.next_edge_id;
1078 self.next_edge_id += 1;
1079 id
1080 };
1081 let edge_id = EdgeId::new(edge_id_u64);
1082 let idx = edge_id_u64 as usize;
1083
1084 for (key, value) in &properties {
1086 self.edge_columns.set_property(idx, key, value.clone());
1087 }
1088
1089 let edge_type = edge_type.into();
1090 let mut edge =
1091 Edge::new_with_properties(edge_id, source, target, edge_type.clone(), properties);
1092 edge.version = self.current_version;
1093
1094 {
1096 let out_list = &mut self.outgoing[source.as_u64() as usize];
1097 let pos = out_list
1098 .binary_search_by_key(&target, |(nid, _)| *nid)
1099 .unwrap_or_else(|p| p);
1100 out_list.insert(pos, (target, edge_id));
1101 }
1102 {
1103 let in_list = &mut self.incoming[target.as_u64() as usize];
1104 let pos = in_list
1105 .binary_search_by_key(&source, |(nid, _)| *nid)
1106 .unwrap_or_else(|p| p);
1107 in_list.insert(pos, (source, edge_id));
1108 }
1109
1110 if idx >= self.edges.len() {
1112 self.edges.resize(idx + 1, Vec::new());
1113 }
1114
1115 self.edge_type_index
1117 .entry(edge_type.clone())
1118 .or_default()
1119 .insert(edge_id);
1120
1121 let src_labels: Vec<Label> = self
1123 .get_node(source)
1124 .map(|n| n.labels.iter().cloned().collect())
1125 .unwrap_or_default();
1126 let tgt_labels: Vec<Label> = self
1127 .get_node(target)
1128 .map(|n| n.labels.iter().cloned().collect())
1129 .unwrap_or_default();
1130 self.catalog
1131 .on_edge_created(source, &src_labels, &edge_type, target, &tgt_labels);
1132
1133 self.edges[idx].push(edge);
1134 Ok(edge_id)
1135 }
1136
1137 pub fn get_edge_at_version(&self, id: EdgeId, version: u64) -> Option<&Edge> {
1139 let idx = id.as_u64() as usize;
1140 let versions = self.edges.get(idx)?;
1141
1142 versions.iter().rev().find(|e| e.version <= version)
1144 }
1145
1146 pub fn get_edge(&self, id: EdgeId) -> Option<&Edge> {
1148 self.get_edge_at_version(id, self.current_version)
1149 }
1150
1151 pub fn get_edge_mut(&mut self, id: EdgeId) -> Option<&mut Edge> {
1153 self.edges
1154 .get_mut(id.as_u64() as usize)
1155 .and_then(|v| v.last_mut())
1156 }
1157
1158 pub fn has_edge(&self, id: EdgeId) -> bool {
1160 self.get_edge(id).is_some()
1161 }
1162
1163 pub fn delete_edge(&mut self, id: EdgeId) -> GraphResult<Edge> {
1165 let idx = id.as_u64() as usize;
1166
1167 let (src_labels, tgt_labels, source_id, target_id, edge_type_clone) = {
1169 let edge = self
1170 .edges
1171 .get(idx)
1172 .and_then(|v| v.last())
1173 .ok_or(GraphError::EdgeNotFound(id))?;
1174 let src_labels: Vec<Label> = self
1175 .get_node(edge.source)
1176 .map(|n| n.labels.iter().cloned().collect())
1177 .unwrap_or_default();
1178 let tgt_labels: Vec<Label> = self
1179 .get_node(edge.target)
1180 .map(|n| n.labels.iter().cloned().collect())
1181 .unwrap_or_default();
1182 (
1183 src_labels,
1184 tgt_labels,
1185 edge.source,
1186 edge.target,
1187 edge.edge_type.clone(),
1188 )
1189 };
1190
1191 let edge = self
1192 .edges
1193 .get_mut(idx)
1194 .and_then(|v| v.pop())
1195 .ok_or(GraphError::EdgeNotFound(id))?;
1196
1197 self.free_edge_ids.push(id.as_u64());
1199
1200 if let Some(edge_set) = self.edge_type_index.get_mut(&edge.edge_type) {
1202 edge_set.remove(&id);
1203 }
1204
1205 if let Some(adj) = self.outgoing.get_mut(edge.source.as_u64() as usize) {
1207 adj.retain(|&(_, eid)| eid != id);
1208 }
1209 if let Some(adj) = self.incoming.get_mut(edge.target.as_u64() as usize) {
1210 adj.retain(|&(_, eid)| eid != id);
1211 }
1212
1213 self.catalog.on_edge_deleted(
1215 source_id,
1216 &src_labels,
1217 &edge_type_clone,
1218 target_id,
1219 &tgt_labels,
1220 );
1221
1222 Ok(edge)
1223 }
1224
1225 pub fn get_outgoing_edges(&self, node_id: NodeId) -> Vec<&Edge> {
1227 self.outgoing
1228 .get(node_id.as_u64() as usize)
1229 .map(|entries| {
1230 entries
1231 .iter()
1232 .filter_map(|&(_, eid)| self.get_edge(eid))
1233 .collect()
1234 })
1235 .unwrap_or_default()
1236 }
1237
1238 pub fn get_incoming_edges(&self, node_id: NodeId) -> Vec<&Edge> {
1240 self.incoming
1241 .get(node_id.as_u64() as usize)
1242 .map(|entries| {
1243 entries
1244 .iter()
1245 .filter_map(|&(_, eid)| self.get_edge(eid))
1246 .collect()
1247 })
1248 .unwrap_or_default()
1249 }
1250
1251 pub fn get_outgoing_edge_targets(
1254 &self,
1255 node_id: NodeId,
1256 ) -> Vec<(EdgeId, NodeId, NodeId, &EdgeType)> {
1257 self.outgoing
1258 .get(node_id.as_u64() as usize)
1259 .map(|entries| {
1260 entries
1261 .iter()
1262 .filter_map(|&(_, eid)| {
1263 self.get_edge(eid)
1264 .map(|e| (eid, e.source, e.target, &e.edge_type))
1265 })
1266 .collect()
1267 })
1268 .unwrap_or_default()
1269 }
1270
1271 pub fn get_incoming_edge_sources(
1274 &self,
1275 node_id: NodeId,
1276 ) -> Vec<(EdgeId, NodeId, NodeId, &EdgeType)> {
1277 self.incoming
1278 .get(node_id.as_u64() as usize)
1279 .map(|entries| {
1280 entries
1281 .iter()
1282 .filter_map(|&(_, eid)| {
1283 self.get_edge(eid)
1284 .map(|e| (eid, e.source, e.target, &e.edge_type))
1285 })
1286 .collect()
1287 })
1288 .unwrap_or_default()
1289 }
1290
1291 pub fn edge_between(
1295 &self,
1296 source: NodeId,
1297 target: NodeId,
1298 edge_type: Option<&EdgeType>,
1299 ) -> Option<EdgeId> {
1300 let out_len = self
1301 .outgoing
1302 .get(source.as_u64() as usize)
1303 .map(|v| v.len())
1304 .unwrap_or(0);
1305 let in_len = self
1306 .incoming
1307 .get(target.as_u64() as usize)
1308 .map(|v| v.len())
1309 .unwrap_or(0);
1310
1311 let (entries, search_key) = if out_len <= in_len {
1313 (self.outgoing.get(source.as_u64() as usize)?, target)
1314 } else {
1315 (self.incoming.get(target.as_u64() as usize)?, source)
1316 };
1317
1318 let start = match entries.binary_search_by_key(&search_key, |(nid, _)| *nid) {
1320 Ok(pos) => {
1321 let mut p = pos;
1323 while p > 0 && entries[p - 1].0 == search_key {
1324 p -= 1;
1325 }
1326 p
1327 }
1328 Err(_) => return None,
1329 };
1330
1331 for &(nid, eid) in entries.iter().skip(start) {
1333 if nid != search_key {
1334 break;
1335 }
1336 if let Some(e) = self.get_edge(eid) {
1337 if e.source == source && e.target == target {
1338 match edge_type {
1339 Some(et) if &e.edge_type != et => continue,
1340 _ => return Some(eid),
1341 }
1342 }
1343 }
1344 }
1345 None
1346 }
1347
1348 pub fn edges_between(
1351 &self,
1352 source: NodeId,
1353 target: NodeId,
1354 edge_type: Option<&EdgeType>,
1355 ) -> Vec<EdgeId> {
1356 let out_len = self
1357 .outgoing
1358 .get(source.as_u64() as usize)
1359 .map(|v| v.len())
1360 .unwrap_or(0);
1361 let in_len = self
1362 .incoming
1363 .get(target.as_u64() as usize)
1364 .map(|v| v.len())
1365 .unwrap_or(0);
1366
1367 let (entries, search_key) = if out_len <= in_len {
1368 match self.outgoing.get(source.as_u64() as usize) {
1369 Some(e) => (e, target),
1370 None => return Vec::new(),
1371 }
1372 } else {
1373 match self.incoming.get(target.as_u64() as usize) {
1374 Some(e) => (e, source),
1375 None => return Vec::new(),
1376 }
1377 };
1378
1379 let start = match entries.binary_search_by_key(&search_key, |(nid, _)| *nid) {
1380 Ok(pos) => {
1381 let mut p = pos;
1382 while p > 0 && entries[p - 1].0 == search_key {
1383 p -= 1;
1384 }
1385 p
1386 }
1387 Err(_) => return Vec::new(),
1388 };
1389
1390 let mut result = Vec::new();
1391 for &(nid, eid) in entries.iter().skip(start) {
1392 if nid != search_key {
1393 break;
1394 }
1395 if let Some(e) = self.get_edge(eid) {
1396 if e.source == source && e.target == target {
1397 match edge_type {
1398 Some(et) if &e.edge_type != et => {}
1399 _ => result.push(eid),
1400 }
1401 }
1402 }
1403 }
1404 result
1405 }
1406
1407 pub fn get_nodes_by_label(&self, label: &Label) -> Vec<&Node> {
1409 self.label_index
1410 .get(label)
1411 .map(|node_ids| {
1412 node_ids
1413 .iter()
1414 .filter_map(|&id| self.get_node(id))
1415 .collect()
1416 })
1417 .unwrap_or_default()
1418 }
1419
1420 pub fn get_edges_by_type(&self, edge_type: &EdgeType) -> Vec<&Edge> {
1422 self.edge_type_index
1423 .get(edge_type)
1424 .map(|edge_ids| {
1425 edge_ids
1426 .iter()
1427 .filter_map(|&id| self.get_edge(id))
1428 .collect()
1429 })
1430 .unwrap_or_default()
1431 }
1432
1433 pub fn node_count(&self) -> usize {
1435 self.nodes.iter().flatten().count()
1436 }
1437
1438 pub fn edge_count(&self) -> usize {
1440 self.edges.iter().flatten().count()
1441 }
1442
1443 pub fn all_nodes(&self) -> Vec<&Node> {
1445 self.nodes.iter().flatten().collect()
1446 }
1447
1448 pub fn all_edges(&self) -> Vec<&Edge> {
1450 self.edges.iter().flatten().collect()
1451 }
1452
1453 pub fn compute_statistics(&self) -> GraphStatistics {
1459 let total_nodes = self.node_count();
1460 let total_edges = self.edge_count();
1461
1462 let mut label_counts = HashMap::new();
1463 for (label, node_ids) in &self.label_index {
1464 label_counts.insert(label.clone(), node_ids.len());
1465 }
1466
1467 let mut edge_type_counts = HashMap::new();
1468 for (edge_type, edge_ids) in &self.edge_type_index {
1469 edge_type_counts.insert(edge_type.clone(), edge_ids.len());
1470 }
1471
1472 let avg_out_degree = if total_nodes > 0 {
1474 total_edges as f64 / total_nodes as f64
1475 } else {
1476 0.0
1477 };
1478
1479 let mut property_stats: HashMap<(Label, String), PropertyStats> = HashMap::new();
1481 for (label, node_ids) in &self.label_index {
1482 let sample_size = node_ids.len().min(1000);
1483 let mut property_presence: HashMap<String, usize> = HashMap::new();
1484 let mut property_distinct: HashMap<String, HashSet<u64>> = HashMap::new();
1485
1486 for (i, &node_id) in node_ids.iter().enumerate() {
1487 if i >= sample_size {
1488 break;
1489 }
1490 if let Some(node) = self.get_node(node_id) {
1491 for (key, val) in &node.properties {
1492 *property_presence.entry(key.clone()).or_insert(0) += 1;
1493
1494 let hash = {
1495 use std::hash::{Hash, Hasher};
1496 let mut hasher = std::collections::hash_map::DefaultHasher::new();
1497 val.hash(&mut hasher);
1498 hasher.finish()
1499 };
1500 property_distinct
1501 .entry(key.clone())
1502 .or_default()
1503 .insert(hash);
1504 }
1505 }
1506 }
1507
1508 for (prop, count) in &property_presence {
1509 let distinct = property_distinct.get(prop).map(|s| s.len()).unwrap_or(0);
1510 let selectivity = if distinct > 0 {
1511 1.0 / distinct as f64
1512 } else {
1513 1.0
1514 };
1515 property_stats.insert(
1516 (label.clone(), prop.clone()),
1517 PropertyStats {
1518 null_fraction: 1.0 - (*count as f64 / sample_size as f64),
1519 distinct_count: distinct,
1520 selectivity,
1521 },
1522 );
1523 }
1524 }
1525
1526 GraphStatistics {
1527 total_nodes,
1528 total_edges,
1529 label_counts,
1530 edge_type_counts,
1531 avg_out_degree,
1532 property_stats,
1533 }
1534 }
1535
1536 pub fn catalog(&self) -> &GraphCatalog {
1538 &self.catalog
1539 }
1540
1541 pub fn label_node_count(&self, label: &Label) -> usize {
1543 self.label_index.get(label).map(|s| s.len()).unwrap_or(0)
1544 }
1545
1546 pub fn edge_type_count(&self, edge_type: &EdgeType) -> usize {
1548 self.edge_type_index
1549 .get(edge_type)
1550 .map(|s| s.len())
1551 .unwrap_or(0)
1552 }
1553
1554 pub fn all_labels(&self) -> Vec<&Label> {
1556 self.label_index.keys().collect()
1557 }
1558
1559 pub fn all_edge_types(&self) -> Vec<&EdgeType> {
1561 self.edge_type_index.keys().collect()
1562 }
1563
1564 pub fn clear(&mut self) {
1566 self.nodes.clear();
1567 self.edges.clear();
1568 self.outgoing.clear();
1569 self.incoming.clear();
1570 self.free_node_ids.clear();
1571 self.free_edge_ids.clear();
1572 self.label_index.clear();
1573 self.edge_type_index.clear();
1574 self.vector_index = Arc::new(VectorIndexManager::new());
1575 self.property_index = Arc::new(IndexManager::new());
1576 self.node_columns = ColumnStore::new();
1577 self.edge_columns = ColumnStore::new();
1578 self.next_node_id = 1;
1579 self.next_edge_id = 1;
1580 self.catalog.clear();
1581 }
1582
1583 pub fn handle_index_event(
1588 &self,
1589 event: crate::graph::event::IndexEvent,
1590 _tenant_manager: Option<Arc<crate::persistence::TenantManager>>,
1591 ) {
1592 use crate::graph::event::IndexEvent::*;
1593 match event {
1594 NodeCreated {
1595 tenant_id: _,
1596 id,
1597 labels,
1598 properties,
1599 } => {
1600 for (key, value) in properties {
1601 if let PropertyValue::Vector(vec) = &value {
1602 for label in &labels {
1603 let _ = self.vector_index.add_vector(label.as_str(), &key, id, vec);
1604 }
1605 }
1606 for label in &labels {
1607 self.property_index
1608 .index_insert(label, &key, value.clone(), id);
1609 }
1610 }
1611 }
1612 NodeDeleted {
1613 tenant_id: _,
1614 id,
1615 labels,
1616 properties,
1617 } => {
1618 for (key, value) in properties {
1619 for label in &labels {
1620 self.property_index.index_remove(label, &key, &value, id);
1621 }
1622 }
1623 }
1624 PropertySet {
1625 tenant_id: _,
1626 id,
1627 labels,
1628 key,
1629 old_value,
1630 new_value,
1631 } => {
1632 if let Some(old) = old_value {
1633 for label in &labels {
1634 self.property_index.index_remove(label, &key, &old, id);
1635 }
1636 }
1637 for label in &labels {
1638 self.property_index
1639 .index_insert(label, &key, new_value.clone(), id);
1640 }
1641 if let PropertyValue::Vector(vec) = &new_value {
1642 for label in &labels {
1643 let _ = self.vector_index.add_vector(label.as_str(), &key, id, vec);
1644 }
1645 }
1646 }
1647 LabelAdded {
1648 tenant_id: _,
1649 id,
1650 label,
1651 properties,
1652 } => {
1653 for (key, value) in properties {
1654 if let PropertyValue::Vector(vec) = &value {
1655 let _ = self.vector_index.add_vector(label.as_str(), &key, id, vec);
1656 }
1657 self.property_index
1658 .index_insert(&label, &key, value.clone(), id);
1659 }
1660 }
1661 }
1662 }
1663
1664 pub fn create_vector_index(
1670 &self,
1671 label: &str,
1672 property_key: &str,
1673 dimensions: usize,
1674 metric: DistanceMetric,
1675 ) -> VectorResult<()> {
1676 self.vector_index
1677 .create_index(label, property_key, dimensions, metric)
1678 }
1679
1680 pub fn vector_search(
1682 &self,
1683 label: &str,
1684 property_key: &str,
1685 query: &[f32],
1686 k: usize,
1687 ) -> VectorResult<Vec<(NodeId, f32)>> {
1688 self.vector_index.search(label, property_key, query, k)
1689 }
1690
1691 pub fn insert_recovered_node(&mut self, node: Node) {
1698 let node_id = node.id;
1699 let idx = node_id.as_u64() as usize;
1700
1701 if idx >= self.nodes.len() {
1703 self.nodes.resize(idx + 1, Vec::new());
1704 self.outgoing.resize(idx + 1, Vec::new());
1705 self.incoming.resize(idx + 1, Vec::new());
1706 }
1707
1708 for label in &node.labels {
1710 self.label_index
1711 .entry(label.clone())
1712 .or_default()
1713 .insert(node_id);
1714 }
1715
1716 self.nodes[idx].push(node);
1718
1719 if node_id.as_u64() >= self.next_node_id {
1721 self.next_node_id = node_id.as_u64() + 1;
1722 }
1723 }
1724
1725 pub fn insert_recovered_edge(&mut self, edge: Edge) -> GraphResult<()> {
1729 let edge_id = edge.id;
1730 let idx = edge_id.as_u64() as usize;
1731 let source = edge.source;
1732 let target = edge.target;
1733
1734 if idx >= self.edges.len() {
1736 self.edges.resize(idx + 1, Vec::new());
1737 }
1738
1739 if !self.has_node(source) {
1741 return Err(GraphError::InvalidEdgeSource(source));
1742 }
1743 if !self.has_node(target) {
1744 return Err(GraphError::InvalidEdgeTarget(target));
1745 }
1746
1747 {
1749 let out_list = &mut self.outgoing[source.as_u64() as usize];
1750 let pos = out_list
1751 .binary_search_by_key(&target, |(nid, _)| *nid)
1752 .unwrap_or_else(|p| p);
1753 out_list.insert(pos, (target, edge_id));
1754 }
1755 {
1756 let in_list = &mut self.incoming[target.as_u64() as usize];
1757 let pos = in_list
1758 .binary_search_by_key(&source, |(nid, _)| *nid)
1759 .unwrap_or_else(|p| p);
1760 in_list.insert(pos, (source, edge_id));
1761 }
1762
1763 self.edge_type_index
1765 .entry(edge.edge_type.clone())
1766 .or_default()
1767 .insert(edge_id);
1768
1769 self.edges[idx].push(edge);
1771
1772 if edge_id.as_u64() >= self.next_edge_id {
1774 self.next_edge_id = edge_id.as_u64() + 1;
1775 }
1776
1777 Ok(())
1778 }
1779}
1780
1781impl Default for GraphStore {
1782 fn default() -> Self {
1783 Self::new()
1784 }
1785}
1786
1787#[cfg(test)]
1788mod tests {
1789 use super::*;
1790
1791 #[test]
1792 fn test_create_and_get_node() {
1793 let mut store = GraphStore::new();
1794 let node_id = store.create_node("Person");
1795
1796 assert_eq!(store.node_count(), 1);
1797 let node = store.get_node(node_id).unwrap();
1798 assert_eq!(node.id, node_id);
1799 assert!(node.has_label(&Label::new("Person")));
1800 }
1801
1802 #[test]
1803 fn test_create_node_with_properties() {
1804 let mut store = GraphStore::new();
1805 let mut props = PropertyMap::new();
1806 props.insert("name".to_string(), "Alice".into());
1807 props.insert("age".to_string(), 30i64.into());
1808
1809 let node_id = store
1810 .create_node_with_properties(
1811 "default",
1812 vec![Label::new("Person"), Label::new("Employee")],
1813 props,
1814 )
1815 .unwrap();
1816
1817 let node = store.get_node(node_id).unwrap();
1818 assert_eq!(node.label_count(), 2);
1819 assert_eq!(
1820 node.get_property("name").unwrap().as_string(),
1821 Some("Alice")
1822 );
1823 assert_eq!(node.get_property("age").unwrap().as_integer(), Some(30));
1824 }
1825
1826 #[test]
1827 fn test_create_and_get_edge() {
1828 let mut store = GraphStore::new();
1829 let node1 = store.create_node("Person");
1830 let node2 = store.create_node("Person");
1831
1832 let edge_id = store.create_edge(node1, node2, "KNOWS").unwrap();
1833
1834 assert_eq!(store.edge_count(), 1);
1835 let edge = store.get_edge(edge_id).unwrap();
1836 assert_eq!(edge.source, node1);
1837 assert_eq!(edge.target, node2);
1838 assert_eq!(edge.edge_type, EdgeType::new("KNOWS"));
1839 }
1840
1841 #[test]
1842 fn test_edge_validation() {
1843 let mut store = GraphStore::new();
1844 let node1 = store.create_node("Person");
1845 let invalid_node = NodeId::new(999);
1846
1847 let result = store.create_edge(invalid_node, node1, "KNOWS");
1849 assert_eq!(result, Err(GraphError::InvalidEdgeSource(invalid_node)));
1850
1851 let result = store.create_edge(node1, invalid_node, "KNOWS");
1853 assert_eq!(result, Err(GraphError::InvalidEdgeTarget(invalid_node)));
1854 }
1855
1856 #[test]
1857 fn test_adjacency_lists() {
1858 let mut store = GraphStore::new();
1859 let node1 = store.create_node("Person");
1860 let node2 = store.create_node("Person");
1861 let node3 = store.create_node("Person");
1862
1863 store.create_edge(node1, node2, "KNOWS").unwrap();
1864 store.create_edge(node1, node3, "KNOWS").unwrap();
1865 store.create_edge(node2, node3, "FOLLOWS").unwrap();
1866
1867 let outgoing = store.get_outgoing_edges(node1);
1869 assert_eq!(outgoing.len(), 2);
1870
1871 let outgoing = store.get_outgoing_edges(node2);
1873 assert_eq!(outgoing.len(), 1);
1874 let incoming = store.get_incoming_edges(node2);
1875 assert_eq!(incoming.len(), 1);
1876
1877 let outgoing = store.get_outgoing_edges(node3);
1879 assert_eq!(outgoing.len(), 0);
1880 let incoming = store.get_incoming_edges(node3);
1881 assert_eq!(incoming.len(), 2);
1882 }
1883
1884 #[test]
1885 fn test_label_index() {
1886 let mut store = GraphStore::new();
1887 store.create_node("Person");
1888 store.create_node("Person");
1889 store.create_node("Company");
1890
1891 let persons = store.get_nodes_by_label(&Label::new("Person"));
1892 assert_eq!(persons.len(), 2);
1893
1894 let companies = store.get_nodes_by_label(&Label::new("Company"));
1895 assert_eq!(companies.len(), 1);
1896 }
1897
1898 #[test]
1899 fn test_edge_type_index() {
1900 let mut store = GraphStore::new();
1901 let n1 = store.create_node("Person");
1902 let n2 = store.create_node("Person");
1903 let n3 = store.create_node("Person");
1904
1905 store.create_edge(n1, n2, "KNOWS").unwrap();
1906 store.create_edge(n2, n3, "KNOWS").unwrap();
1907 store.create_edge(n1, n3, "FOLLOWS").unwrap();
1908
1909 let knows_edges = store.get_edges_by_type(&EdgeType::new("KNOWS"));
1910 assert_eq!(knows_edges.len(), 2);
1911
1912 let follows_edges = store.get_edges_by_type(&EdgeType::new("FOLLOWS"));
1913 assert_eq!(follows_edges.len(), 1);
1914 }
1915
1916 #[test]
1917 fn test_delete_node() {
1918 let mut store = GraphStore::new();
1919 let node1 = store.create_node("Person");
1920 let node2 = store.create_node("Person");
1921 store.create_edge(node1, node2, "KNOWS").unwrap();
1922
1923 assert_eq!(store.node_count(), 2);
1924 assert_eq!(store.edge_count(), 1);
1925
1926 let deleted = store.delete_node("default", node1);
1928 assert!(deleted.is_ok());
1929 assert_eq!(store.node_count(), 1);
1930 assert_eq!(store.edge_count(), 0);
1931 }
1932
1933 #[test]
1934 fn test_delete_edge() {
1935 let mut store = GraphStore::new();
1936 let node1 = store.create_node("Person");
1937 let node2 = store.create_node("Person");
1938 let edge_id = store.create_edge(node1, node2, "KNOWS").unwrap();
1939
1940 assert_eq!(store.edge_count(), 1);
1941
1942 let deleted = store.delete_edge(edge_id);
1943 assert!(deleted.is_ok());
1944 assert_eq!(store.edge_count(), 0);
1945
1946 assert_eq!(store.get_outgoing_edges(node1).len(), 0);
1948 assert_eq!(store.get_incoming_edges(node2).len(), 0);
1949 }
1950
1951 #[test]
1952 fn test_multiple_edges_between_nodes() {
1953 let mut store = GraphStore::new();
1955 let node1 = store.create_node("Person");
1956 let node2 = store.create_node("Person");
1957
1958 let edge1 = store.create_edge(node1, node2, "KNOWS").unwrap();
1959 let edge2 = store.create_edge(node1, node2, "WORKS_WITH").unwrap();
1960 let edge3 = store.create_edge(node1, node2, "KNOWS").unwrap();
1961
1962 assert_eq!(store.edge_count(), 3);
1963 assert_ne!(edge1, edge2);
1964 assert_ne!(edge1, edge3);
1965
1966 let outgoing = store.get_outgoing_edges(node1);
1967 assert_eq!(outgoing.len(), 3);
1968 }
1969
1970 #[test]
1971 fn test_clear() {
1972 let mut store = GraphStore::new();
1973 store.create_node("Person");
1974 store.create_node("Person");
1975
1976 assert_eq!(store.node_count(), 2);
1977
1978 store.clear();
1979 assert_eq!(store.node_count(), 0);
1980 assert_eq!(store.edge_count(), 0);
1981 }
1982
1983 #[test]
1984 fn test_add_label_to_node() {
1985 let mut store = GraphStore::new();
1986 let node_id = store.create_node("Person");
1987
1988 assert_eq!(store.get_nodes_by_label(&Label::new("Person")).len(), 1);
1990 assert_eq!(store.get_nodes_by_label(&Label::new("Employee")).len(), 0);
1991
1992 store
1994 .add_label_to_node("default", node_id, "Employee")
1995 .unwrap();
1996
1997 assert_eq!(store.get_nodes_by_label(&Label::new("Person")).len(), 1);
1999 assert_eq!(store.get_nodes_by_label(&Label::new("Employee")).len(), 1);
2000
2001 let node = store.get_node(node_id).unwrap();
2003 assert!(node.has_label(&Label::new("Person")));
2004 assert!(node.has_label(&Label::new("Employee")));
2005 }
2006
2007 #[test]
2008 fn test_add_label_to_nonexistent_node() {
2009 let mut store = GraphStore::new();
2010 let invalid_id = NodeId::new(999);
2011
2012 let result = store.add_label_to_node("default", invalid_id, "Employee");
2013 assert_eq!(result, Err(GraphError::NodeNotFound(invalid_id)));
2014 }
2015
2016 #[test]
2017 fn test_mvcc_node_versioning() {
2018 let mut store = GraphStore::new();
2019
2020 let node_id = store.create_node("Person");
2022 store
2023 .set_node_property("default", node_id, "name", "Alice")
2024 .unwrap();
2025
2026 let v1_node = store.get_node_at_version(node_id, 1).unwrap();
2028 assert_eq!(v1_node.version, 1);
2029 assert_eq!(
2030 v1_node.get_property("name").unwrap().as_string(),
2031 Some("Alice")
2032 );
2033
2034 store.current_version = 2;
2036 store
2037 .set_node_property("default", node_id, "name", "Alice Cooper")
2038 .unwrap();
2039
2040 let v2_node = store.get_node_at_version(node_id, 2).unwrap();
2042 assert_eq!(v2_node.version, 2);
2043 assert_eq!(
2044 v2_node.get_property("name").unwrap().as_string(),
2045 Some("Alice Cooper")
2046 );
2047
2048 let v1_lookup = store.get_node_at_version(node_id, 1).unwrap();
2050 assert_eq!(v1_lookup.version, 1);
2051 assert_eq!(
2052 v1_lookup.get_property("name").unwrap().as_string(),
2053 Some("Alice")
2054 );
2055
2056 let node = store.get_node(node_id).unwrap();
2057 assert_eq!(node.version, 2); }
2059
2060 #[test]
2061 fn test_arena_resize() {
2062 let mut store = GraphStore::new();
2063 for _ in 0..1100 {
2067 store.create_node("Item");
2068 }
2069
2070 assert_eq!(store.node_count(), 1100);
2071 let last_id = NodeId::new(1100);
2072 assert!(store.has_node(last_id));
2073 }
2074
2075 #[test]
2076 fn test_id_reuse() {
2077 let mut store = GraphStore::new();
2078 let n1 = store.create_node("A");
2079 let _n2 = store.create_node("B");
2080
2081 store.delete_node("default", n1).unwrap();
2082
2083 let n3 = store.create_node("C");
2086
2087 assert_eq!(n3, n1); assert_eq!(store.node_count(), 2); }
2090
2091 #[test]
2094 fn test_get_node() {
2095 let mut store = GraphStore::new();
2096 let id = store.create_node("Person");
2097 let node = store.get_node(id);
2098 assert!(node.is_some());
2099 assert!(node.unwrap().labels.contains(&Label::new("Person")));
2100
2101 assert!(store.get_node(NodeId::new(9999)).is_none());
2103 }
2104
2105 #[test]
2106 fn test_get_node_mut() {
2107 let mut store = GraphStore::new();
2108 let id = store.create_node("Person");
2109 {
2110 let node = store.get_node_mut(id).unwrap();
2111 node.set_property(
2112 "name".to_string(),
2113 PropertyValue::String("Alice".to_string()),
2114 );
2115 }
2116 let node = store.get_node(id).unwrap();
2117 assert_eq!(
2118 node.get_property("name"),
2119 Some(&PropertyValue::String("Alice".to_string()))
2120 );
2121
2122 assert!(store.get_node_mut(NodeId::new(9999)).is_none());
2124 }
2125
2126 #[test]
2127 fn test_has_node() {
2128 let mut store = GraphStore::new();
2129 let id = store.create_node("A");
2130 assert!(store.has_node(id));
2131 store.delete_node("default", id).unwrap();
2132 assert!(!store.has_node(id));
2133 }
2134
2135 #[test]
2136 fn test_set_node_property() {
2137 let mut store = GraphStore::new();
2138 let id = store.create_node("Person");
2139 store
2140 .set_node_property("default", id, "age", PropertyValue::Integer(30))
2141 .unwrap();
2142 let node = store.get_node(id).unwrap();
2143 assert_eq!(node.get_property("age"), Some(&PropertyValue::Integer(30)));
2144
2145 store
2147 .set_node_property("default", id, "age", PropertyValue::Integer(31))
2148 .unwrap();
2149 let node = store.get_node(id).unwrap();
2150 assert_eq!(node.get_property("age"), Some(&PropertyValue::Integer(31)));
2151
2152 let result =
2154 store.set_node_property("default", NodeId::new(9999), "x", PropertyValue::Null);
2155 assert!(result.is_err());
2156 }
2157
2158 #[test]
2159 fn test_create_edge_with_properties() {
2160 let mut store = GraphStore::new();
2161 let a = store.create_node("Person");
2162 let b = store.create_node("Person");
2163
2164 let mut props = std::collections::HashMap::new();
2165 props.insert("since".to_string(), PropertyValue::Integer(2020));
2166 props.insert("weight".to_string(), PropertyValue::Float(0.8));
2167
2168 let eid = store
2169 .create_edge_with_properties(a, b, "KNOWS", props)
2170 .unwrap();
2171 let edge = store.get_edge(eid).unwrap();
2172 assert_eq!(edge.source, a);
2173 assert_eq!(edge.target, b);
2174 assert_eq!(
2175 edge.get_property("since"),
2176 Some(&PropertyValue::Integer(2020))
2177 );
2178 assert_eq!(
2179 edge.get_property("weight"),
2180 Some(&PropertyValue::Float(0.8))
2181 );
2182 }
2183
2184 #[test]
2185 fn test_create_edge_with_properties_invalid_nodes() {
2186 let mut store = GraphStore::new();
2187 let a = store.create_node("A");
2188 let props = std::collections::HashMap::new();
2189
2190 let result = store.create_edge_with_properties(a, NodeId::new(9999), "E", props.clone());
2192 assert!(result.is_err());
2193
2194 let result = store.create_edge_with_properties(NodeId::new(9999), a, "E", props);
2196 assert!(result.is_err());
2197 }
2198
2199 #[test]
2200 fn test_get_edge_and_has_edge() {
2201 let mut store = GraphStore::new();
2202 let a = store.create_node("A");
2203 let b = store.create_node("B");
2204 let eid = store.create_edge(a, b, "LINKS").unwrap();
2205
2206 assert!(store.has_edge(eid));
2207 let edge = store.get_edge(eid).unwrap();
2208 assert_eq!(edge.source, a);
2209 assert_eq!(edge.target, b);
2210
2211 assert!(!store.has_edge(EdgeId::new(9999)));
2213 assert!(store.get_edge(EdgeId::new(9999)).is_none());
2214 }
2215
2216 #[test]
2217 fn test_get_edge_mut() {
2218 let mut store = GraphStore::new();
2219 let a = store.create_node("A");
2220 let b = store.create_node("B");
2221 let eid = store.create_edge(a, b, "LINKS").unwrap();
2222
2223 {
2224 let edge = store.get_edge_mut(eid).unwrap();
2225 edge.set_property("weight".to_string(), PropertyValue::Float(1.5));
2226 }
2227 let edge = store.get_edge(eid).unwrap();
2228 assert_eq!(
2229 edge.get_property("weight"),
2230 Some(&PropertyValue::Float(1.5))
2231 );
2232
2233 assert!(store.get_edge_mut(EdgeId::new(9999)).is_none());
2234 }
2235
2236 #[test]
2237 fn test_get_outgoing_edge_targets() {
2238 let mut store = GraphStore::new();
2239 let a = store.create_node("A");
2240 let b = store.create_node("B");
2241 let c = store.create_node("C");
2242 let e1 = store.create_edge(a, b, "KNOWS").unwrap();
2243 let e2 = store.create_edge(a, c, "LIKES").unwrap();
2244
2245 let targets = store.get_outgoing_edge_targets(a);
2246 assert_eq!(targets.len(), 2);
2247 let edge_ids: Vec<EdgeId> = targets.iter().map(|t| t.0).collect();
2249 assert!(edge_ids.contains(&e1));
2250 assert!(edge_ids.contains(&e2));
2251
2252 let targets = store.get_outgoing_edge_targets(b);
2254 assert!(targets.is_empty());
2255 }
2256
2257 #[test]
2258 fn test_get_incoming_edge_sources() {
2259 let mut store = GraphStore::new();
2260 let a = store.create_node("A");
2261 let b = store.create_node("B");
2262 let c = store.create_node("C");
2263 store.create_edge(a, c, "KNOWS").unwrap();
2264 store.create_edge(b, c, "LIKES").unwrap();
2265
2266 let sources = store.get_incoming_edge_sources(c);
2267 assert_eq!(sources.len(), 2);
2268 let src_nodes: Vec<NodeId> = sources.iter().map(|t| t.1).collect();
2269 assert!(src_nodes.contains(&a));
2270 assert!(src_nodes.contains(&b));
2271
2272 let sources = store.get_incoming_edge_sources(a);
2274 assert!(sources.is_empty());
2275 }
2276
2277 #[test]
2278 fn test_all_nodes() {
2279 let mut store = GraphStore::new();
2280 assert!(store.all_nodes().is_empty());
2281
2282 store.create_node("A");
2283 store.create_node("B");
2284 store.create_node("C");
2285 assert_eq!(store.all_nodes().len(), 3);
2286 }
2287
2288 #[test]
2289 fn test_all_edges() {
2290 let mut store = GraphStore::new();
2291 assert!(store.all_edges().is_empty());
2292
2293 let a = store.create_node("A");
2294 let b = store.create_node("B");
2295 let c = store.create_node("C");
2296 store.create_edge(a, b, "R1").unwrap();
2297 store.create_edge(b, c, "R2").unwrap();
2298 assert_eq!(store.all_edges().len(), 2);
2299 }
2300
2301 #[test]
2302 fn test_compute_statistics() {
2303 let mut store = GraphStore::new();
2304 let a = store.create_node("Person");
2305 let b = store.create_node("Person");
2306 let c = store.create_node("Company");
2307 store.get_node_mut(a).unwrap().set_property(
2308 "name".to_string(),
2309 PropertyValue::String("Alice".to_string()),
2310 );
2311 store
2312 .get_node_mut(b)
2313 .unwrap()
2314 .set_property("name".to_string(), PropertyValue::String("Bob".to_string()));
2315 store.get_node_mut(c).unwrap().set_property(
2316 "name".to_string(),
2317 PropertyValue::String("Acme".to_string()),
2318 );
2319 store.create_edge(a, b, "KNOWS").unwrap();
2320 store.create_edge(a, c, "WORKS_AT").unwrap();
2321
2322 let stats = store.compute_statistics();
2323 assert_eq!(stats.total_nodes, 3);
2324 assert_eq!(stats.total_edges, 2);
2325 assert_eq!(*stats.label_counts.get(&Label::new("Person")).unwrap(), 2);
2326 assert_eq!(*stats.label_counts.get(&Label::new("Company")).unwrap(), 1);
2327 assert_eq!(
2328 *stats.edge_type_counts.get(&EdgeType::new("KNOWS")).unwrap(),
2329 1
2330 );
2331 assert_eq!(
2332 *stats
2333 .edge_type_counts
2334 .get(&EdgeType::new("WORKS_AT"))
2335 .unwrap(),
2336 1
2337 );
2338 assert!(stats.avg_out_degree > 0.0);
2339 let person_name_stats = stats
2341 .property_stats
2342 .get(&(Label::new("Person"), "name".to_string()));
2343 assert!(person_name_stats.is_some());
2344 let ps = person_name_stats.unwrap();
2345 assert_eq!(ps.null_fraction, 0.0); assert_eq!(ps.distinct_count, 2); }
2348
2349 #[test]
2350 fn test_compute_statistics_empty_graph() {
2351 let store = GraphStore::new();
2352 let stats = store.compute_statistics();
2353 assert_eq!(stats.total_nodes, 0);
2354 assert_eq!(stats.total_edges, 0);
2355 assert_eq!(stats.avg_out_degree, 0.0);
2356 assert!(stats.label_counts.is_empty());
2357 assert!(stats.edge_type_counts.is_empty());
2358 }
2359
2360 #[test]
2361 fn test_label_node_count() {
2362 let mut store = GraphStore::new();
2363 store.create_node("Person");
2364 store.create_node("Person");
2365 store.create_node("Company");
2366
2367 assert_eq!(store.label_node_count(&Label::new("Person")), 2);
2368 assert_eq!(store.label_node_count(&Label::new("Company")), 1);
2369 assert_eq!(store.label_node_count(&Label::new("NotExist")), 0);
2370 }
2371
2372 #[test]
2373 fn test_edge_type_count() {
2374 let mut store = GraphStore::new();
2375 let a = store.create_node("A");
2376 let b = store.create_node("B");
2377 let c = store.create_node("C");
2378 store.create_edge(a, b, "KNOWS").unwrap();
2379 store.create_edge(a, c, "KNOWS").unwrap();
2380 store.create_edge(b, c, "LIKES").unwrap();
2381
2382 assert_eq!(store.edge_type_count(&EdgeType::new("KNOWS")), 2);
2383 assert_eq!(store.edge_type_count(&EdgeType::new("LIKES")), 1);
2384 assert_eq!(store.edge_type_count(&EdgeType::new("NOPE")), 0);
2385 }
2386
2387 #[test]
2388 fn test_all_labels() {
2389 let mut store = GraphStore::new();
2390 store.create_node("Person");
2391 store.create_node("Company");
2392 store.create_node("Person"); let labels = store.all_labels();
2395 assert_eq!(labels.len(), 2);
2396 let label_strs: Vec<&str> = labels.iter().map(|l| l.as_str()).collect();
2397 assert!(label_strs.contains(&"Person"));
2398 assert!(label_strs.contains(&"Company"));
2399 }
2400
2401 #[test]
2402 fn test_all_edge_types() {
2403 let mut store = GraphStore::new();
2404 let a = store.create_node("A");
2405 let b = store.create_node("B");
2406 let c = store.create_node("C");
2407 store.create_edge(a, b, "KNOWS").unwrap();
2408 store.create_edge(b, c, "LIKES").unwrap();
2409
2410 let types = store.all_edge_types();
2411 assert_eq!(types.len(), 2);
2412 let type_strs: Vec<&str> = types.iter().map(|t| t.as_str()).collect();
2413 assert!(type_strs.contains(&"KNOWS"));
2414 assert!(type_strs.contains(&"LIKES"));
2415 }
2416
2417 #[test]
2418 fn test_get_node_at_version() {
2419 let mut store = GraphStore::new();
2420 let id = store.create_node("Person");
2421 let v0 = store.get_node(id).unwrap().version;
2422
2423 let node = store.get_node_at_version(id, v0);
2425 assert!(node.is_some());
2426 assert!(node.unwrap().labels.contains(&Label::new("Person")));
2427
2428 if v0 > 0 {
2431 assert!(store.get_node_at_version(id, v0 - 1).is_none());
2432 }
2433
2434 assert!(store.get_node_at_version(NodeId::new(9999), 0).is_none());
2436 }
2437
2438 #[test]
2439 fn test_get_edge_at_version() {
2440 let mut store = GraphStore::new();
2441 let a = store.create_node("A");
2442 let b = store.create_node("B");
2443 let eid = store.create_edge(a, b, "KNOWS").unwrap();
2444 let v0 = store.get_edge(eid).unwrap().version;
2445
2446 assert!(store.get_edge_at_version(eid, v0).is_some());
2448
2449 assert!(store.get_edge_at_version(EdgeId::new(9999), 0).is_none());
2451 }
2452
2453 #[test]
2454 fn test_create_vector_index() {
2455 let store = GraphStore::new();
2456 let result = store.create_vector_index(
2457 "Person",
2458 "embedding",
2459 128,
2460 crate::vector::DistanceMetric::Cosine,
2461 );
2462 assert!(result.is_ok());
2463
2464 let result2 =
2466 store.create_vector_index("Document", "vec", 256, crate::vector::DistanceMetric::L2);
2467 assert!(result2.is_ok());
2468 }
2469
2470 #[test]
2471 fn test_set_node_property_updates_in_place() {
2472 let mut store = GraphStore::new();
2473 let id = store.create_node("Person");
2474 store
2475 .set_node_property(
2476 "default",
2477 id,
2478 "name",
2479 PropertyValue::String("Alice".to_string()),
2480 )
2481 .unwrap();
2482
2483 store
2485 .set_node_property(
2486 "default",
2487 id,
2488 "name",
2489 PropertyValue::String("Bob".to_string()),
2490 )
2491 .unwrap();
2492 let node = store.get_node(id).unwrap();
2493 assert_eq!(
2494 node.get_property("name"),
2495 Some(&PropertyValue::String("Bob".to_string()))
2496 );
2497
2498 store
2500 .set_node_property("default", id, "age", PropertyValue::Integer(30))
2501 .unwrap();
2502 let node = store.get_node(id).unwrap();
2503 assert_eq!(node.get_property("age"), Some(&PropertyValue::Integer(30)));
2504 assert_eq!(
2505 node.get_property("name"),
2506 Some(&PropertyValue::String("Bob".to_string()))
2507 );
2508 }
2509
2510 #[test]
2513 fn test_graph_error_display_node_not_found() {
2514 let err = GraphError::NodeNotFound(NodeId::new(42));
2515 assert_eq!(format!("{}", err), "Node NodeId(42) not found");
2516 }
2517
2518 #[test]
2519 fn test_graph_error_display_edge_not_found() {
2520 let err = GraphError::EdgeNotFound(EdgeId::new(7));
2521 assert_eq!(format!("{}", err), "Edge EdgeId(7) not found");
2522 }
2523
2524 #[test]
2525 fn test_graph_error_display_node_already_exists() {
2526 let err = GraphError::NodeAlreadyExists(NodeId::new(1));
2527 assert_eq!(format!("{}", err), "Node NodeId(1) already exists");
2528 }
2529
2530 #[test]
2531 fn test_graph_error_display_edge_already_exists() {
2532 let err = GraphError::EdgeAlreadyExists(EdgeId::new(3));
2533 assert_eq!(format!("{}", err), "Edge EdgeId(3) already exists");
2534 }
2535
2536 #[test]
2537 fn test_graph_error_display_invalid_edge_source() {
2538 let err = GraphError::InvalidEdgeSource(NodeId::new(99));
2539 assert_eq!(
2540 format!("{}", err),
2541 "Invalid edge: source node NodeId(99) does not exist"
2542 );
2543 }
2544
2545 #[test]
2546 fn test_graph_error_display_invalid_edge_target() {
2547 let err = GraphError::InvalidEdgeTarget(NodeId::new(88));
2548 assert_eq!(
2549 format!("{}", err),
2550 "Invalid edge: target node NodeId(88) does not exist"
2551 );
2552 }
2553
2554 #[test]
2555 fn test_graph_error_equality() {
2556 assert_eq!(
2557 GraphError::NodeNotFound(NodeId::new(1)),
2558 GraphError::NodeNotFound(NodeId::new(1))
2559 );
2560 assert_ne!(
2561 GraphError::NodeNotFound(NodeId::new(1)),
2562 GraphError::NodeNotFound(NodeId::new(2))
2563 );
2564 assert_ne!(
2565 GraphError::NodeNotFound(NodeId::new(1)),
2566 GraphError::EdgeNotFound(EdgeId::new(1))
2567 );
2568 }
2569
2570 #[test]
2571 fn test_graph_statistics_estimate_label_scan() {
2572 let mut store = GraphStore::new();
2573 for _ in 0..50 {
2574 store.create_node("Person");
2575 }
2576 for _ in 0..20 {
2577 store.create_node("Company");
2578 }
2579 let stats = store.compute_statistics();
2580 assert_eq!(stats.estimate_label_scan(&Label::new("Person")), 50);
2581 assert_eq!(stats.estimate_label_scan(&Label::new("Company")), 20);
2582 assert_eq!(stats.estimate_label_scan(&Label::new("Unknown")), 70);
2584 }
2585
2586 #[test]
2587 fn test_graph_statistics_estimate_expand() {
2588 let mut store = GraphStore::new();
2589 let a = store.create_node("A");
2590 let b = store.create_node("B");
2591 let c = store.create_node("C");
2592 store.create_edge(a, b, "KNOWS").unwrap();
2593 store.create_edge(a, c, "KNOWS").unwrap();
2594 store.create_edge(b, c, "LIKES").unwrap();
2595
2596 let stats = store.compute_statistics();
2597 assert_eq!(
2598 stats.estimate_expand(Some(&EdgeType::new("KNOWS"))) as usize,
2599 2
2600 );
2601 assert_eq!(
2602 stats.estimate_expand(Some(&EdgeType::new("LIKES"))) as usize,
2603 1
2604 );
2605 assert_eq!(
2606 stats.estimate_expand(Some(&EdgeType::new("NOPE"))) as usize,
2607 0
2608 );
2609 assert_eq!(stats.estimate_expand(None) as usize, 3);
2611 }
2612
2613 #[test]
2614 fn test_graph_statistics_estimate_equality_selectivity() {
2615 let mut store = GraphStore::new();
2616 for i in 0..10 {
2617 let id = store.create_node("Person");
2618 store.get_node_mut(id).unwrap().set_property(
2619 "city".to_string(),
2620 PropertyValue::String(format!("City{}", i % 5)),
2621 );
2622 }
2623 let stats = store.compute_statistics();
2624 let sel = stats.estimate_equality_selectivity(&Label::new("Person"), "city");
2626 assert!((sel - 0.2).abs() < 0.01);
2627 let default_sel =
2629 stats.estimate_equality_selectivity(&Label::new("Person"), "unknown_prop");
2630 assert!((default_sel - 0.1).abs() < 0.01);
2631 let default_sel2 = stats.estimate_equality_selectivity(&Label::new("NotExist"), "city");
2633 assert!((default_sel2 - 0.1).abs() < 0.01);
2634 }
2635
2636 #[test]
2637 fn test_graph_statistics_format() {
2638 let mut store = GraphStore::new();
2639 let a = store.create_node("Person");
2640 let b = store.create_node("Company");
2641 store.create_edge(a, b, "WORKS_AT").unwrap();
2642
2643 let stats = store.compute_statistics();
2644 let formatted = stats.format();
2645 assert!(formatted.contains("Graph Statistics:"));
2646 assert!(formatted.contains("Total nodes: 2"));
2647 assert!(formatted.contains("Total edges: 1"));
2648 assert!(formatted.contains("Avg out-degree:"));
2649 assert!(formatted.contains(":Person"));
2650 assert!(formatted.contains(":Company"));
2651 assert!(formatted.contains(":WORKS_AT"));
2652 }
2653
2654 #[test]
2655 fn test_graph_statistics_property_null_fraction() {
2656 let mut store = GraphStore::new();
2657 for i in 0..4 {
2659 let id = store.create_node("Person");
2660 store.get_node_mut(id).unwrap().set_property(
2661 "name".to_string(),
2662 PropertyValue::String(format!("Person{}", i)),
2663 );
2664 if i < 2 {
2665 store.get_node_mut(id).unwrap().set_property(
2666 "email".to_string(),
2667 PropertyValue::String(format!("person{}@example.com", i)),
2668 );
2669 }
2670 }
2671 let stats = store.compute_statistics();
2672 let name_stats = stats
2674 .property_stats
2675 .get(&(Label::new("Person"), "name".to_string()))
2676 .unwrap();
2677 assert_eq!(name_stats.null_fraction, 0.0);
2678 let email_stats = stats
2680 .property_stats
2681 .get(&(Label::new("Person"), "email".to_string()))
2682 .unwrap();
2683 assert!((email_stats.null_fraction - 0.5).abs() < 0.01);
2684 }
2685
2686 #[test]
2687 fn test_vector_search_with_data() {
2688 let store = GraphStore::new();
2689 store
2691 .create_vector_index(
2692 "Document",
2693 "embedding",
2694 4,
2695 crate::vector::DistanceMetric::Cosine,
2696 )
2697 .unwrap();
2698
2699 let n1 = NodeId::new(1);
2701 let n2 = NodeId::new(2);
2702 let n3 = NodeId::new(3);
2703 let v1 = vec![1.0, 0.0, 0.0, 0.0];
2704 let v2 = vec![0.0, 1.0, 0.0, 0.0];
2705 let v3 = vec![0.9, 0.1, 0.0, 0.0]; store
2708 .vector_index
2709 .add_vector("Document", "embedding", n1, &v1)
2710 .unwrap();
2711 store
2712 .vector_index
2713 .add_vector("Document", "embedding", n2, &v2)
2714 .unwrap();
2715 store
2716 .vector_index
2717 .add_vector("Document", "embedding", n3, &v3)
2718 .unwrap();
2719
2720 let results = store
2722 .vector_search("Document", "embedding", &[1.0, 0.0, 0.0, 0.0], 2)
2723 .unwrap();
2724 assert_eq!(results.len(), 2);
2725 assert_eq!(results[0].0, n1);
2727 }
2728
2729 #[test]
2730 fn test_vector_search_nonexistent_index() {
2731 let store = GraphStore::new();
2732 let results = store
2734 .vector_search("NoLabel", "noprop", &[1.0, 2.0], 5)
2735 .unwrap();
2736 assert!(results.is_empty());
2737 }
2738
2739 #[test]
2740 fn test_clear_thorough() {
2741 let mut store = GraphStore::new();
2742 let a = store.create_node("Person");
2743 let b = store.create_node("Company");
2744 store
2745 .set_node_property(
2746 "default",
2747 a,
2748 "name",
2749 PropertyValue::String("Alice".to_string()),
2750 )
2751 .unwrap();
2752 let eid = store.create_edge(a, b, "WORKS_AT").unwrap();
2753
2754 assert_eq!(store.node_count(), 2);
2756 assert_eq!(store.edge_count(), 1);
2757 assert_eq!(store.all_labels().len(), 2);
2758 assert_eq!(store.all_edge_types().len(), 1);
2759 assert!(store.has_node(a));
2760 assert!(store.has_edge(eid));
2761
2762 store.clear();
2763
2764 assert_eq!(store.node_count(), 0);
2766 assert_eq!(store.edge_count(), 0);
2767 assert!(store.all_labels().is_empty());
2768 assert!(store.all_edge_types().is_empty());
2769 assert!(!store.has_node(a));
2770 assert!(!store.has_edge(eid));
2771 assert!(store.get_nodes_by_label(&Label::new("Person")).is_empty());
2772 assert!(store
2773 .get_edges_by_type(&EdgeType::new("WORKS_AT"))
2774 .is_empty());
2775
2776 let new_node = store.create_node("NewLabel");
2778 assert_eq!(new_node, NodeId::new(1));
2779 }
2780
2781 #[test]
2782 fn test_delete_edge_verifies_edge_type_index_cleanup() {
2783 let mut store = GraphStore::new();
2784 let a = store.create_node("A");
2785 let b = store.create_node("B");
2786 let c = store.create_node("C");
2787
2788 let e1 = store.create_edge(a, b, "KNOWS").unwrap();
2789 let e2 = store.create_edge(a, c, "KNOWS").unwrap();
2790 assert_eq!(store.get_edges_by_type(&EdgeType::new("KNOWS")).len(), 2);
2791
2792 store.delete_edge(e1).unwrap();
2794 assert_eq!(store.get_edges_by_type(&EdgeType::new("KNOWS")).len(), 1);
2795
2796 store.delete_edge(e2).unwrap();
2798 assert_eq!(store.get_edges_by_type(&EdgeType::new("KNOWS")).len(), 0);
2799 }
2800
2801 #[test]
2802 fn test_delete_edge_nonexistent() {
2803 let mut store = GraphStore::new();
2804 let result = store.delete_edge(EdgeId::new(999));
2805 assert_eq!(result, Err(GraphError::EdgeNotFound(EdgeId::new(999))));
2806 }
2807
2808 #[test]
2809 fn test_delete_node_nonexistent() {
2810 let mut store = GraphStore::new();
2811 let result = store.delete_node("default", NodeId::new(999));
2812 assert_eq!(result, Err(GraphError::NodeNotFound(NodeId::new(999))));
2813 }
2814
2815 #[test]
2816 fn test_delete_node_removes_from_label_index() {
2817 let mut store = GraphStore::new();
2818 let a = store.create_node("Person");
2819 let _b = store.create_node("Person");
2820 assert_eq!(store.get_nodes_by_label(&Label::new("Person")).len(), 2);
2821
2822 store.delete_node("default", a).unwrap();
2823 assert_eq!(store.get_nodes_by_label(&Label::new("Person")).len(), 1);
2824 }
2825
2826 #[test]
2827 fn test_delete_node_cascades_edges() {
2828 let mut store = GraphStore::new();
2829 let a = store.create_node("A");
2830 let b = store.create_node("B");
2831 let c = store.create_node("C");
2832 store.create_edge(a, b, "E1").unwrap();
2833 store.create_edge(c, a, "E2").unwrap();
2834
2835 assert_eq!(store.edge_count(), 2);
2836 store.delete_node("default", a).unwrap();
2837 assert_eq!(store.edge_count(), 0);
2838 assert!(store.has_node(b));
2840 assert!(store.has_node(c));
2841 }
2842
2843 #[test]
2844 fn test_edge_id_reuse() {
2845 let mut store = GraphStore::new();
2846 let a = store.create_node("A");
2847 let b = store.create_node("B");
2848 let c = store.create_node("C");
2849
2850 let e1 = store.create_edge(a, b, "X").unwrap();
2851 store.delete_edge(e1).unwrap();
2852
2853 let e2 = store.create_edge(a, c, "Y").unwrap();
2855 assert_eq!(e2, e1);
2856 }
2857
2858 #[test]
2859 fn test_insert_recovered_node() {
2860 let mut store = GraphStore::new();
2861 let node = Node::new(NodeId::new(10), Label::new("Recovered"));
2862
2863 store.insert_recovered_node(node);
2864 assert!(store.has_node(NodeId::new(10)));
2865 assert_eq!(store.get_nodes_by_label(&Label::new("Recovered")).len(), 1);
2866
2867 let new_id = store.create_node("New");
2869 assert!(new_id.as_u64() > 10);
2870 }
2871
2872 #[test]
2873 fn test_insert_recovered_edge() {
2874 let mut store = GraphStore::new();
2875 let a = store.create_node("A");
2876 let b = store.create_node("B");
2877
2878 let edge = Edge::new(EdgeId::new(50), a, b, EdgeType::new("RECOVERED"));
2879 store.insert_recovered_edge(edge).unwrap();
2880
2881 assert!(store.has_edge(EdgeId::new(50)));
2882 assert_eq!(store.get_outgoing_edges(a).len(), 1);
2883 assert_eq!(store.get_incoming_edges(b).len(), 1);
2884 assert_eq!(
2885 store.get_edges_by_type(&EdgeType::new("RECOVERED")).len(),
2886 1
2887 );
2888
2889 let new_eid = store.create_edge(a, b, "NEW").unwrap();
2891 assert!(new_eid.as_u64() > 50);
2892 }
2893
2894 #[test]
2895 fn test_insert_recovered_edge_invalid_source() {
2896 let mut store = GraphStore::new();
2897 let b = store.create_node("B");
2898 let edge = Edge::new(EdgeId::new(1), NodeId::new(999), b, EdgeType::new("E"));
2899 let result = store.insert_recovered_edge(edge);
2900 assert_eq!(result, Err(GraphError::InvalidEdgeSource(NodeId::new(999))));
2901 }
2902
2903 #[test]
2904 fn test_insert_recovered_edge_invalid_target() {
2905 let mut store = GraphStore::new();
2906 let a = store.create_node("A");
2907 let edge = Edge::new(EdgeId::new(1), a, NodeId::new(999), EdgeType::new("E"));
2908 let result = store.insert_recovered_edge(edge);
2909 assert_eq!(result, Err(GraphError::InvalidEdgeTarget(NodeId::new(999))));
2910 }
2911
2912 #[test]
2913 fn test_default_impl() {
2914 let store = GraphStore::default();
2915 assert_eq!(store.node_count(), 0);
2916 assert_eq!(store.edge_count(), 0);
2917 }
2918
2919 #[test]
2920 fn test_mvcc_cow_versioning() {
2921 let mut store = GraphStore::new();
2922 let id = store.create_node("Person");
2923 store
2924 .set_node_property(
2925 "default",
2926 id,
2927 "name",
2928 PropertyValue::String("Alice".to_string()),
2929 )
2930 .unwrap();
2931
2932 store.current_version = 2;
2934 store
2935 .set_node_property(
2936 "default",
2937 id,
2938 "name",
2939 PropertyValue::String("Bob".to_string()),
2940 )
2941 .unwrap();
2942
2943 let latest = store.get_node(id).unwrap();
2945 assert_eq!(
2946 latest.get_property("name"),
2947 Some(&PropertyValue::String("Bob".to_string()))
2948 );
2949 assert_eq!(latest.version, 2);
2950
2951 let v1 = store.get_node_at_version(id, 1).unwrap();
2953 assert_eq!(
2954 v1.get_property("name"),
2955 Some(&PropertyValue::String("Alice".to_string()))
2956 );
2957 assert_eq!(v1.version, 1);
2958 }
2959
2960 #[test]
2961 fn test_get_outgoing_edge_targets_detail() {
2962 let mut store = GraphStore::new();
2963 let a = store.create_node("A");
2964 let b = store.create_node("B");
2965 let c = store.create_node("C");
2966 let e1 = store.create_edge(a, b, "KNOWS").unwrap();
2967 let e2 = store.create_edge(a, c, "LIKES").unwrap();
2968
2969 let targets = store.get_outgoing_edge_targets(a);
2970 assert_eq!(targets.len(), 2);
2971
2972 for (eid, src, tgt, etype) in &targets {
2974 assert_eq!(*src, a);
2975 if *eid == e1 {
2976 assert_eq!(*tgt, b);
2977 assert_eq!(etype.as_str(), "KNOWS");
2978 } else if *eid == e2 {
2979 assert_eq!(*tgt, c);
2980 assert_eq!(etype.as_str(), "LIKES");
2981 } else {
2982 panic!("Unexpected edge ID");
2983 }
2984 }
2985
2986 let empty = store.get_outgoing_edge_targets(NodeId::new(9999));
2988 assert!(empty.is_empty());
2989 }
2990
2991 #[test]
2992 fn test_get_incoming_edge_sources_detail() {
2993 let mut store = GraphStore::new();
2994 let a = store.create_node("A");
2995 let b = store.create_node("B");
2996 let c = store.create_node("C");
2997 let e1 = store.create_edge(a, c, "KNOWS").unwrap();
2998 let e2 = store.create_edge(b, c, "LIKES").unwrap();
2999
3000 let sources = store.get_incoming_edge_sources(c);
3001 assert_eq!(sources.len(), 2);
3002
3003 for (eid, src, tgt, etype) in &sources {
3004 assert_eq!(*tgt, c);
3005 if *eid == e1 {
3006 assert_eq!(*src, a);
3007 assert_eq!(etype.as_str(), "KNOWS");
3008 } else if *eid == e2 {
3009 assert_eq!(*src, b);
3010 assert_eq!(etype.as_str(), "LIKES");
3011 } else {
3012 panic!("Unexpected edge ID");
3013 }
3014 }
3015
3016 let empty = store.get_incoming_edge_sources(NodeId::new(9999));
3018 assert!(empty.is_empty());
3019 }
3020
3021 #[test]
3022 fn test_all_labels_empty() {
3023 let store = GraphStore::new();
3024 assert!(store.all_labels().is_empty());
3025 }
3026
3027 #[test]
3028 fn test_all_edge_types_empty() {
3029 let store = GraphStore::new();
3030 assert!(store.all_edge_types().is_empty());
3031 }
3032
3033 #[test]
3034 fn test_columnar_storage_integration() {
3035 let mut store = GraphStore::new();
3036 let id = store
3037 .create_node_with_properties("default", vec![Label::new("Person")], {
3038 let mut props = PropertyMap::new();
3039 props.insert(
3040 "name".to_string(),
3041 PropertyValue::String("Alice".to_string()),
3042 );
3043 props.insert("age".to_string(), PropertyValue::Integer(30));
3044 props
3045 })
3046 .unwrap();
3047
3048 let idx = id.as_u64() as usize;
3050 let name_col = store.node_columns.get_property(idx, "name");
3051 assert_eq!(name_col, PropertyValue::String("Alice".to_string()));
3052 let age_col = store.node_columns.get_property(idx, "age");
3053 assert_eq!(age_col, PropertyValue::Integer(30));
3054 }
3055
3056 #[test]
3057 fn test_edge_columnar_storage_integration() {
3058 let mut store = GraphStore::new();
3059 let a = store.create_node("A");
3060 let b = store.create_node("B");
3061 let mut props = std::collections::HashMap::new();
3062 props.insert("weight".to_string(), PropertyValue::Float(0.75));
3063 let eid = store
3064 .create_edge_with_properties(a, b, "WEIGHTED", props)
3065 .unwrap();
3066
3067 let idx = eid.as_u64() as usize;
3068 let weight_col = store.edge_columns.get_property(idx, "weight");
3069 assert_eq!(weight_col, PropertyValue::Float(0.75));
3070 }
3071
3072 #[test]
3073 fn test_set_node_property_updates_columnar_storage() {
3074 let mut store = GraphStore::new();
3075 let id = store.create_node("Person");
3076 store
3077 .set_node_property("default", id, "score", PropertyValue::Float(1.0))
3078 .unwrap();
3079
3080 let idx = id.as_u64() as usize;
3081 assert_eq!(
3082 store.node_columns.get_property(idx, "score"),
3083 PropertyValue::Float(1.0)
3084 );
3085
3086 store
3088 .set_node_property("default", id, "score", PropertyValue::Float(2.5))
3089 .unwrap();
3090 assert_eq!(
3091 store.node_columns.get_property(idx, "score"),
3092 PropertyValue::Float(2.5)
3093 );
3094 }
3095
3096 #[test]
3097 fn test_get_nodes_by_label_after_deletions() {
3098 let mut store = GraphStore::new();
3099 let a = store.create_node("Person");
3100 let b_id = store.create_node("Person");
3101 let c = store.create_node("Person");
3102
3103 store.delete_node("default", b_id).unwrap();
3104 let persons = store.get_nodes_by_label(&Label::new("Person"));
3105 assert_eq!(persons.len(), 2);
3106 let ids: Vec<NodeId> = persons.iter().map(|n| n.id).collect();
3107 assert!(ids.contains(&a));
3108 assert!(ids.contains(&c));
3109 assert!(!ids.contains(&b_id));
3110 }
3111
3112 #[test]
3113 fn test_get_edges_by_type_after_deletion() {
3114 let mut store = GraphStore::new();
3115 let a = store.create_node("A");
3116 let b = store.create_node("B");
3117 let c = store.create_node("C");
3118 let e1 = store.create_edge(a, b, "KNOWS").unwrap();
3119 let e2 = store.create_edge(b, c, "KNOWS").unwrap();
3120
3121 store.delete_edge(e1).unwrap();
3122 let knows_edges = store.get_edges_by_type(&EdgeType::new("KNOWS"));
3123 assert_eq!(knows_edges.len(), 1);
3124 assert_eq!(knows_edges[0].id, e2);
3125 }
3126
3127 #[test]
3128 fn test_all_nodes_after_operations() {
3129 let mut store = GraphStore::new();
3130 let a = store.create_node("A");
3131 store.create_node("B");
3132 store.create_node("C");
3133 store.delete_node("default", a).unwrap();
3134
3135 let all = store.all_nodes();
3136 assert_eq!(all.len(), 2);
3137 }
3138
3139 #[test]
3140 fn test_compute_statistics_with_large_sample() {
3141 let mut store = GraphStore::new();
3142 for i in 0..1100 {
3144 let id = store.create_node("BigLabel");
3145 store
3146 .get_node_mut(id)
3147 .unwrap()
3148 .set_property("idx".to_string(), PropertyValue::Integer(i));
3149 }
3150 let stats = store.compute_statistics();
3151 assert_eq!(stats.total_nodes, 1100);
3152 let idx_stats = stats
3154 .property_stats
3155 .get(&(Label::new("BigLabel"), "idx".to_string()));
3156 assert!(idx_stats.is_some());
3157 let ps = idx_stats.unwrap();
3158 assert_eq!(ps.null_fraction, 0.0);
3160 assert_eq!(ps.distinct_count, 1000);
3162 }
3163
3164 #[test]
3165 fn test_add_label_then_label_count() {
3166 let mut store = GraphStore::new();
3167 let id = store.create_node("Person");
3168 store.add_label_to_node("default", id, "Employee").unwrap();
3169
3170 assert_eq!(store.label_node_count(&Label::new("Person")), 1);
3171 assert_eq!(store.label_node_count(&Label::new("Employee")), 1);
3172 }
3173
3174 #[test]
3175 fn test_insert_recovered_node_with_multiple_labels() {
3176 let mut store = GraphStore::new();
3177 let mut node = Node::new(NodeId::new(5), Label::new("Person"));
3178 node.add_label(Label::new("Employee"));
3179
3180 store.insert_recovered_node(node);
3181 assert_eq!(store.get_nodes_by_label(&Label::new("Person")).len(), 1);
3182 assert_eq!(store.get_nodes_by_label(&Label::new("Employee")).len(), 1);
3183 }
3184
3185 #[test]
3186 fn test_graph_statistics_avg_out_degree() {
3187 let mut store = GraphStore::new();
3188 let a = store.create_node("A");
3189 let b = store.create_node("B");
3190 let c = store.create_node("C");
3191 let d = store.create_node("D");
3192 store.create_edge(a, b, "E").unwrap();
3194 store.create_edge(a, c, "E").unwrap();
3195 store.create_edge(a, d, "E").unwrap();
3196
3197 let stats = store.compute_statistics();
3198 assert!((stats.avg_out_degree - 0.75).abs() < 0.01); }
3200
3201 #[test]
3202 fn test_self_loop_edge() {
3203 let mut store = GraphStore::new();
3204 let a = store.create_node("A");
3205 let eid = store.create_edge(a, a, "SELF").unwrap();
3206
3207 assert_eq!(store.get_outgoing_edges(a).len(), 1);
3208 assert_eq!(store.get_incoming_edges(a).len(), 1);
3209
3210 store.delete_edge(eid).unwrap();
3211 assert_eq!(store.get_outgoing_edges(a).len(), 0);
3212 assert_eq!(store.get_incoming_edges(a).len(), 0);
3213 }
3214
3215 #[test]
3216 fn test_get_outgoing_edges_nonexistent_node() {
3217 let store = GraphStore::new();
3218 let edges = store.get_outgoing_edges(NodeId::new(999));
3219 assert!(edges.is_empty());
3220 }
3221
3222 #[test]
3223 fn test_get_incoming_edges_nonexistent_node() {
3224 let store = GraphStore::new();
3225 let edges = store.get_incoming_edges(NodeId::new(999));
3226 assert!(edges.is_empty());
3227 }
3228
3229 #[test]
3230 fn test_get_nodes_by_label_nonexistent() {
3231 let store = GraphStore::new();
3232 let nodes = store.get_nodes_by_label(&Label::new("NoSuch"));
3233 assert!(nodes.is_empty());
3234 }
3235
3236 #[test]
3237 fn test_get_edges_by_type_nonexistent() {
3238 let store = GraphStore::new();
3239 let edges = store.get_edges_by_type(&EdgeType::new("NoSuch"));
3240 assert!(edges.is_empty());
3241 }
3242
3243 #[test]
3246 fn test_edge_between_exists() {
3247 let mut store = GraphStore::new();
3248 let n1 = store.create_node("Person");
3249 let n2 = store.create_node("Person");
3250 let eid = store.create_edge(n1, n2, "KNOWS").unwrap();
3251
3252 assert_eq!(
3253 store.edge_between(n1, n2, Some(&EdgeType::new("KNOWS"))),
3254 Some(eid)
3255 );
3256 }
3257
3258 #[test]
3259 fn test_edge_between_not_exists() {
3260 let mut store = GraphStore::new();
3261 let n1 = store.create_node("Person");
3262 let n2 = store.create_node("Person");
3263
3264 assert_eq!(
3265 store.edge_between(n1, n2, Some(&EdgeType::new("KNOWS"))),
3266 None
3267 );
3268 }
3269
3270 #[test]
3271 fn test_edge_between_wrong_type() {
3272 let mut store = GraphStore::new();
3273 let n1 = store.create_node("Person");
3274 let n2 = store.create_node("Person");
3275 store.create_edge(n1, n2, "KNOWS").unwrap();
3276
3277 assert_eq!(
3278 store.edge_between(n1, n2, Some(&EdgeType::new("FOLLOWS"))),
3279 None
3280 );
3281 }
3282
3283 #[test]
3284 fn test_edge_between_any_type() {
3285 let mut store = GraphStore::new();
3286 let n1 = store.create_node("Person");
3287 let n2 = store.create_node("Person");
3288 let eid = store.create_edge(n1, n2, "KNOWS").unwrap();
3289
3290 assert_eq!(store.edge_between(n1, n2, None), Some(eid));
3292 }
3293
3294 #[test]
3295 fn test_edge_between_reverse_direction() {
3296 let mut store = GraphStore::new();
3297 let n1 = store.create_node("Person");
3298 let n2 = store.create_node("Person");
3299 store.create_edge(n1, n2, "KNOWS").unwrap();
3300
3301 assert_eq!(
3303 store.edge_between(n2, n1, Some(&EdgeType::new("KNOWS"))),
3304 None
3305 );
3306 }
3307
3308 #[test]
3309 fn test_edges_between_multi() {
3310 let mut store = GraphStore::new();
3311 let n1 = store.create_node("Person");
3312 let n2 = store.create_node("Person");
3313 let eid1 = store.create_edge(n1, n2, "KNOWS").unwrap();
3314 let eid2 = store.create_edge(n1, n2, "FOLLOWS").unwrap();
3315 let eid3 = store.create_edge(n1, n2, "KNOWS").unwrap();
3316
3317 let all = store.edges_between(n1, n2, None);
3319 assert_eq!(all.len(), 3);
3320
3321 let knows = store.edges_between(n1, n2, Some(&EdgeType::new("KNOWS")));
3323 assert_eq!(knows.len(), 2);
3324 assert!(knows.contains(&eid1));
3325 assert!(knows.contains(&eid3));
3326
3327 let follows = store.edges_between(n1, n2, Some(&EdgeType::new("FOLLOWS")));
3329 assert_eq!(follows.len(), 1);
3330 assert!(follows.contains(&eid2));
3331 }
3332
3333 #[test]
3336 fn test_sorted_adjacency_insert_order() {
3337 let mut store = GraphStore::new();
3338 let n1 = store.create_node("Person");
3339 let n3 = store.create_node("Person");
3340 let n2 = store.create_node("Person");
3341
3342 store.create_edge(n1, n3, "KNOWS").unwrap();
3344 store.create_edge(n1, n2, "KNOWS").unwrap();
3345
3346 let out = &store.outgoing[n1.as_u64() as usize];
3348 assert_eq!(out.len(), 2);
3349 assert!(
3350 out[0].0 <= out[1].0,
3351 "outgoing adjacency should be sorted by target NodeId"
3352 );
3353 }
3354
3355 #[test]
3356 fn test_sorted_adjacency_delete() {
3357 let mut store = GraphStore::new();
3358 let n1 = store.create_node("Person");
3359 let n2 = store.create_node("Person");
3360 let n3 = store.create_node("Person");
3361
3362 let e1 = store.create_edge(n1, n2, "KNOWS").unwrap();
3363 let _e2 = store.create_edge(n1, n3, "KNOWS").unwrap();
3364
3365 store.delete_edge(e1).unwrap();
3366
3367 let out = &store.outgoing[n1.as_u64() as usize];
3368 assert_eq!(out.len(), 1);
3369 assert_eq!(out[0].0, n3); }
3371
3372 #[test]
3373 fn test_sorted_adjacency_multiple_edges_same_target() {
3374 let mut store = GraphStore::new();
3375 let n1 = store.create_node("Person");
3376 let n2 = store.create_node("Person");
3377
3378 let _e1 = store.create_edge(n1, n2, "KNOWS").unwrap();
3379 let _e2 = store.create_edge(n1, n2, "FOLLOWS").unwrap();
3380
3381 let out = &store.outgoing[n1.as_u64() as usize];
3383 assert_eq!(out.len(), 2);
3384 assert_eq!(out[0].0, n2);
3385 assert_eq!(out[1].0, n2);
3386
3387 assert!(store
3389 .edge_between(n1, n2, Some(&EdgeType::new("KNOWS")))
3390 .is_some());
3391 assert!(store
3392 .edge_between(n1, n2, Some(&EdgeType::new("FOLLOWS")))
3393 .is_some());
3394 }
3395
3396 #[test]
3397 fn test_edge_between_binary_search_high_degree() {
3398 let mut store = GraphStore::new();
3399 let hub = store.create_node("Hub");
3400
3401 let mut targets = Vec::new();
3403 for _ in 0..100 {
3404 let t = store.create_node("Target");
3405 store.create_edge(hub, t, "LINKS").unwrap();
3406 targets.push(t);
3407 }
3408
3409 for &t in &targets {
3411 assert!(
3412 store
3413 .edge_between(hub, t, Some(&EdgeType::new("LINKS")))
3414 .is_some(),
3415 "edge_between should find edge to target {:?}",
3416 t
3417 );
3418 }
3419
3420 let fake = NodeId::new(9999);
3422 assert!(store
3423 .edge_between(hub, fake, Some(&EdgeType::new("LINKS")))
3424 .is_none());
3425 }
3426}