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")]
110#[allow(dead_code)] // Scaffolded for hybrid graph+vector storage
111pub(crate) enum Element {
112    /// A vector point with optional metadata.
113    Point(Point),
114    /// A graph node with label, properties, and optional vector.
115    Node(GraphNode),
116}
117
118#[allow(dead_code)] // Scaffolded for hybrid graph+vector storage
119impl Element {
120    /// Returns the element ID.
121    #[must_use]
122    pub fn id(&self) -> u64 {
123        match self {
124            Self::Point(p) => p.id,
125            Self::Node(n) => n.id(),
126        }
127    }
128
129    /// Returns true if this is a Point.
130    #[must_use]
131    pub fn is_point(&self) -> bool {
132        matches!(self, Self::Point(_))
133    }
134
135    /// Returns true if this is a Node.
136    #[must_use]
137    pub fn is_node(&self) -> bool {
138        matches!(self, Self::Node(_))
139    }
140
141    /// Returns the inner Point if this is a Point.
142    #[must_use]
143    pub fn as_point(&self) -> Option<&Point> {
144        match self {
145            Self::Point(p) => Some(p),
146            Self::Node(_) => None,
147        }
148    }
149
150    /// Returns the inner Node if this is a Node.
151    #[must_use]
152    pub fn as_node(&self) -> Option<&GraphNode> {
153        match self {
154            Self::Point(_) => None,
155            Self::Node(n) => Some(n),
156        }
157    }
158
159    /// Returns true if this element has a vector embedding.
160    ///
161    /// Points always have vectors. Nodes may or may not have vectors.
162    #[must_use]
163    pub fn has_vector(&self) -> bool {
164        match self {
165            Self::Point(_) => true,
166            Self::Node(n) => n.vector().is_some(),
167        }
168    }
169
170    /// Returns the vector embedding if available.
171    #[must_use]
172    pub fn vector(&self) -> Option<&Vec<f32>> {
173        match self {
174            Self::Point(p) => Some(&p.vector),
175            Self::Node(n) => n.vector(),
176        }
177    }
178}