use std::collections::HashMap;
use petgraph::Graph;
use petgraph::graph::NodeIndex;
use crate::ast::*;
use crate::error::CypherError;
use crate::{CypherEdge, CypherNode};
pub(crate) fn build_graph<N: CypherNode, E: CypherEdge>(
query: CypherQuery,
) -> Result<Graph<N, E>, CypherError> {
let mut graph: Graph<N, E> = Graph::new();
let mut var_map: HashMap<String, NodeIndex> = HashMap::new();
for clause in query.clauses {
match clause {
Clause::Create { patterns } => {
for pattern in patterns {
apply_path_pattern(&mut graph, &mut var_map, &pattern);
}
}
Clause::Merge { pattern, .. } => {
apply_path_pattern(&mut graph, &mut var_map, &pattern);
}
_ => {}
}
}
Ok(graph)
}
fn apply_path_pattern<N: CypherNode, E: CypherEdge>(
graph: &mut Graph<N, E>,
var_map: &mut HashMap<String, NodeIndex>,
pattern: &PathPattern,
) {
let mut prev_idx = get_or_add_node(graph, var_map, &pattern.start);
for (rel, target_node) in &pattern.rels {
let target_idx = get_or_add_node(graph, var_map, target_node);
let edge_data = E::from_cypher(
rel.variable.clone(),
rel.rel_type.clone(),
rel.properties.clone(),
);
match rel.direction {
RelDirection::Right => {
graph.add_edge(prev_idx, target_idx, edge_data);
}
RelDirection::Left => {
graph.add_edge(target_idx, prev_idx, edge_data);
}
RelDirection::Both => {
graph.add_edge(prev_idx, target_idx, edge_data);
}
}
prev_idx = target_idx;
}
}
fn get_or_add_node<N: CypherNode, E: CypherEdge>(
graph: &mut Graph<N, E>,
var_map: &mut HashMap<String, NodeIndex>,
pattern: &NodePattern,
) -> NodeIndex {
if let Some(var) = &pattern.variable
&& let Some(&idx) = var_map.get(var)
{
return idx;
}
let data = N::from_cypher(
pattern.variable.clone(),
pattern.labels.clone(),
pattern.properties.clone(),
);
let idx = graph.add_node(data);
if let Some(var) = &pattern.variable {
var_map.insert(var.clone(), idx);
}
idx
}