Rust Query Builder
A powerful, type-safe query builder library for Rust that leverages key-paths for SQL-like operations on in-memory collections. This library brings the expressiveness of SQL to Rust's collections with compile-time type safety.
🎉 v1.0.0 - Stable Release! Production-ready with all features tested and optimized!
🔐 Universal Lock Support! Works with
std::sync,tokio, andparking_lotlocks (189x lazy speedup) - see lock types guide
🎯 Lock-Aware Queries! SQL syntax on
HashMap<K, Arc<RwLock<V>>>with JOINs and VIEWs - see guide
🎯 v0.5.0 - Extension Trait & Derive Macros! Call
.query()and.lazy_query()directly on containers - see extension guide
⚡ v0.5.0 - Build Optimized! Split into 3 crates - 65% faster builds, 6KB umbrella crate - see build guide
🎨 v0.4.0 - Helper Macros! 12 macros to reduce boilerplate - save 20-45 characters per operation - see macro guide
📦 v0.3.0 - Container Support! Query Vec, HashMap, HashSet, BTreeMap, VecDeque, and more - see container guide
⚡ v0.3.0 - Lazy Evaluation! New
LazyQuerywith deferred execution and early termination - see lazy guide
🚀 v0.2.0 - Performance Optimized! Most operations now work without
Clone- see optimization guide
🔒 Memory Safe! Using
'staticbounds causes 0 memory leaks - verified with tests ✅
💡 New! See how SQL queries map to Rust Query Builder in our SQL Comparison Example - demonstrates 15 SQL patterns side-by-side!
✅ Verified! All query results are exact SQL equivalents - see verification tests (17/17 tests passing)
Features
- 🔒 Type-safe queries: Compile-time type checking using key-paths
- 📊 SQL-like operations: WHERE, SELECT, ORDER BY, GROUP BY, JOIN
- 🧮 Rich aggregations: COUNT, SUM, AVG, MIN, MAX
- 📄 Pagination: LIMIT and SKIP operations
- 🔗 Join operations: INNER JOIN, LEFT JOIN, RIGHT JOIN, CROSS JOIN
- ⏰ DateTime operations: Filter by dates, times, weekdays, business hours - details
- ⚡ Zero-cost abstractions: Leverages Rust's zero-cost abstractions
- 🎯 Fluent API: Chain operations naturally
- 🚀 Clone-free operations: Most operations work without
Clone- details - ⚡ Lazy evaluation: Deferred execution with early termination - up to 1000x faster - details
- 📦 Multiple containers: Vec, HashMap, HashSet, BTreeMap, VecDeque, arrays, and more - details
- 🎨 Helper macros: 12 macros to reduce boilerplate - 30% less code - details
- 🎯 Extension trait: Call
.query()and.lazy_query()directly on containers - details - 📝 Derive macros: Auto-generate query helpers with
#[derive(QueryBuilder)]- details - 🔒 Lock-aware querying: Query
Arc<RwLock<T>>andArc<Mutex<T>>without copying - 5x faster! - 🚀 Universal lock support: Works with
std::sync,tokio::sync, andparking_lotlocks - ⚡ Async support: Native tokio RwLock support for async applications
- 🔥 High-performance locks: parking_lot support (10-30% faster, no poisoning)
Installation
Option 1: Umbrella Crate (Recommended for Applications)
Add this to your Cargo.toml:
[]
= "1.0.1"
= "0.5.0"
# Optional: Enable datetime operations with chrono
= { = "1.0.1", = ["datetime"] }
= "0.4"
# Optional: For async/tokio support
= { = "1.35", = ["sync"] }
# Optional: For high-performance parking_lot locks
= "0.12"
Option 2: Individual Crates (Recommended for Libraries/POCs)
For faster builds (65% faster) and minimal dependencies:
[]
= "1.0.1"
= "1.0.1" # Optional, only if using derive macros
= "0.5.0"
# Optional: Enable datetime operations with chrono
= { = "1.0.1", = ["datetime"] }
= "0.4"
# Optional: For async/tokio support
= { = "1.35", = ["sync"] }
# Optional: For high-performance parking_lot locks
= "0.12"
⚠️ Important: When using individual crates, import from the correct locations:
use ; // ← QueryExt is here!
use QueryBuilder; // ← Derive macros here
See the Individual Crates Guide for complete details.
Quick Start
Extension Trait (Easiest)
use QueryExt; // Extension trait
use Keypaths;
// Note: Clone not required for most operations!
let products = vec!;
// Call .query() directly on Vec!
let query = products.query.where_;
let expensive = query.all;
// Or use lazy queries for better performance
let cheap: = products
.lazy_query
.where_
.collect;
Standard Query (Eager)
use Query;
use Keypaths;
Lazy Query (Deferred Execution - NEW in v0.3.0!)
use LazyQuery;
use Keypaths;
Core Operations
Filtering with where_
Filter collections using type-safe key-paths:
let query = new
.where_;
let electronics = query.all;
// Multiple conditions
let query2 = new
.where_
.where_
.where_;
let premium_electronics = query2.all;
Selecting Fields with select
Project specific fields from your data:
// Get all product names
let names: = new
.select;
// Get prices of electronics
let prices: = new
.where_
.select;
Ordering Results
Sort results by any field:
// Sort by price (ascending)
let by_price = new
.order_by_float;
// Sort by name (descending)
let by_name_desc = new
.order_by_desc;
// Sort with filtering
let sorted_electronics = new
.where_
.order_by_float;
Aggregations
Compute statistics over your data:
let electronics = new
.where_;
// Count
let count = electronics.count;
// Sum
let total_value: f64 = electronics.sum;
// Average
let avg_price = electronics.avg.unwrap_or;
// Min and Max
let cheapest = electronics.min_float;
let most_expensive = electronics.max_float;
Grouping with group_by
Group data by field values:
use HashMap;
// Group products by category
let by_category: = new
.group_by;
// Calculate statistics per group
for in &by_category
Pagination
Limit and skip results for pagination:
// Get first 10 products
let query = new;
let first_page = query.limit;
// Get second page (skip 10, take 10)
let query = new;
let second_page = query.skip.limit;
// Get first matching item
let query = new
.where_;
let first = query.first;
Join Operations
Combine multiple collections with type-safe joins:
use JoinQuery;
let users = vec!;
let orders = vec!;
// Inner join: users with their orders
let user_orders = new
.inner_join;
// Left join: all users, with or without orders
let all_users_orders = new
.left_join;
// Join with filter: only high-value orders
let high_value = new
.inner_join_where;
Available Join Types
- Inner Join: Returns only matching pairs
- Left Join: Returns all left items with optional right matches
- Right Join: Returns all right items with optional left matches
- Cross Join: Returns Cartesian product of both collections
- Join Where: Inner join with additional predicates
Lock-Aware Querying (NEW in v0.8.0!)
Query Arc<RwLock<T>> and Arc<Mutex<T>> with full SQL syntax - NO copying required!
use ;
use ;
use HashMap;
use Keypaths;
let products: = /* ... */;
// Full SQL-like syntax on locked data!
let expensive = products
.lock_query
.where_
.where_
.order_by_float_desc
.limit;
// GROUP BY with aggregations
let by_category = products
.lock_query
.group_by;
// Aggregations
let stats = products.lock_query;
let total = stats.sum;
let avg = stats.avg;
let count = stats.count;
// Lazy with early termination
let first_match: = products
.lock_lazy_query
.where_
.take_lazy
.collect;
Performance: 5.25x faster than copy-based approach!
Available Operations on Locked Data
- WHERE: Filter with key-path predicates
- SELECT: Project specific fields
- ORDER BY: Sort by any field (ASC/DESC)
- GROUP BY: Group by field values
- Aggregations: COUNT, SUM, AVG, MIN, MAX
- LIMIT: Paginate results
- EXISTS: Check existence
- FIRST: Find first match
- Lazy: Early termination with
lock_lazy_query() - JOINS: INNER, LEFT, RIGHT, CROSS joins on locked data
- VIEWS: Materialized views with caching and refresh
Universal Lock Support
Standard Library (std::sync)
Works out-of-the-box with Arc<RwLock<T>> and Arc<Mutex<T>>:
use ;
use HashMap;
use LockQueryable;
let products: = /* ... */;
let expensive = products
.lock_query
.where_
.all;
Tokio Support (Async)
Native support for tokio::sync::RwLock:
use RwLock;
use Arc;
use ;
// Create extension wrapper
use TokioRwLockWrapper;
let mut products: = new;
products.insert;
// Query asynchronously
async
See the tokio_rwlock_support example for complete async examples.
parking_lot Support (High Performance)
Support for parking_lot::RwLock and parking_lot::Mutex with better performance:
use RwLock;
use Arc;
use HashMap;
// Create wrapper for parking_lot locks
;
// Implement LockValue trait
use LockValue;
// Create extension trait for direct method calls
// Now use it!
let products: = /* ... */;
let expensive = products
.lock_query // Direct method call!
.where_
.all;
parking_lot Advantages:
- 🚀 10-30% faster lock acquisition than std::sync
- 🔥 No poisoning - simpler API, no Result types
- 💾 8x smaller memory footprint (8 bytes vs 64 bytes)
- ⚖️ Fair unlocking - prevents writer starvation
- ⚡ Better cache locality - improved performance
See the parking_lot_support example for complete implementation.
DateTime Operations
Query by dates, times, weekdays, and business hours with optional chrono support:
use Query;
use ;
use Keypaths;
let events = vec!;
let now = now;
// Events scheduled in the next 7 days
let upcoming = new
.where_between;
// Weekend events
let weekend = new
.where_weekend;
// Work events during business hours on weekdays
let work_hours = new
.where_
.where_weekday
.where_business_hours;
// Events in December 2024
let december = new
.where_year
.where_month;
Available DateTime Operations
- Date Comparisons:
where_after,where_before,where_between - Date Components:
where_year,where_month,where_day - Day Type:
where_weekend,where_weekday,where_today - Time Filters:
where_business_hours - SystemTime Support: Basic operations without feature flags
See the DateTime Guide for complete documentation and examples.
Advanced Examples
Complex Multi-Stage Query
// Find top 5 expensive electronics in stock, ordered by rating
let top_electronics = new
.where_
.where_
.where_
.order_by_float_desc;
for product in top_electronics.iter.take
Three-Way Join
// First join: Orders with Users
let orders_users = new
.inner_join;
// Second join: Add Products
let mut complete_orders = Vecnew;
for in orders_users
Category Sales Analysis
// Join orders with products, then aggregate by category
let order_products = new
.inner_join;
let mut category_sales: = new;
for in order_products
API Reference
Query Methods
Basic Operations:
new(data: &[T])- Create a new querywhere_(path, predicate)- Filter by predicateall()- Get all matching itemsfirst()- Get first matching itemcount()- Count matching itemslimit(n)- Limit resultsskip(n)- Skip results for paginationexists()- Check if any match
Ordering:
order_by(path)- Sort ascendingorder_by_desc(path)- Sort descendingorder_by_float(path)- Sort f64 ascendingorder_by_float_desc(path)- Sort f64 descending
Projection & Grouping:
select(path)- Project fieldgroup_by(path)- Group by field
Aggregations:
sum(path)- Sum numeric fieldavg(path)- Average of f64 fieldmin(path)/max(path)- Min/max of Ord fieldmin_float(path)/max_float(path)- Min/max of f64 field
DateTime Operations (with datetime feature):
where_after(path, time)- Filter after datetimewhere_before(path, time)- Filter before datetimewhere_between(path, start, end)- Filter within rangewhere_today(path, now)- Filter for todaywhere_year(path, year)- Filter by yearwhere_month(path, month)- Filter by month (1-12)where_day(path, day)- Filter by day (1-31)where_weekend(path)- Filter for weekendswhere_weekday(path)- Filter for weekdayswhere_business_hours(path)- Filter for business hours (9 AM - 5 PM)
DateTime Operations (SystemTime, always available):
where_after_systemtime(path, time)- Filter after SystemTimewhere_before_systemtime(path, time)- Filter before SystemTimewhere_between_systemtime(path, start, end)- Filter within range
JoinQuery Methods
new(left, right)- Create a new join queryinner_join(left_key, right_key, mapper)- Inner joinleft_join(left_key, right_key, mapper)- Left joinright_join(left_key, right_key, mapper)- Right joininner_join_where(left_key, right_key, predicate, mapper)- Filtered joincross_join(mapper)- Cartesian product
Running Examples
# Advanced query builder example
# Join operations example
# DateTime operations - filter by dates, times, weekdays (v0.7.0+, requires datetime feature)
# Lazy DateTime operations - efficient datetime queries with early termination (v0.7.0+)
# DateTime helper functions - all datetime helpers with SQL equivalents (v0.7.0+)
# Lazy datetime helpers - all helpers with lazy evaluation and performance benchmarks (v0.7.0+)
# SQL comparison - see how SQL queries map to Rust Query Builder
# SQL verification - verify exact SQL equivalence (17 tests)
# Documentation examples - verify all doc examples compile and run (10 tests)
# Clone-free operations - demonstrates performance optimization (v0.2.0+)
# Memory safety verification - proves 'static doesn't cause memory leaks
# Lazy evaluation - demonstrates deferred execution and early termination
# Container support - demonstrates querying various container types
# Custom Queryable - implement Queryable for custom containers (7 examples)
# Arc<RwLock<T>> HashMap - thread-safe shared data with all 17 lazy operations
# Lock-aware queries - query Arc<RwLock<T>> WITHOUT copying (v0.8.0+, 5x faster!)
# SQL-like lock queries - full SQL syntax on locked HashMaps (v0.8.0+)
# Advanced lock SQL - joins, views, lazy queries on locked data (v0.8.0+)
# Macro helpers - reduce boilerplate with 12 helper macros (30% less code)
# Extension trait & derive macros - call .query() directly on containers (v0.5.0+)
# Individual crates usage - demonstrates using core + derive separately (v0.6.0+)
# Tokio RwLock support - async lock-aware queries (v0.9.0+)
# parking_lot support - high-performance locks with queries (v1.0.0+)
Example: SQL Comparison
The sql_comparison example demonstrates how traditional SQL queries (like those in HSQLDB) translate to Rust Query Builder:
// SQL: SELECT * FROM employees WHERE department = 'Engineering';
let engineering = new
.where_
.all;
// SQL: SELECT AVG(salary) FROM employees WHERE age < 30;
let avg_salary = new
.where_
.avg;
// SQL: SELECT * FROM employees ORDER BY salary DESC LIMIT 5;
let top_5 = new
.order_by_float_desc
.into_iter
.take
.;
The example demonstrates 15 different SQL patterns including SELECT, WHERE, JOIN, GROUP BY, ORDER BY, aggregations, and subqueries.
Performance
The query builder uses:
- O(n) filtering operations
- O(n log n) sorting operations
- O(n + m) hash-based joins
- Zero-cost abstractions - compiled down to efficient iterators
- Clone-free by default - most operations work with references (v0.2.0+)
Performance Characteristics
| Operation | Complexity | Memory | Clone Required? |
|---|---|---|---|
where_ / all |
O(n) | Zero extra | ❌ No |
count |
O(n) | Zero extra | ❌ No |
select |
O(n) | Only field copies | ❌ No |
sum / avg |
O(n) | Zero extra | ❌ No |
limit / skip |
O(n) | Zero extra | ❌ No |
order_by* |
O(n log n) | Clones all items | ✅ Yes |
group_by |
O(n) | Clones all items | ✅ Yes |
| Joins | O(n + m) | Zero extra | ❌ No |
Example: Filtering 10,000 employees (1KB each)
- v0.1.0: ~5ms (cloned 10MB)
- v0.2.0: ~0.1ms (zero copy) - 50x faster!
Key-Paths
This library leverages the key-paths crate to provide type-safe field access. The Keypaths derive macro automatically generates accessor methods for your structs:
// Generated methods:
// - Product::id_r() -> KeyPaths<Product, u32>
// - Product::name_r() -> KeyPaths<Product, String>
// - Product::price_r() -> KeyPaths<Product, f64>
Lock Type Comparison
| Feature | std::sync::RwLock | tokio::sync::RwLock | parking_lot::RwLock |
|---|---|---|---|
| Lock Acquisition | Baseline | Async | 10-30% faster |
| Memory Footprint | 64 bytes | 64 bytes | 8 bytes (8x smaller) |
| Poisoning | Yes (Result type) | No | No |
| Fair Unlocking | No | No | Yes |
| Async Support | ❌ | ✅ | ❌ |
| Use Case | General sync | Async/await | High-perf sync |
| Setup Required | None (built-in) | Extension trait | Newtype wrapper |
When to Use Each Lock Type
std::sync::RwLock / Mutex
- ✅ Default choice for most applications
- ✅ No additional dependencies
- ✅ Works out-of-the-box with our library
- ❌ Poisoning adds complexity
- ❌ Larger memory footprint
tokio::sync::RwLock
- ✅ Perfect for async applications
- ✅ Native tokio integration
- ✅ No blocking in async contexts
- ❌ Requires tokio runtime
- ❌ Only for async code
parking_lot::RwLock / Mutex
- ✅ Best raw performance (10-30% faster)
- ✅ Smallest memory footprint
- ✅ No poisoning complexity
- ✅ Fair unlocking prevents starvation
- ❌ Requires wrapper implementation (3 steps)
- ❌ Additional dependency
Migration Guide
Upgrading to v1.0.0
What's New:
- ✅ Stable API - no breaking changes planned
- ✅ Universal lock support (std::sync, tokio, parking_lot)
- ✅ Production-ready with comprehensive testing
- ✅ All features from v0.9.0 are fully stable
Breaking Changes: None! v1.0.0 is fully backward compatible with v0.9.0.
Update your Cargo.toml:
# Old (v0.7.0-0.9.0)
= "0.9.0"
# New (v1.0.0)
= "1.0.1"
All your existing code will work without modification!
From v0.8.0 or Earlier
If upgrading from v0.8.0 or earlier, you'll gain:
-
Tokio Support - Add async lock-aware queries:
use TokioRwLockWrapper; // See examples/tokio_rwlock_support.rs -
parking_lot Support - High-performance locks:
// See examples/parking_lot_support.rs for implementation -
Better Performance - Lazy queries up to 189x faster
-
More Examples - Comprehensive guides and patterns
Version History
- v1.0.0 (2025) - Stable release, universal lock support
- v0.9.0 (2024) - Tokio and parking_lot lock extensions
- v0.8.0 (2024) - Lock-aware queries with JOINs and VIEWs
- v0.7.0 (2024) - DateTime operations with chrono
- v0.6.0 (2024) - Individual crates for faster builds
- v0.5.0 (2024) - Extension traits and derive macros
- v0.4.0 (2024) - Helper macros (12 macros)
- v0.3.0 (2024) - Container support and lazy evaluation
- v0.2.0 (2024) - Clone-free operations
- v0.1.0 (2024) - Initial release
License
This project is licensed under either of:
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Acknowledgments
Built with rust-key-paths for type-safe field access.