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}