use graph_d::query::{AggregateFunction, Pagination, QueryBuilder, SortCriteria};
use graph_d::{Graph, Result};
use serde_json::json;
use std::collections::HashMap;
fn main() -> Result<()> {
println!("=== Advanced Query Operations Demo ===\n");
let graph = create_sample_data()?;
demo_aggregations(&graph)?;
demo_sorting(&graph)?;
demo_pagination(&graph)?;
demo_statistics(&graph)?;
demo_complex_queries(&graph)?;
Ok(())
}
fn create_sample_data() -> Result<Graph> {
let mut graph = Graph::new()?;
println!("Creating sample dataset...");
let alice_id = graph.create_node(
[
("name".to_string(), json!("Alice Johnson")),
("department".to_string(), json!("Engineering")),
("level".to_string(), json!("Senior")),
("salary".to_string(), json!(95000)),
("experience".to_string(), json!(5)),
("performance_score".to_string(), json!(4.2)),
]
.into(),
)?;
let bob_id = graph.create_node(
[
("name".to_string(), json!("Bob Smith")),
("department".to_string(), json!("Sales")),
("level".to_string(), json!("Manager")),
("salary".to_string(), json!(87000)),
("experience".to_string(), json!(7)),
("performance_score".to_string(), json!(3.8)),
]
.into(),
)?;
let charlie_id = graph.create_node(
[
("name".to_string(), json!("Charlie Brown")),
("department".to_string(), json!("Engineering")),
("level".to_string(), json!("Junior")),
("salary".to_string(), json!(65000)),
("experience".to_string(), json!(2)),
("performance_score".to_string(), json!(4.5)),
]
.into(),
)?;
let diana_id = graph.create_node(
[
("name".to_string(), json!("Diana Prince")),
("department".to_string(), json!("Marketing")),
("level".to_string(), json!("Senior")),
("salary".to_string(), json!(78000)),
("experience".to_string(), json!(6)),
("performance_score".to_string(), json!(4.0)),
]
.into(),
)?;
let eve_id = graph.create_node(
[
("name".to_string(), json!("Eve Wilson")),
("department".to_string(), json!("Engineering")),
("level".to_string(), json!("Principal")),
("salary".to_string(), json!(125000)),
("experience".to_string(), json!(10)),
("performance_score".to_string(), json!(4.7)),
]
.into(),
)?;
let frank_id = graph.create_node(
[
("name".to_string(), json!("Frank Miller")),
("department".to_string(), json!("Sales")),
("level".to_string(), json!("Senior")),
("salary".to_string(), json!(89000)),
("experience".to_string(), json!(4)),
("performance_score".to_string(), json!(3.9)),
]
.into(),
)?;
graph.create_relationship(alice_id, eve_id, "REPORTS_TO".to_string(), HashMap::new())?;
graph.create_relationship(
charlie_id,
alice_id,
"REPORTS_TO".to_string(),
HashMap::new(),
)?;
graph.create_relationship(
bob_id,
diana_id,
"COLLABORATES_WITH".to_string(),
HashMap::new(),
)?;
graph.create_relationship(alice_id, diana_id, "WORKS_WITH".to_string(), HashMap::new())?;
graph.create_relationship(frank_id, bob_id, "REPORTS_TO".to_string(), HashMap::new())?;
println!(
"Created {} employees with {} relationships\n",
graph.storage.node_count(),
graph.storage.relationship_count()
);
Ok(graph)
}
fn demo_aggregations(graph: &Graph) -> Result<()> {
println!("=== Aggregation Operations ===");
let all_employees: Vec<_> = (1..=6).collect();
let query = QueryBuilder::new(graph, all_employees);
let count_result = query.aggregate(AggregateFunction::Count)?;
println!("Total employees: {count_result:?}");
let avg_salary = query.aggregate(AggregateFunction::Avg("salary".to_string()))?;
println!(
"Average salary: ${:.2}",
match avg_salary {
graph_d::query::AggregateResult::Number(n) => n,
_ => 0.0,
}
);
let total_salary = query.aggregate(AggregateFunction::Sum("salary".to_string()))?;
println!(
"Total payroll: ${:.0}",
match total_salary {
graph_d::query::AggregateResult::Number(n) => n,
_ => 0.0,
}
);
let dept_groups = query.aggregate(AggregateFunction::GroupBy("department".to_string()))?;
if let graph_d::query::AggregateResult::Groups(groups) = dept_groups {
println!("Employees by department:");
for (dept, employees) in groups {
println!(" {}: {} employees", dept, employees.len());
}
}
let unique_depts = query.aggregate(AggregateFunction::Distinct("department".to_string()))?;
if let graph_d::query::AggregateResult::List(depts) = unique_depts {
println!("Departments: {depts:?}");
}
println!();
Ok(())
}
fn demo_sorting(graph: &Graph) -> Result<()> {
println!("=== Sorting Operations ===");
let all_employees: Vec<_> = (1..=6).collect();
let sorted_by_salary = QueryBuilder::new(graph, all_employees.clone())
.sort(vec![SortCriteria::desc("salary")])?
.nodes()?;
println!("Employees sorted by salary (highest first):");
for node in &sorted_by_salary {
let name = node
.get_property("name")
.and_then(|v| v.as_str())
.unwrap_or("Unknown");
let salary = node
.get_property("salary")
.and_then(|v| v.as_f64())
.unwrap_or(0.0);
println!(" {name}: ${salary:.0}");
}
let sorted_multi = QueryBuilder::new(graph, all_employees.clone())
.sort(vec![
SortCriteria::asc("department"),
SortCriteria::desc("performance_score"),
])?
.nodes()?;
println!("\nEmployees sorted by department, then performance (desc):");
for node in &sorted_multi {
let name = node
.get_property("name")
.and_then(|v| v.as_str())
.unwrap_or("Unknown");
let dept = node
.get_property("department")
.and_then(|v| v.as_str())
.unwrap_or("Unknown");
let score = node
.get_property("performance_score")
.and_then(|v| v.as_f64())
.unwrap_or(0.0);
println!(" {name} ({dept}): {score:.1}");
}
println!();
Ok(())
}
fn demo_pagination(graph: &Graph) -> Result<()> {
println!("=== Pagination ===");
let all_employees: Vec<_> = (1..=6).collect();
let query = QueryBuilder::new(graph, all_employees);
let page1 = query.sorted_page(vec![SortCriteria::asc("name")], Pagination::new(0, 2))?;
println!("Page 1 (first 2 employees by name):");
for node in &page1.items {
let name = node
.get_property("name")
.and_then(|v| v.as_str())
.unwrap_or("Unknown");
println!(" {name}");
}
println!(
"Total: {}, Has next: {}, Has prev: {}",
page1.total_count,
page1.has_next_page(),
page1.has_previous_page()
);
if let Some(next_pagination) = page1.next_page() {
let page2 = query.sorted_page(vec![SortCriteria::asc("name")], next_pagination)?;
println!("\nPage 2 (next 2 employees):");
for node in &page2.items {
let name = node
.get_property("name")
.and_then(|v| v.as_str())
.unwrap_or("Unknown");
println!(" {name}");
}
}
println!();
Ok(())
}
fn demo_statistics(graph: &Graph) -> Result<()> {
println!("=== Statistical Analysis ===");
let all_employees: Vec<_> = (1..=6).collect();
let query = QueryBuilder::new(graph, all_employees);
let salary_stats = query.statistics("salary")?;
println!("Salary Statistics:");
println!(" Count: {}", salary_stats.count);
println!(" Mean: ${:.2}", salary_stats.mean);
println!(" Median: ${:.2}", salary_stats.median);
println!(" Min: ${:.0}", salary_stats.min);
println!(" Max: ${:.0}", salary_stats.max);
println!(" Std Dev: ${:.2}", salary_stats.std_dev);
let performance_stats = query.statistics("performance_score")?;
println!("\nPerformance Score Statistics:");
println!(" Mean: {:.2}", performance_stats.mean);
println!(" Median: {:.2}", performance_stats.median);
println!(" Min: {:.1}", performance_stats.min);
println!(" Max: {:.1}", performance_stats.max);
println!();
Ok(())
}
fn demo_complex_queries(graph: &Graph) -> Result<()> {
println!("=== Complex Query Examples ===");
let all_employees: Vec<_> = (1..=6).collect();
let high_performers = QueryBuilder::new(graph, all_employees.clone())
.filter_by_property("department", &json!("Engineering"))?
.nodes()?
.into_iter()
.filter(|node| {
node.get_property("performance_score")
.and_then(|v| v.as_f64())
.map(|score| score >= 4.0)
.unwrap_or(false)
})
.collect::<Vec<_>>();
println!("High-performing Engineering employees (score >= 4.0):");
for node in &high_performers {
let name = node
.get_property("name")
.and_then(|v| v.as_str())
.unwrap_or("Unknown");
let score = node
.get_property("performance_score")
.and_then(|v| v.as_f64())
.unwrap_or(0.0);
println!(" {name}: {score:.1}");
}
let dept_groups = QueryBuilder::new(graph, all_employees.clone())
.group_by_sorted("department", vec![SortCriteria::desc("salary")])?;
println!("\nEmployees by department (sorted by salary desc):");
for (dept, employees) in dept_groups {
println!(" {dept}:");
for employee in employees {
let name = employee
.get_property("name")
.and_then(|v| v.as_str())
.unwrap_or("Unknown");
let salary = employee
.get_property("salary")
.and_then(|v| v.as_f64())
.unwrap_or(0.0);
println!(" {name}: ${salary:.0}");
}
}
let senior_high_performers = QueryBuilder::new(graph, all_employees.clone())
.nodes()?
.into_iter()
.filter(|node| {
let level = node
.get_property("level")
.and_then(|v| v.as_str())
.unwrap_or("");
let score = node
.get_property("performance_score")
.and_then(|v| v.as_f64())
.unwrap_or(0.0);
(level == "Senior" || level == "Principal") && score >= 4.0
})
.collect::<Vec<_>>();
println!("\nSenior+ employees with high performance:");
for node in &senior_high_performers {
let name = node
.get_property("name")
.and_then(|v| v.as_str())
.unwrap_or("Unknown");
let level = node
.get_property("level")
.and_then(|v| v.as_str())
.unwrap_or("Unknown");
let score = node
.get_property("performance_score")
.and_then(|v| v.as_f64())
.unwrap_or(0.0);
println!(" {name} ({level}): {score:.1}");
}
println!("\nAdvanced query operations completed successfully!");
Ok(())
}