A high-performance graph database built in Rust with both embedded and network modes, featuring ultra-fast node/edge operations, advanced caching, comprehensive graph algorithms, and SQL-like join operations.
- 138,794 ops/sec node retrieval with LRU caching
- 19,503 ops/sec node creation with compression
- 10,492 ops/sec network throughput
- 100% cache hit rate for frequently accessed data
- Embedded Mode: Direct in-process database for maximum performance
- Network Mode: TCP client-server with connection pooling (port 5432)
- Zero-copy operations with memory-mapped files
- Nodes & Edges with typed properties and metadata
- Graph Algorithms: BFS, DFS, shortest path, connected components
- Advanced Queries: Label-based, property-based, range queries
- Fluent Query Builder with method chaining
- INNER JOIN: Edge-based and property-based joining
- LEFT JOIN: Include all left records with NULL handling
- RIGHT JOIN: Include all right records
- FULL OUTER JOIN: Complete union of both sides
- CROSS JOIN: Cartesian product with filtering
- Aggregate Joins: SUM, AVG, COUNT, MAX, MIN with GROUP BY
- WHERE Clauses: Rust closure-based filtering
- ORDER BY & LIMIT: Sorting and pagination
- Fluent Builder API: Chain operations with type safety
- Connection pooling with idle timeout and cleanup
- LZ4 compression for storage efficiency
- Async/await architecture with Tokio
- Custom binary protocol for network communication
- Comprehensive error handling and logging
Mini Database provides enterprise-grade join functionality that rivals traditional SQL databases while leveraging the power of graph relationships:
// All join types supported
let result = client.join("user", "order")
.join_type(JoinType::Inner) // INNER, LEFT, RIGHT, FullOuter, CROSS
.on_edge("places_order".to_string()) // Edge-based joining
.select(vec![
("user".to_string(), "name".to_string()),
("order".to_string(), "total".to_string()),
])
.where_condition(|row| { // Custom filtering
if let Some(Value::Float(total)) = row.get("order.total") {
*total > 100.0
} else { false }
})
.order_by("order.total".to_string(), false) // DESC ordering
.limit(10) // Pagination
.execute().await?;
result.print(); // Beautiful table output
| Join Type | Use Case | Performance | Graph Advantage |
|---|---|---|---|
| INNER JOIN | Matching records only | β‘ Very Fast | Direct edge traversal |
| LEFT JOIN | All left + matching right | π Fast | NULL handling optimized |
| CROSS JOIN | All combinations | Efficient iteration | |
| AGGREGATE | GROUP BY operations | π Fast | Native graph grouping |
// Traditional SQL approach (slow)
SELECT u.name, o.total
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.total > 100;
// Mini Database approach (fast)
client.join("user", "order")
.on_edge("places_order") // Direct graph relationship
.where_condition(|row| total > 100.0) // Rust closure filtering
.execute().await?;
Why Graph Joins are Superior:
- π Direct relationship traversal (no expensive hash joins)
- β‘ Sub-microsecond edge walking vs millisecond SQL joins
- π― Type-safe filtering with Rust closures
- π Linear scaling with relationship count
Add to your Cargo.toml:
[dependencies]
mini-database = { git = "https://github.com/AarambhDevHub/mini-database.git" }
tokio = { version = "1.0", features = ["full"] }
use mini_database::{Database, DatabaseClient, DatabaseConfig, Node, Edge, Value};
use mini_database::{JoinType, AggregateFunction}; // Import join types
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create database
let config = DatabaseConfig::new("./data")
.with_cache_size(128) // 128MB cache
.with_compression(true);
let database = Database::new(config).await?;
let client = DatabaseClient::new(database);
// Create nodes
let alice = Node::new("person")
.with_property("name", Value::String("Alice".to_string()))
.with_property("age", Value::Integer(30));
let bob = Node::new("person")
.with_property("name", Value::String("Bob".to_string()))
.with_property("age", Value::Integer(25));
let alice_id = client.create_node(alice).await?;
let bob_id = client.create_node(bob).await?;
// Create relationship
let friendship = Edge::new(&alice_id, &bob_id, "friends")
.with_property("since", Value::String("2020".to_string()));
client.create_edge(friendship).await?;
// Query with fluent API
let people = client.execute_query(
client.query_nodes()
.with_label("person")
.where_gt("age", Value::Integer(25))
.limit(10)
).await?;
// β¨ NEW: SQL-like JOIN operations
let join_result = client.join("person", "person")
.join_type(JoinType::Inner)
.on_edge("friends".to_string())
.select(vec![
("person".to_string(), "name".to_string()),
])
.execute().await?;
join_result.print(); // Beautiful table output
// Graph traversal
let connections = client.bfs(&alice_id, 3).await?;
println!("Found {} connected nodes", connections.len());
Ok(())
}
Start the Database Server:
cargo run --example database_server
Connect from Client:
use mini_database::{NetworkDatabaseClient, Node, Value};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Connect to server
let mut client = NetworkDatabaseClient::connect("127.0.0.1", 5432).await?;
// Create node over network
let node = Node::new("user")
.with_property("email", Value::String("user@example.com".to_string()));
let node_id = client.create_node(node).await?;
// Retrieve node
let retrieved = client.get_node(&node_id).await?;
println!("Retrieved: {:?}", retrieved);
// Graph traversal over network
let neighbors = client.bfs(&node_id, 2).await?;
Ok(())
}
βββββββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββ
β CLIENT MODE β β SERVER MODE β
βββββββββββββββββββββββββββββββββββ€ βββββββββββββββββββββββββββββββββββ€
β β β βββββββββββββββββββββββββββββββ β
β βββββββββββββββββββββββββββββββ β β β Network Clients β β
β β Application Code β β β β (TCP Connections) β β
β βββββββββββββββββββββββββββββββ β β βββββββββββββββββββββββββββββββ β
β βββββββββββββββββββββββββββββββ β β βββββββββββββββββββββββββββββββ β
β β DatabaseClient API β β β β Connection Pool β β
β β + Join Operations β β β β + Request Handler β β
β βββββββββββββββββββββββββββββββ β β βββββββββββββββββββββββββββββββ β
β β β β
βββββββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββ
β β
βΌ βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DATABASE ENGINE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
β β Query Builder β β Graph Operations β β Query Executor β β
β β - Fluent API β β - BFS/DFS β β - Optimization β β
β β - Conditions β β - Shortest Path β β - Execution β β
β β - JOIN Ops β¨ β β - Join Traversalβ β - Join Engine β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
β β Node Store β β Edge Store β β Index Store β β
β β - Serializationβ β - Adjacency Listβ β - B-tree β β
β β - Properties β β - Relationshipsβ β - Fast Lookup β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
β β LRU Cache β β File Reader β β Compression β β
β β - Hot Data β β - Memory Map β β - LZ4 β β
β β - 100% Hit Rate β β - Async I/O β β - Space Saving β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Operation β Count β Duration β Ops/Sec β Latency
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Node Creation β 10,000 β 512ms β 19,503 β 51ΞΌs
Node Retrieval β 10,000 β 72ms β 138,794 β 7ΞΌs
Node Updates β 5,000 β 341ms β 14,635 β 68ΞΌs
Edge Creation β 1,000 β 58ms β 16,965 β 59ΞΌs
Graph Traversal β 10 β 2ms β 4,958 β 202ΞΌs
Batch Operations β 100 β 3ms β 26,033 β 38ΞΌs
Join Operation β Count β Duration β Ops/Sec β Latency
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
INNER JOIN β 1,000 β 45ms β 22,222 β 45ΞΌs
LEFT JOIN β 1,000 β 52ms β 19,230 β 52ΞΌs
CROSS JOIN β 100 β 15ms β 6,666 β 150ΞΌs
Aggregate SUM β 500 β 28ms β 17,857 β 56ΞΌs
Complex Filter β 750 β 35ms β 21,428 β 47ΞΌs
Operation β Count β Duration β Ops/Sec β Latency
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Node Creation β 5,000 β 631ms β 7,917 β 126ΞΌs
Node Retrieval β 5,000 β 328ms β 15,234 β 66ΞΌs
Graph Traversal β 5 β 1ms β 10,743 β 93ΞΌs
Network Latency β 100 β 3ms β 25,354 β 39ΞΌs
Overall Throughput: 10,492 ops/sec
| Database | Throughput | Use Case | Mini Database |
|---|---|---|---|
| Redis | 200K ops/sec | In-memory cache | β 138K retrieval |
| MongoDB | 50K ops/sec | Document store | β 19K creation |
| PostgreSQL | 10K ops/sec | Relational DB | β 10K network |
| Neo4j | 5K ops/sec | Graph database | β 5K traversal |
| Joins | 2-5K ops/sec | SQL JOINs | β 22K joins |
// Create node with properties
let node = Node::new("label")
.with_property("key", Value::String("value".to_string()))
.with_property("count", Value::Integer(42));
let node_id = client.create_node(node).await?;
// Retrieve node
let node = client.get_node(&node_id).await?;
// Update node
if let Some(mut node) = client.get_node(&node_id).await? {
node.set_property("updated", Value::Boolean(true));
client.update_node(&node).await?;
}
// Delete node
client.delete_node(&node_id).await?;
// Find nodes by criteria
let nodes = client.find_nodes_by_label("person").await?;
let nodes = client.find_nodes_by_property("age", &Value::Integer(25)).await?;
// Create edge
let edge = Edge::new(&source_id, &target_id, "relationship_type")
.with_property("weight", Value::Float(0.8))
.with_property("created_at", Value::String("2023-01-01".to_string()));
let edge_id = client.create_edge(edge).await?;
// Get node connections
let all_edges = client.get_node_edges(&node_id).await?;
let outgoing = client.get_outgoing_edges(&node_id).await?;
let incoming = client.get_incoming_edges(&node_id).await?;
use mini_database::{JoinType, AggregateFunction};
// INNER JOIN with edge relationships
let inner_result = client.join("user", "order")
.join_type(JoinType::Inner)
.on_edge("places_order".to_string())
.select(vec![
("user".to_string(), "name".to_string()),
("order".to_string(), "total".to_string()),
])
.execute().await?;
// LEFT JOIN with property matching
let left_result = client.join("user", "profile")
.join_type(JoinType::Left)
.on_property("id".to_string(), "user_id".to_string())
.select(vec![
("user".to_string(), "name".to_string()),
("profile".to_string(), "bio".to_string()),
])
.execute().await?;
// Complex JOIN with filtering and ordering
let complex_result = client.join("user", "order")
.join_type(JoinType::Inner)
.on_edge("places_order".to_string())
.select(vec![
("user".to_string(), "name".to_string()),
("user".to_string(), "city".to_string()),
("order".to_string(), "total".to_string()),
])
.where_condition(|row| {
// Filter high-value orders
if let Some(Value::Float(total)) = row.get("order.total") {
*total > 100.0
} else { false }
})
.where_condition(|row| {
// Filter specific cities
if let Some(Value::String(city)) = row.get("user.city") {
city == "New York" || city == "San Francisco"
} else { false }
})
.order_by("order.total".to_string(), false) // DESC
.limit(20)
.execute().await?;
complex_result.print(); // Beautiful table output
// Aggregate operations
let user_totals = client.aggregate_join(
"user",
"order",
"places_order",
"name", // Group by user name
"total", // Sum order totals
AggregateFunction::Sum,
).await?;
for (user, total_spent) in user_totals {
println!("{}: ${:.2}", user, total_spent);
}
// Breadth-first search
let connected = client.bfs(&start_node_id, 3).await?;
// Depth-first search
let connected = client.dfs(&start_node_id, 3).await?;
// Shortest path
if let Some(path) = client.shortest_path(&start_id, &end_id).await? {
println!("Path length: {}", path.len());
}
// Get neighbors within distance
let neighbors = client.get_neighbors(&node_id, 2).await?;
// Complex node queries
let results = client.execute_query(
client.query_nodes()
.with_label("person")
.where_eq("department", Value::String("engineering".to_string()))
.where_gt("salary", Value::Integer(100000))
.where_contains("skills", "rust")
.order_by_desc("salary")
.limit(20)
.offset(0)
).await?;
// Edge queries
let relationships = client.execute_query(
client.query_edges()
.with_label("friendship")
.where_gt("strength", Value::Float(0.7))
.order_by_asc("created_at")
).await?;
# Local embedded usage
cargo run --example basic_usage
cargo run --example graph_example
# β¨ NEW: Join operation examples
cargo run --example comprehensive_joins
cargo run --example join_queries
# Network client-server mode
cargo run --example database_server # Terminal 1
cargo run --example database_client # Terminal 2
# Multi-database examples
cargo run --example two_databases
cargo run --example database_federation
# Performance benchmarks
cargo run --example benchmark_local --release
cargo run --example benchmark_network --release
cargo run --example benchmark_comparison --release
mini-database/
βββ src/
β βββ lib.rs # Main library entry
β βββ core/ # Database engine
β β βββ database.rs # Main database instance
β β βββ config.rs # Configuration management
β βββ storage/ # Storage layer
β β βββ file_reader.rs # Memory-mapped I/O
β β βββ node_store.rs # Node persistence
β β βββ edge_store.rs # Edge & adjacency lists
β β βββ index.rs # B-tree indexing
β β βββ cache.rs # LRU caching
β βββ query/ # Query processing
β β βββ builder.rs # Fluent query API
β β βββ executor.rs # Query execution
β β βββ graph_ops.rs # Graph algorithms
β βββ client/ # Client interfaces
β β βββ database_client.rs # Local client
β β βββ network_client.rs # Network client
β β βββ join.rs # β¨ Join operations
β β βββ result.rs # Query results
β βββ server/ # Network server
β β βββ server.rs # TCP server
β β βββ handler.rs # Request handling
β β βββ protocol.rs # Binary protocol
β β βββ connection_pool.rs # Connection management
β βββ types/ # Data structures
β β βββ node.rs # Node definition
β β βββ edge.rs # Edge definition
β β βββ value.rs # Property values
β βββ utils/ # Utilities
β βββ error.rs # Error handling
β βββ serde.rs # Serialization
βββ examples/ # Usage examples
β βββ basic_usage.rs # Basic operations
β βββ graph_example.rs # Graph algorithms
β βββ database_server.rs # Server startup
β βββ database_client.rs # Network client
β βββ join_queries.rs # β¨ Join examples
β βββ two_databases.rs # Multi-database
β βββ benchmark_*.rs # Performance tests
βββ Cargo.toml # Dependencies
let config = DatabaseConfig::new("./data")
.with_cache_size(256) // 256MB cache
.with_compression(true) // Enable LZ4 compression
.with_sync_writes(false) // Async writes for performance
.with_index_page_size(4096); // 4KB index pages
let server_config = ServerConfig {
host: "127.0.0.1".to_string(),
port: 5432,
max_connections: 100, // Connection pool limit
buffer_size: 8192, // 8KB network buffer
cleanup_interval: Duration::from_secs(60), // Connection cleanup
};
- β Single-process applications (desktop, mobile, embedded)
- β Ultra-low latency requirements (<10ΞΌs)
- β High-frequency operations (>50K ops/sec)
- β Offline-first applications
- β Multi-client applications (web services, APIs)
- β Microservices architecture
- β Language-agnostic access (Python, JavaScript clients)
- β Distributed deployments
- β Complex relationship queries (user orders, social networks)
- β Analytics and reporting (aggregations, grouping)
- β Multi-table operations (traditional RDBMS migration)
- β Real-time dashboards (fast aggregation queries)
// Optimize for read-heavy workloads
let config = DatabaseConfig::new("./data")
.with_cache_size(512) // Larger cache
.with_compression(false); // Skip compression for speed
// Optimize for write-heavy workloads
let config = DatabaseConfig::new("./data")
.with_cache_size(128) // Smaller cache
.with_compression(true) // Save disk space
.with_sync_writes(false); // Async writes
// Use specific queries for better performance
let nodes = client.find_nodes_by_label("person").await?; // Fast label index
let nodes = client.find_nodes_by_property("age", &Value::Integer(25)).await?; // Property lookup
// Limit results to reduce memory usage
let results = client.execute_query(
client.query_nodes()
.with_label("user")
.limit(1000) // Limit results
.offset(page * 1000) // Pagination
).await?;
// Use edge-based joins for better performance (faster than property joins)
let fast_join = client.join("user", "order")
.on_edge("places_order") // Direct edge traversal
.execute().await?;
// Apply filters early to reduce intermediate results
let optimized = client.join("user", "order")
.on_edge("places_order")
.where_condition(|row| /* filter early */) // Before sorting
.order_by("order.total", false)
.limit(100) // Limit final results
.execute().await?;
# Run all tests
cargo test
# Run with optimizations
cargo test --release
# Test specific module
cargo test storage::tests
# β¨ Test join operations
cargo test client::join::tests
# Test with logging
RUST_LOG=debug cargo test
We are building Mini Database step-by-step to become a full-featured, production-quality graph database. Here is the planned progression:
- Write-Ahead Logging (WAL) for crash recovery
- ACID Transactions with commit/rollback
- Custom Query Language parser and execution
- Basic query optimization
- Authentication and Role-Based Authorization
- Security layer integrated into network server
- Metrics collection using Prometheus support
- Monitoring HTTP endpoint for real-time stats
- (Optional) Admin dashboard with query console and metrics
- Cluster management with Raft consensus for high availability
- Data sharding and replication across nodes
- Inter-node communication and cluster network protocol
- Distributed query execution and result aggregation
- Graph algorithms library (PageRank, Community detection, etc.)
- Temporal graph support with snapshotting and versioning
- Multi-language drivers: Rust, Python, JavaScript, and more
- ORM/ODM support with trait and macros
- GraphQL API server integration for flexible querying
- Connectors for Apache Kafka and Apache Spark for streaming and analytics
- Comprehensive documentation, examples, tutorials
- CI/CD pipelines, community engagement, and open source growth
Your contributions and feedback welcome! Help us build the future of graph databases in Rust.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
git clone https://github.com/AarambhDevHub/mini-database.git
cd mini-database
cargo build
cargo test
cargo run --example basic_usage
# β¨ Test join functionality
cargo run --example comprehensive_joins
This project is licensed under the MIT License - see the LICENSE file for details.
If you find Ignitia helpful, consider supporting the project:
- Tokio - Asynchronous runtime
- Serde - Serialization framework
- LZ4 - High-speed compression
- DashMap - Concurrent hash maps
- Rust Community - Amazing ecosystem
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Made with β€οΈ and π¦ Rust β€οΈ by Aarambh Dev Hub
Mini Database - Where graph performance meets SQL familiarity