graph_d 1.3.2

A native graph database implementation in Rust with built-in JSON support and SQLite-like simplicity
Documentation
//! Advanced query operations demonstration.

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()?;

    // Demonstrate aggregation operations
    demo_aggregations(&graph)?;

    // Demonstrate sorting operations
    demo_sorting(&graph)?;

    // Demonstrate pagination
    demo_pagination(&graph)?;

    // Demonstrate statistics
    demo_statistics(&graph)?;

    // Demonstrate complex queries
    demo_complex_queries(&graph)?;

    Ok(())
}

fn create_sample_data() -> Result<Graph> {
    let mut graph = Graph::new()?;

    println!("Creating sample dataset...");

    // Create employees
    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(),
    )?;

    // Create relationships
    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);

    // Count employees
    let count_result = query.aggregate(AggregateFunction::Count)?;
    println!("Total employees: {count_result:?}");

    // Average salary
    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,
        }
    );

    // Total payroll
    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,
        }
    );

    // Group by department
    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());
        }
    }

    // Unique departments
    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();

    // Sort by salary (descending)
    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}");
    }

    // Multi-criteria sorting
    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);

    // Get first page (2 items)
    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()
    );

    // Get second 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);

    // Salary statistics
    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);

    // Performance score statistics
    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();

    // Find high-performing engineering employees
    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}");
    }

    // Group by department with sorted employees
    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}");
        }
    }

    // Advanced filtering: Senior+ employees with high performance
    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(())
}