Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'

This commit is contained in:
ruv
2026-02-28 14:39:40 -05:00
7854 changed files with 3522914 additions and 0 deletions

View File

@@ -0,0 +1,562 @@
//! # RuVector Delta Graph
//!
//! Delta operations for graph structures, supporting incremental updates
//! to edges and node properties.
//!
//! ## Key Features
//!
//! - Edge delta operations (add, remove, update weight)
//! - Node property deltas (including vector properties)
//! - Delta-aware graph traversal
//! - Efficient batch updates
#![warn(missing_docs)]
#![warn(clippy::all)]
pub mod edge_delta;
pub mod error;
pub mod node_delta;
pub mod traversal;
use std::collections::{HashMap, HashSet};
use ruvector_delta_core::{Delta, DeltaStream, VectorDelta};
use smallvec::SmallVec;
pub use edge_delta::{EdgeDelta, EdgeOp};
pub use error::{GraphDeltaError, Result};
pub use node_delta::{NodeDelta, PropertyDelta};
pub use traversal::{DeltaAwareTraversal, TraversalMode};
/// A node ID type
pub type NodeId = String;
/// An edge ID type
pub type EdgeId = String;
/// Property value types
#[derive(Debug, Clone, PartialEq)]
pub enum PropertyValue {
/// Null/missing value
Null,
/// Boolean
Bool(bool),
/// Integer
Int(i64),
/// Float
Float(f64),
/// String
String(String),
/// Vector (for embeddings)
Vector(Vec<f32>),
/// List of values
List(Vec<PropertyValue>),
/// Map of values
Map(HashMap<String, PropertyValue>),
}
impl Default for PropertyValue {
fn default() -> Self {
Self::Null
}
}
/// A graph delta representing changes to graph structure
#[derive(Debug, Clone)]
pub struct GraphDelta {
/// Node additions
pub node_adds: Vec<(NodeId, HashMap<String, PropertyValue>)>,
/// Node removals
pub node_removes: Vec<NodeId>,
/// Node property updates
pub node_updates: Vec<(NodeId, NodeDelta)>,
/// Edge additions
pub edge_adds: Vec<EdgeAddition>,
/// Edge removals
pub edge_removes: Vec<EdgeId>,
/// Edge updates
pub edge_updates: Vec<(EdgeId, EdgeDelta)>,
}
/// An edge addition
#[derive(Debug, Clone)]
pub struct EdgeAddition {
/// Edge ID
pub id: EdgeId,
/// Source node
pub source: NodeId,
/// Target node
pub target: NodeId,
/// Edge type/label
pub edge_type: String,
/// Edge properties
pub properties: HashMap<String, PropertyValue>,
}
impl GraphDelta {
/// Create an empty graph delta
pub fn new() -> Self {
Self {
node_adds: Vec::new(),
node_removes: Vec::new(),
node_updates: Vec::new(),
edge_adds: Vec::new(),
edge_removes: Vec::new(),
edge_updates: Vec::new(),
}
}
/// Check if delta is empty
pub fn is_empty(&self) -> bool {
self.node_adds.is_empty()
&& self.node_removes.is_empty()
&& self.node_updates.is_empty()
&& self.edge_adds.is_empty()
&& self.edge_removes.is_empty()
&& self.edge_updates.is_empty()
}
/// Get total number of operations
pub fn operation_count(&self) -> usize {
self.node_adds.len()
+ self.node_removes.len()
+ self.node_updates.len()
+ self.edge_adds.len()
+ self.edge_removes.len()
+ self.edge_updates.len()
}
/// Add a node addition
pub fn add_node(&mut self, id: NodeId, properties: HashMap<String, PropertyValue>) {
self.node_adds.push((id, properties));
}
/// Add a node removal
pub fn remove_node(&mut self, id: NodeId) {
self.node_removes.push(id);
}
/// Add a node update
pub fn update_node(&mut self, id: NodeId, delta: NodeDelta) {
self.node_updates.push((id, delta));
}
/// Add an edge
pub fn add_edge(&mut self, edge: EdgeAddition) {
self.edge_adds.push(edge);
}
/// Remove an edge
pub fn remove_edge(&mut self, id: EdgeId) {
self.edge_removes.push(id);
}
/// Update an edge
pub fn update_edge(&mut self, id: EdgeId, delta: EdgeDelta) {
self.edge_updates.push((id, delta));
}
/// Compose with another graph delta
pub fn compose(mut self, other: GraphDelta) -> Self {
// Handle conflicting operations
let removed_nodes: HashSet<_> = other.node_removes.iter().collect();
let removed_edges: HashSet<_> = other.edge_removes.iter().collect();
// Filter out adds that are removed
self.node_adds.retain(|(id, _)| !removed_nodes.contains(id));
self.edge_adds.retain(|e| !removed_edges.contains(&e.id));
// Merge adds
self.node_adds.extend(other.node_adds);
self.node_removes.extend(other.node_removes);
self.edge_adds.extend(other.edge_adds);
self.edge_removes.extend(other.edge_removes);
// Compose node updates
let mut node_update_map: HashMap<NodeId, NodeDelta> = HashMap::new();
for (id, delta) in self.node_updates {
node_update_map.insert(id, delta);
}
for (id, delta) in other.node_updates {
node_update_map
.entry(id)
.and_modify(|existing| *existing = existing.clone().compose(delta.clone()))
.or_insert(delta);
}
self.node_updates = node_update_map.into_iter().collect();
// Compose edge updates
let mut edge_update_map: HashMap<EdgeId, EdgeDelta> = HashMap::new();
for (id, delta) in self.edge_updates {
edge_update_map.insert(id, delta);
}
for (id, delta) in other.edge_updates {
edge_update_map
.entry(id)
.and_modify(|existing| *existing = existing.clone().compose(delta.clone()))
.or_insert(delta);
}
self.edge_updates = edge_update_map.into_iter().collect();
self
}
/// Compute inverse delta
pub fn inverse(&self) -> Self {
// Note: Full inverse requires knowing original state
// This is a partial inverse that works for simple cases
Self {
node_adds: Vec::new(), // Would need originals to undo removes
node_removes: self.node_adds.iter().map(|(id, _)| id.clone()).collect(),
node_updates: self
.node_updates
.iter()
.map(|(id, d)| (id.clone(), d.inverse()))
.collect(),
edge_adds: Vec::new(),
edge_removes: self.edge_adds.iter().map(|e| e.id.clone()).collect(),
edge_updates: self
.edge_updates
.iter()
.map(|(id, d)| (id.clone(), d.inverse()))
.collect(),
}
}
/// Get affected node IDs
pub fn affected_nodes(&self) -> HashSet<NodeId> {
let mut nodes = HashSet::new();
for (id, _) in &self.node_adds {
nodes.insert(id.clone());
}
for id in &self.node_removes {
nodes.insert(id.clone());
}
for (id, _) in &self.node_updates {
nodes.insert(id.clone());
}
for edge in &self.edge_adds {
nodes.insert(edge.source.clone());
nodes.insert(edge.target.clone());
}
nodes
}
/// Get affected edge IDs
pub fn affected_edges(&self) -> HashSet<EdgeId> {
let mut edges = HashSet::new();
for edge in &self.edge_adds {
edges.insert(edge.id.clone());
}
for id in &self.edge_removes {
edges.insert(id.clone());
}
for (id, _) in &self.edge_updates {
edges.insert(id.clone());
}
edges
}
}
impl Default for GraphDelta {
fn default() -> Self {
Self::new()
}
}
/// Builder for graph deltas
pub struct GraphDeltaBuilder {
delta: GraphDelta,
}
impl GraphDeltaBuilder {
/// Create a new builder
pub fn new() -> Self {
Self {
delta: GraphDelta::new(),
}
}
/// Add a node
pub fn add_node(mut self, id: impl Into<NodeId>) -> Self {
self.delta.add_node(id.into(), HashMap::new());
self
}
/// Add a node with properties
pub fn add_node_with_props(
mut self,
id: impl Into<NodeId>,
props: HashMap<String, PropertyValue>,
) -> Self {
self.delta.add_node(id.into(), props);
self
}
/// Remove a node
pub fn remove_node(mut self, id: impl Into<NodeId>) -> Self {
self.delta.remove_node(id.into());
self
}
/// Add an edge
pub fn add_edge(
mut self,
id: impl Into<EdgeId>,
source: impl Into<NodeId>,
target: impl Into<NodeId>,
edge_type: impl Into<String>,
) -> Self {
self.delta.add_edge(EdgeAddition {
id: id.into(),
source: source.into(),
target: target.into(),
edge_type: edge_type.into(),
properties: HashMap::new(),
});
self
}
/// Remove an edge
pub fn remove_edge(mut self, id: impl Into<EdgeId>) -> Self {
self.delta.remove_edge(id.into());
self
}
/// Build the delta
pub fn build(self) -> GraphDelta {
self.delta
}
}
impl Default for GraphDeltaBuilder {
fn default() -> Self {
Self::new()
}
}
/// Stream of graph deltas for event sourcing
pub struct GraphDeltaStream {
stream: DeltaStream<WrappedGraphDelta>,
}
// Wrapper to implement Delta trait
#[derive(Clone)]
struct WrappedGraphDelta(GraphDelta);
impl Delta for WrappedGraphDelta {
type Base = GraphState;
type Error = GraphDeltaError;
fn compute(_old: &Self::Base, _new: &Self::Base) -> Self {
// Would diff two graph states
WrappedGraphDelta(GraphDelta::new())
}
fn apply(&self, base: &mut Self::Base) -> std::result::Result<(), Self::Error> {
base.apply_delta(&self.0)
}
fn compose(self, other: Self) -> Self {
WrappedGraphDelta(self.0.compose(other.0))
}
fn inverse(&self) -> Self {
WrappedGraphDelta(self.0.inverse())
}
fn is_identity(&self) -> bool {
self.0.is_empty()
}
fn byte_size(&self) -> usize {
// Approximate size
std::mem::size_of::<GraphDelta>() + self.0.operation_count() * 100
}
}
impl Default for WrappedGraphDelta {
fn default() -> Self {
Self(GraphDelta::new())
}
}
/// Simplified graph state for delta application
#[derive(Debug, Clone, Default)]
pub struct GraphState {
/// Nodes with properties
pub nodes: HashMap<NodeId, HashMap<String, PropertyValue>>,
/// Edges
pub edges: HashMap<EdgeId, (NodeId, NodeId, String, HashMap<String, PropertyValue>)>,
}
impl GraphState {
/// Create empty state
pub fn new() -> Self {
Self::default()
}
/// Apply a graph delta
pub fn apply_delta(&mut self, delta: &GraphDelta) -> Result<()> {
// Remove nodes
for id in &delta.node_removes {
self.nodes.remove(id);
// Also remove incident edges
self.edges.retain(|_, (s, t, _, _)| s != id && t != id);
}
// Remove edges
for id in &delta.edge_removes {
self.edges.remove(id);
}
// Add nodes
for (id, props) in &delta.node_adds {
self.nodes.insert(id.clone(), props.clone());
}
// Add edges
for edge in &delta.edge_adds {
self.edges.insert(
edge.id.clone(),
(
edge.source.clone(),
edge.target.clone(),
edge.edge_type.clone(),
edge.properties.clone(),
),
);
}
// Update nodes
for (id, node_delta) in &delta.node_updates {
if let Some(props) = self.nodes.get_mut(id) {
for prop_delta in &node_delta.property_deltas {
match &prop_delta.operation {
PropertyOp::Set(value) => {
props.insert(prop_delta.key.clone(), value.clone());
}
PropertyOp::Remove => {
props.remove(&prop_delta.key);
}
PropertyOp::VectorDelta(vd) => {
if let Some(PropertyValue::Vector(v)) = props.get_mut(&prop_delta.key) {
vd.apply(v)
.map_err(|e| GraphDeltaError::DeltaError(format!("{:?}", e)))?;
}
}
}
}
}
}
// Update edges
for (id, edge_delta) in &delta.edge_updates {
if let Some((_, _, _, props)) = self.edges.get_mut(id) {
for prop_delta in &edge_delta.property_deltas {
match &prop_delta.operation {
PropertyOp::Set(value) => {
props.insert(prop_delta.key.clone(), value.clone());
}
PropertyOp::Remove => {
props.remove(&prop_delta.key);
}
PropertyOp::VectorDelta(_) => {
// Edges typically don't have vector properties
}
}
}
}
}
Ok(())
}
/// Get node count
pub fn node_count(&self) -> usize {
self.nodes.len()
}
/// Get edge count
pub fn edge_count(&self) -> usize {
self.edges.len()
}
}
/// Property operation types
#[derive(Debug, Clone)]
pub enum PropertyOp {
/// Set property value
Set(PropertyValue),
/// Remove property
Remove,
/// Apply vector delta to vector property
VectorDelta(VectorDelta),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_graph_delta_builder() {
let delta = GraphDeltaBuilder::new()
.add_node("a")
.add_node("b")
.add_edge("e1", "a", "b", "KNOWS")
.build();
assert_eq!(delta.node_adds.len(), 2);
assert_eq!(delta.edge_adds.len(), 1);
}
#[test]
fn test_graph_state_apply() {
let mut state = GraphState::new();
let delta = GraphDeltaBuilder::new()
.add_node("a")
.add_node("b")
.add_edge("e1", "a", "b", "KNOWS")
.build();
state.apply_delta(&delta).unwrap();
assert_eq!(state.node_count(), 2);
assert_eq!(state.edge_count(), 1);
}
#[test]
fn test_delta_compose() {
let d1 = GraphDeltaBuilder::new().add_node("a").add_node("b").build();
let d2 = GraphDeltaBuilder::new()
.remove_node("b")
.add_node("c")
.build();
let composed = d1.compose(d2);
// "a" added, "b" removed, "c" added
assert_eq!(composed.node_adds.len(), 2); // a and c
assert_eq!(composed.node_removes.len(), 1); // b
}
#[test]
fn test_affected_nodes() {
let delta = GraphDeltaBuilder::new()
.add_node("a")
.add_edge("e1", "b", "c", "RELATES")
.build();
let affected = delta.affected_nodes();
assert!(affected.contains("a"));
assert!(affected.contains("b"));
assert!(affected.contains("c"));
}
}