use graph_d::query::{AggregateFunction, Pagination, QueryBuilder, SortCriteria};
use graph_d::transaction::{IsolationLevel, LockableResource, TransactionManager};
use graph_d::{Graph, Result};
use serde_json::json;
use std::collections::HashMap;
fn main() -> Result<()> {
println!("🦀 Welcome to Graph_D - Getting Started Guide\n");
basic_operations()?;
property_operations()?;
graph_traversal()?;
advanced_queries()?;
persistent_storage()?;
concurrent_transactions()?;
println!("🎉 Congratulations! You've completed the Graph_D getting started guide.");
println!("Check out more examples in the examples/ directory!");
Ok(())
}
fn basic_operations() -> Result<()> {
println!("=== Part 1: Basic Graph Operations ===");
let mut graph = Graph::new()?;
println!("✓ Created new graph database");
let alice_id = graph.create_node(
[
("name".to_string(), json!("Alice")),
("age".to_string(), json!(30)),
]
.into(),
)?;
let bob_id = graph.create_node(
[
("name".to_string(), json!("Bob")),
("age".to_string(), json!(25)),
]
.into(),
)?;
println!("✓ Created nodes: Alice (ID: {alice_id}), Bob (ID: {bob_id})");
let rel_id = graph.create_relationship(
alice_id,
bob_id,
"KNOWS".to_string(),
[("since".to_string(), json!("2020"))].into(),
)?;
println!("✓ Created relationship: Alice KNOWS Bob (ID: {rel_id})");
if let Some(alice) = graph.get_node(alice_id)? {
println!("✓ Retrieved Alice: {:?}", alice.properties);
}
if let Some(relationship) = graph.get_relationship(rel_id)? {
println!(
"✓ Retrieved relationship: {} -> {} ({})",
relationship.from_id, relationship.to_id, relationship.rel_type
);
}
println!("✓ Part 1 completed successfully!\n");
Ok(())
}
fn property_operations() -> Result<()> {
println!("=== Part 2: Working with Properties ===");
let mut graph = Graph::new()?;
let user_id = graph.create_node(
[
("name".to_string(), json!("John Doe")),
("email".to_string(), json!("john@example.com")),
(
"profile".to_string(),
json!({
"bio": "Software engineer with 5 years experience",
"skills": ["Rust", "Python", "JavaScript"],
"location": {
"city": "San Francisco",
"country": "USA",
"coordinates": [37.7749, -122.4194]
},
"preferences": {
"notifications": true,
"theme": "dark"
}
}),
),
(
"metadata".to_string(),
json!({
"created_at": "2024-01-15T10:30:00Z",
"last_login": "2024-01-19T14:22:33Z",
"login_count": 42
}),
),
]
.into(),
)?;
println!("✓ Created user with rich JSON properties");
if let Some(user) = graph.get_node(user_id)? {
println!("✓ User name: {:?}", user.get_property("name"));
if let Some(profile) = user.get_property("profile") {
println!("✓ User profile: {profile}");
}
println!("✓ Property keys: {:?}", user.property_keys());
}
let company_id = graph.create_node(
[
("name".to_string(), json!("TechCorp Inc.")),
("industry".to_string(), json!("Software")),
("size".to_string(), json!("500-1000")),
]
.into(),
)?;
let _employment_rel = graph.create_relationship(
user_id,
company_id,
"WORKS_FOR".to_string(),
[
("position".to_string(), json!("Senior Developer")),
("start_date".to_string(), json!("2023-03-01")),
("salary".to_string(), json!(95000)),
(
"benefits".to_string(),
json!(["health", "dental", "401k", "stock_options"]),
),
]
.into(),
)?;
println!("✓ Created employment relationship with detailed properties");
println!("✓ Part 2 completed successfully!\n");
Ok(())
}
fn graph_traversal() -> Result<()> {
println!("=== Part 3: Graph Traversal ===");
let mut graph = Graph::new()?;
let alice_id = graph.create_node([("name".to_string(), json!("Alice"))].into())?;
let bob_id = graph.create_node([("name".to_string(), json!("Bob"))].into())?;
let charlie_id = graph.create_node([("name".to_string(), json!("Charlie"))].into())?;
let diana_id = graph.create_node([("name".to_string(), json!("Diana"))].into())?;
graph.create_relationship(alice_id, bob_id, "FRIENDS_WITH".to_string(), HashMap::new())?;
graph.create_relationship(
bob_id,
charlie_id,
"FRIENDS_WITH".to_string(),
HashMap::new(),
)?;
graph.create_relationship(
charlie_id,
diana_id,
"FRIENDS_WITH".to_string(),
HashMap::new(),
)?;
graph.create_relationship(
alice_id,
diana_id,
"FRIENDS_WITH".to_string(),
HashMap::new(),
)?;
println!("✓ Created social network: Alice -> Bob -> Charlie -> Diana");
let alice_friends = QueryBuilder::from_node(&graph, alice_id)
.outgoing("FRIENDS_WITH")?
.nodes()?;
println!("✓ Alice's direct friends:");
for friend in &alice_friends {
let name = friend
.get_property("name")
.and_then(|v| v.as_str())
.unwrap_or("Unknown");
println!(" - {name}");
}
let friends_of_friends = QueryBuilder::from_node(&graph, alice_id)
.outgoing("FRIENDS_WITH")?
.outgoing("FRIENDS_WITH")?
.nodes()?;
println!("✓ Friends of Alice's friends:");
for friend in &friends_of_friends {
let name = friend
.get_property("name")
.and_then(|v| v.as_str())
.unwrap_or("Unknown");
println!(" - {name}");
}
use graph_d::query::PathFinder;
let path_finder = PathFinder::new(&graph);
if let Some(path) = path_finder.shortest_path(alice_id, diana_id)? {
println!("✓ Shortest path from Alice to Diana: {path:?}");
println!(" Path length: {} hops", path.len() - 1);
}
println!("✓ Part 3 completed successfully!\n");
Ok(())
}
fn advanced_queries() -> Result<()> {
println!("=== Part 4: Advanced Queries ===");
let mut graph = Graph::new()?;
let departments = ["Engineering", "Sales", "Marketing", "HR"];
let levels = ["Junior", "Senior", "Manager", "Director"];
let mut employee_ids = Vec::new();
for i in 0..20 {
let employee_id = graph.create_node(
[
("name".to_string(), json!(format!("Employee_{}", i))),
(
"department".to_string(),
json!(departments[i % departments.len()]),
),
("level".to_string(), json!(levels[i % levels.len()])),
("salary".to_string(), json!(50000 + (i * 5000))),
("performance".to_string(), json!(3.0 + (i as f64 % 3.0))),
("years_experience".to_string(), json!(1 + (i % 10))),
]
.into(),
)?;
employee_ids.push(employee_id);
}
println!(
"✓ Created {} employees across {} departments",
employee_ids.len(),
departments.len()
);
let query = QueryBuilder::new(&graph, employee_ids.clone());
let total_count = query.aggregate(AggregateFunction::Count)?;
println!("✓ Total employees: {total_count:?}");
let avg_salary = query.aggregate(AggregateFunction::Avg("salary".to_string()))?;
println!("✓ Average salary: {avg_salary:?}");
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 sorted_page = query.sorted_page(
vec![SortCriteria::desc("salary"), SortCriteria::asc("name")],
Pagination::new(0, 5),
)?;
println!("✓ Top 5 highest paid employees:");
for (i, employee) in sorted_page.items.iter().enumerate() {
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!(" {}. {}: ${:.0}", i + 1, name, salary);
}
let salary_stats = query.statistics("salary")?;
println!("✓ Salary statistics:");
println!(" Mean: ${:.2}", salary_stats.mean);
println!(" Median: ${:.2}", salary_stats.median);
println!(" Standard deviation: ${:.2}", salary_stats.std_dev);
println!("✓ Part 4 completed successfully!\n");
Ok(())
}
fn persistent_storage() -> Result<()> {
println!("=== Part 5: Persistent Storage ===");
let db_path = "getting_started_test.db";
{
let mut graph = Graph::open(db_path)?;
println!("✓ Created persistent database at: {db_path}");
let node_id = graph.create_node(
[
("type".to_string(), json!("persistent_test")),
("created_at".to_string(), json!("2024-01-01")),
("data".to_string(), json!({"important": true, "value": 42})),
]
.into(),
)?;
println!("✓ Added test data (Node ID: {node_id})");
graph.storage.flush()?;
println!("✓ Data flushed to disk");
}
{
let _graph = Graph::open(db_path)?;
println!("✓ Reopened database from disk");
println!("✓ Database reopened successfully (metadata persisted)");
}
std::fs::remove_file(db_path).ok();
println!("✓ Cleaned up test database");
println!("✓ Part 5 completed successfully!\n");
Ok(())
}
fn concurrent_transactions() -> Result<()> {
println!("=== Part 6: Concurrent Transactions ===");
let tx_manager = TransactionManager::new(IsolationLevel::ReadCommitted);
println!("✓ Created transaction manager with ReadCommitted isolation");
{
let mut tx = tx_manager.begin_concurrent();
println!("✓ Started transaction {}", tx.id());
tx.read_lock(LockableResource::Node(1))?;
tx.write_lock(LockableResource::Node(2))?;
println!("✓ Acquired read lock on Node 1 and write lock on Node 2");
tx.commit()?;
println!("✓ Transaction committed and locks released");
}
{
let mut tx1 = tx_manager.begin_concurrent();
let mut tx2 = tx_manager.begin_concurrent();
let mut tx3 = tx_manager.begin_concurrent();
tx1.read_lock(LockableResource::Node(100))?;
tx2.read_lock(LockableResource::Node(100))?;
tx3.read_lock(LockableResource::Node(100))?;
println!("✓ Three transactions successfully acquired shared read locks");
tx1.commit()?;
tx2.commit()?;
tx3.commit()?;
println!("✓ All transactions committed successfully");
}
let stats = tx_manager.lock_statistics();
println!("✓ Current lock statistics:");
println!(" Active locks: {}", stats.total_active_locks);
println!(" Waiting requests: {}", stats.waiting_requests);
println!(" Locked resources: {}", stats.locked_resources);
println!("✓ Part 6 completed successfully!\n");
Ok(())
}
#[allow(dead_code)]
fn error_handling_example() -> Result<()> {
let mut graph = Graph::new()?;
match graph.create_relationship(999, 1000, "INVALID".to_string(), HashMap::new()) {
Ok(_) => println!("This shouldn't happen"),
Err(e) => println!("Expected error: {e}"),
}
match graph.get_node(999)? {
Some(_) => println!("This shouldn't happen"),
None => println!("Node not found (expected)"),
}
Ok(())
}