use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::id::{EdgeId, NodeId};
use crate::value::Value;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SearchResult {
pub id: String,
pub node_id: Option<NodeId>,
pub distance: f32,
pub metadata: HashMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SubGraph {
pub nodes: Vec<SubGraphNode>,
pub edges: Vec<SubGraphEdge>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SubGraphNode {
pub id: NodeId,
pub depth: u8,
pub properties: HashMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SubGraphEdge {
pub id: EdgeId,
pub from: NodeId,
pub to: NodeId,
pub label: String,
pub properties: HashMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct QueryResult {
pub columns: Vec<String>,
pub rows: Vec<Vec<Value>>,
pub rows_affected: u64,
}
impl QueryResult {
pub fn empty() -> Self {
Self {
columns: Vec::new(),
rows: Vec::new(),
rows_affected: 0,
}
}
pub fn row_count(&self) -> usize {
self.rows.len()
}
pub fn row_as_map(&self, index: usize) -> Option<HashMap<&str, &Value>> {
let row = self.rows.get(index)?;
Some(
self.columns
.iter()
.zip(row.iter())
.map(|(col, val)| (col.as_str(), val))
.collect(),
)
}
}
impl SubGraph {
pub fn empty() -> Self {
Self {
nodes: Vec::new(),
edges: Vec::new(),
}
}
pub fn node_count(&self) -> usize {
self.nodes.len()
}
pub fn edge_count(&self) -> usize {
self.edges.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn search_result() {
let r = SearchResult {
id: "vec-1".into(),
node_id: Some(NodeId::new("node-1")),
distance: 0.123,
metadata: HashMap::new(),
};
assert_eq!(r.id, "vec-1");
assert!(r.distance < 1.0);
}
#[test]
fn subgraph_construction() {
let mut sg = SubGraph::empty();
sg.nodes.push(SubGraphNode {
id: NodeId::new("a"),
depth: 0,
properties: HashMap::new(),
});
sg.nodes.push(SubGraphNode {
id: NodeId::new("b"),
depth: 1,
properties: HashMap::new(),
});
sg.edges.push(SubGraphEdge {
id: EdgeId::from_components("a", "b", "KNOWS"),
from: NodeId::new("a"),
to: NodeId::new("b"),
label: "KNOWS".into(),
properties: HashMap::new(),
});
assert_eq!(sg.node_count(), 2);
assert_eq!(sg.edge_count(), 1);
}
#[test]
fn query_result_row_as_map() {
let qr = QueryResult {
columns: vec!["name".into(), "age".into()],
rows: vec![vec![Value::String("Alice".into()), Value::Integer(30)]],
rows_affected: 0,
};
let row = qr.row_as_map(0).unwrap();
assert_eq!(row["name"].as_str(), Some("Alice"));
assert_eq!(row["age"].as_i64(), Some(30));
}
#[test]
fn query_result_empty() {
let qr = QueryResult::empty();
assert_eq!(qr.row_count(), 0);
assert!(qr.row_as_map(0).is_none());
}
}