use graph_d::{gql::Gql, Graph, Result};
use serde_json::json;
use std::cell::RefCell;
fn main() -> Result<()> {
println!("=== GQL (Graph Query Language) Demo ===\n");
let mut graph = Graph::new()?;
setup_sample_data(&mut graph)?;
let graph = RefCell::new(graph);
let gql = Gql::new(&graph);
demo_basic_queries(&gql)?;
demo_path_queries(&gql)?;
demo_property_filtering(&gql)?;
demo_pattern_matching(&gql)?;
println!("🎉 GQL demo completed successfully!");
Ok(())
}
fn setup_sample_data(graph: &mut Graph) -> Result<()> {
println!("Setting up sample social network data...\n");
let alice_id = graph.create_node(
[
("type".to_string(), json!("Person")),
("name".to_string(), json!("Alice")),
("age".to_string(), json!(30)),
("city".to_string(), json!("San Francisco")),
("occupation".to_string(), json!("Engineer")),
]
.into(),
)?;
let bob_id = graph.create_node(
[
("type".to_string(), json!("Person")),
("name".to_string(), json!("Bob")),
("age".to_string(), json!(25)),
("city".to_string(), json!("Seattle")),
("occupation".to_string(), json!("Designer")),
]
.into(),
)?;
let charlie_id = graph.create_node(
[
("type".to_string(), json!("Person")),
("name".to_string(), json!("Charlie")),
("age".to_string(), json!(35)),
("city".to_string(), json!("San Francisco")),
("occupation".to_string(), json!("Manager")),
]
.into(),
)?;
let diana_id = graph.create_node(
[
("type".to_string(), json!("Person")),
("name".to_string(), json!("Diana")),
("age".to_string(), json!(28)),
("city".to_string(), json!("Portland")),
("occupation".to_string(), json!("Artist")),
]
.into(),
)?;
let techcorp_id = graph.create_node(
[
("type".to_string(), json!("Company")),
("name".to_string(), json!("TechCorp")),
("industry".to_string(), json!("Technology")),
("size".to_string(), json!("Large")),
]
.into(),
)?;
let designstudio_id = graph.create_node(
[
("type".to_string(), json!("Company")),
("name".to_string(), json!("Design Studio")),
("industry".to_string(), json!("Creative")),
("size".to_string(), json!("Small")),
]
.into(),
)?;
graph.create_relationship(
alice_id,
bob_id,
"KNOWS".to_string(),
[("since".to_string(), json!("2020"))].into(),
)?;
graph.create_relationship(
bob_id,
charlie_id,
"KNOWS".to_string(),
[("since".to_string(), json!("2019"))].into(),
)?;
graph.create_relationship(
charlie_id,
diana_id,
"KNOWS".to_string(),
[("since".to_string(), json!("2021"))].into(),
)?;
graph.create_relationship(
alice_id,
diana_id,
"KNOWS".to_string(),
[("since".to_string(), json!("2022"))].into(),
)?;
graph.create_relationship(
alice_id,
techcorp_id,
"WORKS_FOR".to_string(),
[("position".to_string(), json!("Senior Engineer"))].into(),
)?;
graph.create_relationship(
bob_id,
designstudio_id,
"WORKS_FOR".to_string(),
[("position".to_string(), json!("Lead Designer"))].into(),
)?;
graph.create_relationship(
charlie_id,
techcorp_id,
"WORKS_FOR".to_string(),
[("position".to_string(), json!("Engineering Manager"))].into(),
)?;
println!(
"✓ Created {} users and {} companies with relationships",
4, 2
);
Ok(())
}
fn demo_basic_queries(gql: &Gql) -> Result<()> {
println!("=== Demo 1: Basic Node Queries ===\n");
println!("🔍 Query: Find all people");
println!("MATCH (p:Person) RETURN p.name, p.age");
match gql.execute("MATCH (p:Person) RETURN p.name, p.age") {
Ok(result) => {
println!("✓ Results: {} rows", result.row_count());
print_query_result(&result);
}
Err(e) => println!("❌ Error: {e}"),
}
println!();
println!("🔍 Query: Find all companies");
println!("MATCH (c:Company) RETURN c.name, c.industry");
match gql.execute("MATCH (c:Company) RETURN c.name, c.industry") {
Ok(result) => {
println!("✓ Results: {} rows", result.row_count());
print_query_result(&result);
}
Err(e) => println!("❌ Error: {e}"),
}
println!();
Ok(())
}
fn demo_path_queries(gql: &Gql) -> Result<()> {
println!("=== Demo 2: Path Traversal Queries ===\n");
println!("🔍 Query: Find friendship connections");
println!("MATCH (p1:Person)-[:KNOWS]->(p2:Person) RETURN p1.name, p2.name");
match gql.execute("MATCH (p1:Person)-[:KNOWS]->(p2:Person) RETURN p1.name, p2.name") {
Ok(result) => {
println!("✓ Results: {} relationships", result.row_count());
print_query_result(&result);
}
Err(e) => println!("❌ Error: {e}"),
}
println!();
println!("🔍 Query: Find employment relationships");
println!("MATCH (p:Person)-[:WORKS_FOR]->(c:Company) RETURN p.name, c.name");
match gql.execute("MATCH (p:Person)-[:WORKS_FOR]->(c:Company) RETURN p.name, c.name") {
Ok(result) => {
println!("✓ Results: {} employment relationships", result.row_count());
print_query_result(&result);
}
Err(e) => println!("❌ Error: {e}"),
}
println!();
Ok(())
}
fn demo_property_filtering(gql: &Gql) -> Result<()> {
println!("=== Demo 3: Property Filtering ===\n");
println!("🔍 Query: Find people over 30");
println!("MATCH (p:Person) WHERE p.age > 30 RETURN p.name, p.age");
match gql.execute("MATCH (p:Person) WHERE p.age > 30 RETURN p.name, p.age") {
Ok(result) => {
println!("✓ Results: {} people", result.row_count());
print_query_result(&result);
}
Err(e) => println!("❌ Error: {e}"),
}
println!();
println!("🔍 Query: Find people in San Francisco");
println!("MATCH (p:Person) WHERE p.city = 'San Francisco' RETURN p.name, p.occupation");
match gql.execute("MATCH (p:Person) WHERE p.city = 'San Francisco' RETURN p.name, p.occupation")
{
Ok(result) => {
println!("✓ Results: {} people", result.row_count());
print_query_result(&result);
}
Err(e) => println!("❌ Error: {e}"),
}
println!();
Ok(())
}
fn demo_pattern_matching(gql: &Gql) -> Result<()> {
println!("=== Demo 4: Complex Pattern Matching ===\n");
println!("🔍 Query: Find coworkers");
println!("MATCH (p1:Person)-[:WORKS_FOR]->(c:Company)<-[:WORKS_FOR]-(p2:Person) RETURN p1.name, p2.name, c.name");
match gql.execute("MATCH (p1:Person)-[:WORKS_FOR]->(c:Company)<-[:WORKS_FOR]-(p2:Person) RETURN p1.name, p2.name, c.name") {
Ok(result) => {
println!("✓ Results: {} coworker relationships", result.row_count());
print_query_result(&result);
}
Err(e) => println!("❌ Error: {e}"),
}
println!();
println!("🔍 Query: Find friends of friends");
println!("MATCH (p:Person)-[:KNOWS]->()-[:KNOWS]->(fof:Person) RETURN p.name, fof.name");
match gql
.execute("MATCH (p:Person)-[:KNOWS]->()-[:KNOWS]->(fof:Person) RETURN p.name, fof.name")
{
Ok(result) => {
println!(
"✓ Results: {} friend-of-friend relationships",
result.row_count()
);
print_query_result(&result);
}
Err(e) => println!("❌ Error: {e}"),
}
println!();
Ok(())
}
fn print_query_result(result: &graph_d::gql::QueryResult) {
if result.is_empty() {
println!(" (no results)");
return;
}
print!(" ");
for (i, column) in result.columns.iter().enumerate() {
if i > 0 {
print!(" | ");
}
print!("{column:<15}");
}
println!();
print!(" ");
for (i, _) in result.columns.iter().enumerate() {
if i > 0 {
print!("-+-");
}
print!("{}", "-".repeat(15));
}
println!();
for row in &result.rows {
print!(" ");
for (i, value) in row.iter().enumerate() {
if i > 0 {
print!(" | ");
}
let display_value = format_query_value(value);
print!("{display_value:<15}");
}
println!();
}
println!();
}
fn format_query_value(value: &graph_d::gql::QueryValue) -> String {
match value {
graph_d::gql::QueryValue::Null => "NULL".to_string(),
graph_d::gql::QueryValue::Boolean(b) => b.to_string(),
graph_d::gql::QueryValue::Integer(i) => i.to_string(),
graph_d::gql::QueryValue::Float(f) => format!("{f:.2}"),
graph_d::gql::QueryValue::String(s) => s.clone(),
graph_d::gql::QueryValue::Node { id, .. } => format!("Node({id})"),
graph_d::gql::QueryValue::Relationship { id, .. } => format!("Rel({id})"),
graph_d::gql::QueryValue::Path(_) => "Path".to_string(),
graph_d::gql::QueryValue::List(l) => format!("List[{}]", l.len()),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gql_basic_functionality() -> Result<()> {
let mut graph = Graph::new()?;
setup_sample_data(&mut graph)?;
let gql = Gql::new(&graph);
let result = gql.execute("MATCH (p:Person) RETURN p.name")?;
assert!(result.row_count() > 0);
assert_eq!(result.columns.len(), 1);
assert_eq!(result.columns[0], "p.name");
Ok(())
}
#[test]
fn test_gql_parsing() -> Result<()> {
let graph = Graph::new()?;
let gql = Gql::new(&graph);
let ast = gql.parse("MATCH (n:Person) WHERE n.age > 25 RETURN n.name")?;
if let graph_d::gql::GqlStatement::LinearQuery(query) = ast {
assert_eq!(query.clauses.len(), 3); } else {
panic!("Expected LinearQuery");
}
Ok(())
}
}