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