Files
wifi-densepose/vendor/ruvector/crates/ruvector-graph/src/index.rs

473 lines
13 KiB
Rust

//! Index structures for fast node and edge lookups
//!
//! Provides label indexes, property indexes, and edge type indexes for efficient querying
use crate::edge::Edge;
use crate::hyperedge::{Hyperedge, HyperedgeId};
use crate::node::Node;
use crate::types::{EdgeId, NodeId, PropertyValue};
use dashmap::DashMap;
use std::collections::HashSet;
use std::sync::Arc;
/// Label index for nodes (maps labels to node IDs)
#[derive(Debug, Clone)]
pub struct LabelIndex {
/// Label -> Set of node IDs
index: Arc<DashMap<String, HashSet<NodeId>>>,
}
impl LabelIndex {
/// Create a new label index
pub fn new() -> Self {
Self {
index: Arc::new(DashMap::new()),
}
}
/// Add a node to the index
pub fn add_node(&self, node: &Node) {
for label in &node.labels {
self.index
.entry(label.name.clone())
.or_insert_with(HashSet::new)
.insert(node.id.clone());
}
}
/// Remove a node from the index
pub fn remove_node(&self, node: &Node) {
for label in &node.labels {
if let Some(mut set) = self.index.get_mut(&label.name) {
set.remove(&node.id);
}
}
}
/// Get all nodes with a specific label
pub fn get_nodes_by_label(&self, label: &str) -> Vec<NodeId> {
self.index
.get(label)
.map(|set| set.iter().cloned().collect())
.unwrap_or_default()
}
/// Get all labels in the index
pub fn all_labels(&self) -> Vec<String> {
self.index.iter().map(|entry| entry.key().clone()).collect()
}
/// Count nodes with a specific label
pub fn count_by_label(&self, label: &str) -> usize {
self.index.get(label).map(|set| set.len()).unwrap_or(0)
}
/// Clear the index
pub fn clear(&self) {
self.index.clear();
}
}
impl Default for LabelIndex {
fn default() -> Self {
Self::new()
}
}
/// Property index for nodes (maps property keys to values to node IDs)
#[derive(Debug, Clone)]
pub struct PropertyIndex {
/// Property key -> Property value -> Set of node IDs
index: Arc<DashMap<String, DashMap<String, HashSet<NodeId>>>>,
}
impl PropertyIndex {
/// Create a new property index
pub fn new() -> Self {
Self {
index: Arc::new(DashMap::new()),
}
}
/// Add a node to the index
pub fn add_node(&self, node: &Node) {
for (key, value) in &node.properties {
let value_str = self.property_value_to_string(value);
self.index
.entry(key.clone())
.or_insert_with(DashMap::new)
.entry(value_str)
.or_insert_with(HashSet::new)
.insert(node.id.clone());
}
}
/// Remove a node from the index
pub fn remove_node(&self, node: &Node) {
for (key, value) in &node.properties {
let value_str = self.property_value_to_string(value);
if let Some(value_map) = self.index.get(key) {
if let Some(mut set) = value_map.get_mut(&value_str) {
set.remove(&node.id);
}
}
}
}
/// Get nodes by property key-value pair
pub fn get_nodes_by_property(&self, key: &str, value: &PropertyValue) -> Vec<NodeId> {
let value_str = self.property_value_to_string(value);
self.index
.get(key)
.and_then(|value_map| {
value_map
.get(&value_str)
.map(|set| set.iter().cloned().collect())
})
.unwrap_or_default()
}
/// Get all nodes that have a specific property key (regardless of value)
pub fn get_nodes_with_property(&self, key: &str) -> Vec<NodeId> {
self.index
.get(key)
.map(|value_map| {
let mut result = HashSet::new();
for entry in value_map.iter() {
for id in entry.value().iter() {
result.insert(id.clone());
}
}
result.into_iter().collect()
})
.unwrap_or_default()
}
/// Get all property keys in the index
pub fn all_property_keys(&self) -> Vec<String> {
self.index.iter().map(|entry| entry.key().clone()).collect()
}
/// Clear the index
pub fn clear(&self) {
self.index.clear();
}
/// Convert property value to string for indexing
fn property_value_to_string(&self, value: &PropertyValue) -> String {
match value {
PropertyValue::Null => "null".to_string(),
PropertyValue::Boolean(b) => b.to_string(),
PropertyValue::Integer(i) => i.to_string(),
PropertyValue::Float(f) => f.to_string(),
PropertyValue::String(s) => s.clone(),
PropertyValue::Array(_) | PropertyValue::List(_) => format!("{:?}", value),
PropertyValue::Map(_) => format!("{:?}", value),
}
}
}
impl Default for PropertyIndex {
fn default() -> Self {
Self::new()
}
}
/// Edge type index (maps edge types to edge IDs)
#[derive(Debug, Clone)]
pub struct EdgeTypeIndex {
/// Edge type -> Set of edge IDs
index: Arc<DashMap<String, HashSet<EdgeId>>>,
}
impl EdgeTypeIndex {
/// Create a new edge type index
pub fn new() -> Self {
Self {
index: Arc::new(DashMap::new()),
}
}
/// Add an edge to the index
pub fn add_edge(&self, edge: &Edge) {
self.index
.entry(edge.edge_type.clone())
.or_insert_with(HashSet::new)
.insert(edge.id.clone());
}
/// Remove an edge from the index
pub fn remove_edge(&self, edge: &Edge) {
if let Some(mut set) = self.index.get_mut(&edge.edge_type) {
set.remove(&edge.id);
}
}
/// Get all edges of a specific type
pub fn get_edges_by_type(&self, edge_type: &str) -> Vec<EdgeId> {
self.index
.get(edge_type)
.map(|set| set.iter().cloned().collect())
.unwrap_or_default()
}
/// Get all edge types
pub fn all_edge_types(&self) -> Vec<String> {
self.index.iter().map(|entry| entry.key().clone()).collect()
}
/// Count edges of a specific type
pub fn count_by_type(&self, edge_type: &str) -> usize {
self.index.get(edge_type).map(|set| set.len()).unwrap_or(0)
}
/// Clear the index
pub fn clear(&self) {
self.index.clear();
}
}
impl Default for EdgeTypeIndex {
fn default() -> Self {
Self::new()
}
}
/// Adjacency index for fast neighbor lookups
#[derive(Debug, Clone)]
pub struct AdjacencyIndex {
/// Node ID -> Set of outgoing edge IDs
outgoing: Arc<DashMap<NodeId, HashSet<EdgeId>>>,
/// Node ID -> Set of incoming edge IDs
incoming: Arc<DashMap<NodeId, HashSet<EdgeId>>>,
}
impl AdjacencyIndex {
/// Create a new adjacency index
pub fn new() -> Self {
Self {
outgoing: Arc::new(DashMap::new()),
incoming: Arc::new(DashMap::new()),
}
}
/// Add an edge to the index
pub fn add_edge(&self, edge: &Edge) {
self.outgoing
.entry(edge.from.clone())
.or_insert_with(HashSet::new)
.insert(edge.id.clone());
self.incoming
.entry(edge.to.clone())
.or_insert_with(HashSet::new)
.insert(edge.id.clone());
}
/// Remove an edge from the index
pub fn remove_edge(&self, edge: &Edge) {
if let Some(mut set) = self.outgoing.get_mut(&edge.from) {
set.remove(&edge.id);
}
if let Some(mut set) = self.incoming.get_mut(&edge.to) {
set.remove(&edge.id);
}
}
/// Get all outgoing edges from a node
pub fn get_outgoing_edges(&self, node_id: &NodeId) -> Vec<EdgeId> {
self.outgoing
.get(node_id)
.map(|set| set.iter().cloned().collect())
.unwrap_or_default()
}
/// Get all incoming edges to a node
pub fn get_incoming_edges(&self, node_id: &NodeId) -> Vec<EdgeId> {
self.incoming
.get(node_id)
.map(|set| set.iter().cloned().collect())
.unwrap_or_default()
}
/// Get all edges connected to a node (both incoming and outgoing)
pub fn get_all_edges(&self, node_id: &NodeId) -> Vec<EdgeId> {
let mut edges = self.get_outgoing_edges(node_id);
edges.extend(self.get_incoming_edges(node_id));
edges
}
/// Get degree (number of outgoing edges)
pub fn out_degree(&self, node_id: &NodeId) -> usize {
self.outgoing.get(node_id).map(|set| set.len()).unwrap_or(0)
}
/// Get in-degree (number of incoming edges)
pub fn in_degree(&self, node_id: &NodeId) -> usize {
self.incoming.get(node_id).map(|set| set.len()).unwrap_or(0)
}
/// Clear the index
pub fn clear(&self) {
self.outgoing.clear();
self.incoming.clear();
}
}
impl Default for AdjacencyIndex {
fn default() -> Self {
Self::new()
}
}
/// Hyperedge node index (maps nodes to hyperedges they participate in)
#[derive(Debug, Clone)]
pub struct HyperedgeNodeIndex {
/// Node ID -> Set of hyperedge IDs
index: Arc<DashMap<NodeId, HashSet<HyperedgeId>>>,
}
impl HyperedgeNodeIndex {
/// Create a new hyperedge node index
pub fn new() -> Self {
Self {
index: Arc::new(DashMap::new()),
}
}
/// Add a hyperedge to the index
pub fn add_hyperedge(&self, hyperedge: &Hyperedge) {
for node_id in &hyperedge.nodes {
self.index
.entry(node_id.clone())
.or_insert_with(HashSet::new)
.insert(hyperedge.id.clone());
}
}
/// Remove a hyperedge from the index
pub fn remove_hyperedge(&self, hyperedge: &Hyperedge) {
for node_id in &hyperedge.nodes {
if let Some(mut set) = self.index.get_mut(node_id) {
set.remove(&hyperedge.id);
}
}
}
/// Get all hyperedges containing a node
pub fn get_hyperedges_by_node(&self, node_id: &NodeId) -> Vec<HyperedgeId> {
self.index
.get(node_id)
.map(|set| set.iter().cloned().collect())
.unwrap_or_default()
}
/// Clear the index
pub fn clear(&self) {
self.index.clear();
}
}
impl Default for HyperedgeNodeIndex {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::node::NodeBuilder;
#[test]
fn test_label_index() {
let index = LabelIndex::new();
let node1 = NodeBuilder::new().label("Person").label("User").build();
let node2 = NodeBuilder::new().label("Person").build();
index.add_node(&node1);
index.add_node(&node2);
let people = index.get_nodes_by_label("Person");
assert_eq!(people.len(), 2);
let users = index.get_nodes_by_label("User");
assert_eq!(users.len(), 1);
assert_eq!(index.count_by_label("Person"), 2);
}
#[test]
fn test_property_index() {
let index = PropertyIndex::new();
let node1 = NodeBuilder::new()
.property("name", "Alice")
.property("age", 30i64)
.build();
let node2 = NodeBuilder::new()
.property("name", "Bob")
.property("age", 30i64)
.build();
index.add_node(&node1);
index.add_node(&node2);
let alice =
index.get_nodes_by_property("name", &PropertyValue::String("Alice".to_string()));
assert_eq!(alice.len(), 1);
let age_30 = index.get_nodes_by_property("age", &PropertyValue::Integer(30));
assert_eq!(age_30.len(), 2);
let with_age = index.get_nodes_with_property("age");
assert_eq!(with_age.len(), 2);
}
#[test]
fn test_edge_type_index() {
let index = EdgeTypeIndex::new();
let edge1 = Edge::create("n1".to_string(), "n2".to_string(), "KNOWS");
let edge2 = Edge::create("n2".to_string(), "n3".to_string(), "KNOWS");
let edge3 = Edge::create("n1".to_string(), "n3".to_string(), "WORKS_WITH");
index.add_edge(&edge1);
index.add_edge(&edge2);
index.add_edge(&edge3);
let knows_edges = index.get_edges_by_type("KNOWS");
assert_eq!(knows_edges.len(), 2);
let works_with_edges = index.get_edges_by_type("WORKS_WITH");
assert_eq!(works_with_edges.len(), 1);
assert_eq!(index.all_edge_types().len(), 2);
}
#[test]
fn test_adjacency_index() {
let index = AdjacencyIndex::new();
let edge1 = Edge::create("n1".to_string(), "n2".to_string(), "KNOWS");
let edge2 = Edge::create("n1".to_string(), "n3".to_string(), "KNOWS");
let edge3 = Edge::create("n2".to_string(), "n1".to_string(), "KNOWS");
index.add_edge(&edge1);
index.add_edge(&edge2);
index.add_edge(&edge3);
assert_eq!(index.out_degree(&"n1".to_string()), 2);
assert_eq!(index.in_degree(&"n1".to_string()), 1);
let outgoing = index.get_outgoing_edges(&"n1".to_string());
assert_eq!(outgoing.len(), 2);
let incoming = index.get_incoming_edges(&"n1".to_string());
assert_eq!(incoming.len(), 1);
}
}