1use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6
7use crate::id::{EdgeId, NodeId};
8use crate::value::Value;
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12pub struct SearchResult {
13 pub id: String,
15 pub node_id: Option<NodeId>,
17 pub distance: f32,
19 pub metadata: HashMap<String, Value>,
21}
22
23#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
25pub struct SubGraph {
26 pub nodes: Vec<SubGraphNode>,
28 pub edges: Vec<SubGraphEdge>,
30}
31
32#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
34pub struct SubGraphNode {
35 pub id: NodeId,
37 pub depth: u8,
39 pub properties: HashMap<String, Value>,
41}
42
43#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
45pub struct SubGraphEdge {
46 pub id: EdgeId,
48 pub from: NodeId,
50 pub to: NodeId,
52 pub label: String,
54 pub properties: HashMap<String, Value>,
56}
57
58#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
60pub struct QueryResult {
61 pub columns: Vec<String>,
63 pub rows: Vec<Vec<Value>>,
65 pub rows_affected: u64,
67}
68
69impl QueryResult {
70 pub fn empty() -> Self {
72 Self {
73 columns: Vec::new(),
74 rows: Vec::new(),
75 rows_affected: 0,
76 }
77 }
78
79 pub fn row_count(&self) -> usize {
81 self.rows.len()
82 }
83
84 pub fn row_as_map(&self, index: usize) -> Option<HashMap<&str, &Value>> {
86 let row = self.rows.get(index)?;
87 Some(
88 self.columns
89 .iter()
90 .zip(row.iter())
91 .map(|(col, val)| (col.as_str(), val))
92 .collect(),
93 )
94 }
95}
96
97impl SubGraph {
98 pub fn empty() -> Self {
100 Self {
101 nodes: Vec::new(),
102 edges: Vec::new(),
103 }
104 }
105
106 pub fn node_count(&self) -> usize {
108 self.nodes.len()
109 }
110
111 pub fn edge_count(&self) -> usize {
113 self.edges.len()
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn search_result() {
123 let r = SearchResult {
124 id: "vec-1".into(),
125 node_id: Some(NodeId::new("node-1")),
126 distance: 0.123,
127 metadata: HashMap::new(),
128 };
129 assert_eq!(r.id, "vec-1");
130 assert!(r.distance < 1.0);
131 }
132
133 #[test]
134 fn subgraph_construction() {
135 let mut sg = SubGraph::empty();
136 sg.nodes.push(SubGraphNode {
137 id: NodeId::new("a"),
138 depth: 0,
139 properties: HashMap::new(),
140 });
141 sg.nodes.push(SubGraphNode {
142 id: NodeId::new("b"),
143 depth: 1,
144 properties: HashMap::new(),
145 });
146 sg.edges.push(SubGraphEdge {
147 id: EdgeId::from_components("a", "b", "KNOWS"),
148 from: NodeId::new("a"),
149 to: NodeId::new("b"),
150 label: "KNOWS".into(),
151 properties: HashMap::new(),
152 });
153 assert_eq!(sg.node_count(), 2);
154 assert_eq!(sg.edge_count(), 1);
155 }
156
157 #[test]
158 fn query_result_row_as_map() {
159 let qr = QueryResult {
160 columns: vec!["name".into(), "age".into()],
161 rows: vec![vec![Value::String("Alice".into()), Value::Integer(30)]],
162 rows_affected: 0,
163 };
164 let row = qr.row_as_map(0).unwrap();
165 assert_eq!(row["name"].as_str(), Some("Alice"));
166 assert_eq!(row["age"].as_i64(), Some(30));
167 }
168
169 #[test]
170 fn query_result_empty() {
171 let qr = QueryResult::empty();
172 assert_eq!(qr.row_count(), 0);
173 assert!(qr.row_as_map(0).is_none());
174 }
175}