Skip to main content

velesdb_core/collection/graph/
node.rs

1//! Graph node and element types for knowledge graph storage.
2//!
3//! This module provides the core types for representing nodes in a knowledge graph:
4//! - `GraphNode`: A typed entity with properties and optional vector embedding
5//! - `Element`: An enum that unifies Points (vector data) and Nodes (graph entities)
6
7use crate::Point;
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use std::collections::HashMap;
11
12/// A node in the knowledge graph.
13///
14/// Represents a typed entity with properties and an optional vector embedding.
15/// Nodes are distinct from Points in that they have a label (type) and structured
16/// properties, while Points are primarily vector data with metadata.
17///
18/// # Example
19///
20/// ```rust,ignore
21/// use velesdb_core::collection::graph::GraphNode;
22/// use serde_json::json;
23/// use std::collections::HashMap;
24///
25/// let mut props = HashMap::new();
26/// props.insert("name".to_string(), json!("Alice"));
27/// props.insert("age".to_string(), json!(30));
28///
29/// let node = GraphNode::new(1, "Person")
30///     .with_properties(props)
31///     .with_vector(vec![0.1, 0.2, 0.3]);
32/// ```
33#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
34pub struct GraphNode {
35    id: u64,
36    label: String,
37    properties: HashMap<String, Value>,
38    #[serde(skip_serializing_if = "Option::is_none")]
39    vector: Option<Vec<f32>>,
40}
41
42impl GraphNode {
43    /// Creates a new graph node with the given ID and label.
44    #[must_use]
45    pub fn new(id: u64, label: &str) -> Self {
46        Self {
47            id,
48            label: label.to_string(),
49            properties: HashMap::new(),
50            vector: None,
51        }
52    }
53
54    /// Adds properties to this node (builder pattern).
55    #[must_use]
56    pub fn with_properties(mut self, properties: HashMap<String, Value>) -> Self {
57        self.properties = properties;
58        self
59    }
60
61    /// Adds a vector embedding to this node (builder pattern).
62    #[must_use]
63    pub fn with_vector(mut self, vector: Vec<f32>) -> Self {
64        self.vector = Some(vector);
65        self
66    }
67
68    /// Returns the node ID.
69    #[must_use]
70    pub fn id(&self) -> u64 {
71        self.id
72    }
73
74    /// Returns the node label (type).
75    #[must_use]
76    pub fn label(&self) -> &str {
77        &self.label
78    }
79
80    /// Returns all properties of this node.
81    #[must_use]
82    pub fn properties(&self) -> &HashMap<String, Value> {
83        &self.properties
84    }
85
86    /// Returns a specific property value, if it exists.
87    #[must_use]
88    pub fn property(&self, name: &str) -> Option<&Value> {
89        self.properties.get(name)
90    }
91
92    /// Returns the optional vector embedding.
93    #[must_use]
94    pub fn vector(&self) -> Option<&Vec<f32>> {
95        self.vector.as_ref()
96    }
97
98    /// Sets a property value.
99    pub fn set_property(&mut self, name: &str, value: Value) {
100        self.properties.insert(name.to_string(), value);
101    }
102}
103
104/// A unified element that can be either a Point or a Node.
105///
106/// This enum allows storing both vector data (Points) and graph entities (Nodes)
107/// in the same collection, enabling hybrid graph+vector storage.
108#[derive(Debug, Clone, Serialize, Deserialize)]
109#[serde(tag = "type", rename_all = "lowercase")]
110pub enum Element {
111    /// A vector point with optional metadata.
112    Point(Point),
113    /// A graph node with label, properties, and optional vector.
114    Node(GraphNode),
115}
116
117impl Element {
118    /// Returns the element ID.
119    #[must_use]
120    pub fn id(&self) -> u64 {
121        match self {
122            Self::Point(p) => p.id,
123            Self::Node(n) => n.id(),
124        }
125    }
126
127    /// Returns true if this is a Point.
128    #[must_use]
129    pub fn is_point(&self) -> bool {
130        matches!(self, Self::Point(_))
131    }
132
133    /// Returns true if this is a Node.
134    #[must_use]
135    pub fn is_node(&self) -> bool {
136        matches!(self, Self::Node(_))
137    }
138
139    /// Returns the inner Point if this is a Point.
140    #[must_use]
141    pub fn as_point(&self) -> Option<&Point> {
142        match self {
143            Self::Point(p) => Some(p),
144            Self::Node(_) => None,
145        }
146    }
147
148    /// Returns the inner Node if this is a Node.
149    #[must_use]
150    pub fn as_node(&self) -> Option<&GraphNode> {
151        match self {
152            Self::Point(_) => None,
153            Self::Node(n) => Some(n),
154        }
155    }
156
157    /// Returns true if this element has a vector embedding.
158    ///
159    /// Points always have vectors. Nodes may or may not have vectors.
160    #[must_use]
161    pub fn has_vector(&self) -> bool {
162        match self {
163            Self::Point(_) => true,
164            Self::Node(n) => n.vector().is_some(),
165        }
166    }
167
168    /// Returns the vector embedding if available.
169    #[must_use]
170    pub fn vector(&self) -> Option<&Vec<f32>> {
171        match self {
172            Self::Point(p) => Some(&p.vector),
173            Self::Node(n) => n.vector(),
174        }
175    }
176}