JoinQuery

Struct JoinQuery 

Source
pub struct JoinQuery<'a, L, R>
where L: 'static, R: 'static,
{ /* private fields */ }
Expand description

A query builder for joining two collections.

Supports inner joins, left joins, and filtered joins using key-paths for type-safe join conditions.

§Type Parameters

  • 'a - The lifetime of the data being joined
  • L - The type of items in the left collection
  • R - The type of items in the right collection

§Example

let user_orders = JoinQuery::new(&users, &orders)
    .inner_join(
        User::id_r(),
        Order::user_id_r(),
        |user, order| (user.name.clone(), order.total)
    );

Implementations§

Source§

impl<'a, L, R> JoinQuery<'a, L, R>
where L: 'static, R: 'static,

Source

pub fn new(left: &'a [L], right: &'a [R]) -> JoinQuery<'a, L, R>

Creates a new join query from two collections.

Note: No Clone required on L or R. The mapper function handles any cloning needed for the result type.

§Arguments
  • left - The left collection to join
  • right - The right collection to join
§Example
let join = JoinQuery::new(&users, &orders);
Examples found in repository?
examples/without_clone.rs (line 148)
28fn main() {
29    println!("╔════════════════════════════════════════════════════════════╗");
30    println!("║  Query Builder Without Clone - Performance Optimization   ║");
31    println!("╚════════════════════════════════════════════════════════════╝\n");
32
33    let employees = vec![
34        Employee {
35            id: 1,
36            name: "Alice".to_string(),
37            email: "alice@example.com".to_string(),
38            department: "Engineering".to_string(),
39            salary: 95000.0,
40            large_data: vec![0; 1000], // Large data - expensive to clone!
41        },
42        Employee {
43            id: 2,
44            name: "Bob".to_string(),
45            email: "bob@example.com".to_string(),
46            department: "Engineering".to_string(),
47            salary: 87000.0,
48            large_data: vec![0; 1000],
49        },
50        Employee {
51            id: 3,
52            name: "Carol".to_string(),
53            email: "carol@example.com".to_string(),
54            department: "Sales".to_string(),
55            salary: 75000.0,
56            large_data: vec![0; 1000],
57        },
58    ];
59
60    let departments = vec![
61        Department {
62            id: 1,
63            name: "Engineering".to_string(),
64            budget: 1000000.0,
65        },
66        Department {
67            id: 2,
68            name: "Sales".to_string(),
69            budget: 500000.0,
70        },
71    ];
72
73    println!("✅ Operations that DON'T require Clone:\n");
74
75    // 1. WHERE filtering - returns Vec<&T>
76    println!("1. WHERE filtering (returns references)");
77    let query = Query::new(&employees)
78        .where_(Employee::department_r(), |dept| dept == "Engineering");
79    let engineering = query.all();
80    println!("   Found {} engineering employees", engineering.len());
81    for emp in &engineering {
82        println!("     - {}: ${:.0}", emp.name, emp.salary);
83    }
84
85    // 2. COUNT - no cloning needed
86    println!("\n2. COUNT aggregation");
87    let count = Query::new(&employees)
88        .where_(Employee::salary_r(), |&sal| sal > 80000.0)
89        .count();
90    println!("   {} employees earn over $80k", count);
91
92    // 3. SELECT - only clones the selected field
93    println!("\n3. SELECT (only selected fields are cloned)");
94    let names: Vec<String> = Query::new(&employees)
95        .select(Employee::name_r());
96    println!("   Employee names: {:?}", names);
97
98    // 4. FIRST - returns Option<&T>
99    println!("\n4. FIRST (returns reference)");
100    let query = Query::new(&employees)
101        .where_(Employee::salary_r(), |&sal| sal > 90000.0);
102    if let Some(emp) = query.first() {
103        println!("   First high earner: {} (${:.0})", emp.name, emp.salary);
104    }
105
106    // 5. SUM/AVG aggregations - no cloning
107    println!("\n5. Aggregations (SUM/AVG)");
108    let eng_query = Query::new(&employees)
109        .where_(Employee::department_r(), |dept| dept == "Engineering");
110    let total = eng_query.sum(Employee::salary_r());
111    let avg = eng_query.avg(Employee::salary_r()).unwrap_or(0.0);
112    println!("   Engineering total: ${:.0}", total);
113    println!("   Engineering average: ${:.0}", avg);
114
115    // 6. MIN/MAX - no cloning
116    println!("\n6. MIN/MAX");
117    let min = Query::new(&employees).min_float(Employee::salary_r());
118    let max = Query::new(&employees).max_float(Employee::salary_r());
119    println!("   Salary range: ${:.0} - ${:.0}", min.unwrap(), max.unwrap());
120
121    // 7. LIMIT - returns Vec<&T>
122    println!("\n7. LIMIT (returns references)");
123    let query = Query::new(&employees);
124    let first_two = query.limit(2);
125    println!("   First 2 employees:");
126    for emp in &first_two {
127        println!("     - {}", emp.name);
128    }
129
130    // 8. SKIP/Pagination - returns Vec<&T>
131    println!("\n8. SKIP/Pagination (returns references)");
132    let query = Query::new(&employees);
133    let page_2 = query.skip(2).limit(1);
134    println!("   Page 2:");
135    for emp in &page_2 {
136        println!("     - {}", emp.name);
137    }
138
139    // 9. EXISTS - just checks
140    println!("\n9. EXISTS check");
141    let has_sales = Query::new(&employees)
142        .where_(Employee::department_r(), |dept| dept == "Sales")
143        .exists();
144    println!("   Has Sales employees: {}", has_sales);
145
146    // 10. JOIN - no Clone required on L or R!
147    println!("\n10. JOIN operations (no Clone required!)");
148    let results = JoinQuery::new(&employees, &departments)
149        .inner_join(
150            Employee::department_r(),
151            Department::name_r(),
152            |emp, dept| {
153                // Mapper only clones what it needs for the result
154                (emp.name.clone(), dept.budget)
155            },
156        );
157    println!("   Employee-Department pairs:");
158    for (name, budget) in &results {
159        println!("     - {} works in dept with ${:.0} budget", name, budget);
160    }
161
162    println!("\n╔════════════════════════════════════════════════════════════╗");
163    println!("║  Operations that REQUIRE Clone (only when needed)         ║");
164    println!("╚════════════════════════════════════════════════════════════╝\n");
165
166    println!("⚠️  The following operations require Clone because they return owned Vec<T>:");
167    println!("   - order_by() / order_by_desc()");
168    println!("   - order_by_float() / order_by_float_desc()");
169    println!("   - group_by()");
170    println!("\n   To use these, add #[derive(Clone)] to your struct:");
171    println!("   ```rust");
172    println!("   #[derive(Clone, Keypaths)]  // Add Clone here");
173    println!("   struct Employee {{ ... }}");
174    println!("   ```");
175
176    println!("\n╔════════════════════════════════════════════════════════════╗");
177    println!("║  Performance Benefits                                      ║");
178    println!("╚════════════════════════════════════════════════════════════╝\n");
179
180    println!("✅ Zero cloning for most operations");
181    println!("✅ Work with large structs efficiently");
182    println!("✅ No unnecessary memory allocations");
183    println!("✅ Only clone when you actually need owned data");
184    println!("✅ Pay for what you use");
185
186    println!("\n✓ Example complete! Most operations work without Clone.\n");
187}
More examples
Hide additional examples
examples/doc_examples.rs (line 151)
30fn main() {
31    println!("Testing documentation examples...\n");
32
33    // Example from README - Quick Start
34    println!("Test 1: README Quick Start Example");
35    {
36        let products = vec![
37            Product { id: 1, name: "Laptop".to_string(), price: 999.99, category: "Electronics".to_string(), stock: 15, rating: 4.5 },
38            Product { id: 2, name: "Mouse".to_string(), price: 29.99, category: "Electronics".to_string(), stock: 50, rating: 4.0 },
39            Product { id: 3, name: "Desk".to_string(), price: 299.99, category: "Furniture".to_string(), stock: 10, rating: 4.8 },
40        ];
41
42        let affordable_query = Query::new(&products)
43            .where_(Product::category_r(), |cat| cat == "Electronics")
44            .where_(Product::price_r(), |&price| price < 100.0);
45        let affordable_electronics = affordable_query.all();
46
47        println!("  Found {} affordable electronics ✅", affordable_electronics.len());
48    }
49
50    // Example from README - Filtering
51    println!("\nTest 2: Filtering Example");
52    {
53        let products = vec![
54            Product { id: 1, name: "Laptop".to_string(), price: 999.99, category: "Electronics".to_string(), stock: 15, rating: 4.5 },
55        ];
56
57        let electronics_query = Query::new(&products)
58            .where_(Product::category_r(), |cat| cat == "Electronics");
59        let electronics = electronics_query.all();
60
61        println!("  Found {} electronics ✅", electronics.len());
62    }
63
64    // Example from README - Selecting
65    println!("\nTest 3: Selecting Fields Example");
66    {
67        let products = vec![
68            Product { id: 1, name: "Laptop".to_string(), price: 999.99, category: "Electronics".to_string(), stock: 15, rating: 4.5 },
69            Product { id: 2, name: "Mouse".to_string(), price: 29.99, category: "Electronics".to_string(), stock: 50, rating: 4.0 },
70        ];
71
72        let names: Vec<String> = Query::new(&products)
73            .select(Product::name_r());
74
75        println!("  Selected {} names ✅", names.len());
76    }
77
78    // Example from README - Ordering
79    println!("\nTest 4: Ordering Example");
80    {
81        let products = vec![
82            Product { id: 1, name: "Laptop".to_string(), price: 999.99, category: "Electronics".to_string(), stock: 15, rating: 4.5 },
83            Product { id: 2, name: "Mouse".to_string(), price: 29.99, category: "Electronics".to_string(), stock: 50, rating: 4.0 },
84        ];
85
86        let by_price = Query::new(&products).order_by_float(Product::price_r());
87        println!("  Ordered {} products ✅", by_price.len());
88    }
89
90    // Example from README - Aggregations
91    println!("\nTest 5: Aggregations Example");
92    {
93        let products = vec![
94            Product { id: 1, name: "Laptop".to_string(), price: 999.99, category: "Electronics".to_string(), stock: 15, rating: 4.5 },
95            Product { id: 2, name: "Mouse".to_string(), price: 29.99, category: "Electronics".to_string(), stock: 50, rating: 4.0 },
96        ];
97
98        let electronics_query = Query::new(&products)
99            .where_(Product::category_r(), |cat| cat == "Electronics");
100
101        let count = electronics_query.count();
102        let total_value: f64 = electronics_query.sum(Product::price_r());
103        let avg_price = electronics_query.avg(Product::price_r()).unwrap_or(0.0);
104
105        println!("  Count: {}, Total: ${:.2}, Avg: ${:.2} ✅", count, total_value, avg_price);
106    }
107
108    // Example from README - Grouping
109    println!("\nTest 6: Grouping Example");
110    {
111        let products = vec![
112            Product { id: 1, name: "Laptop".to_string(), price: 999.99, category: "Electronics".to_string(), stock: 15, rating: 4.5 },
113            Product { id: 2, name: "Mouse".to_string(), price: 29.99, category: "Electronics".to_string(), stock: 50, rating: 4.0 },
114            Product { id: 3, name: "Desk".to_string(), price: 299.99, category: "Furniture".to_string(), stock: 10, rating: 4.8 },
115        ];
116
117        let by_category = Query::new(&products).group_by(Product::category_r());
118        println!("  Grouped into {} categories ✅", by_category.len());
119    }
120
121    // Example from README - Pagination
122    println!("\nTest 7: Pagination Example");
123    {
124        let products = vec![
125            Product { id: 1, name: "P1".to_string(), price: 10.0, category: "A".to_string(), stock: 1, rating: 4.0 },
126            Product { id: 2, name: "P2".to_string(), price: 20.0, category: "A".to_string(), stock: 1, rating: 4.0 },
127            Product { id: 3, name: "P3".to_string(), price: 30.0, category: "A".to_string(), stock: 1, rating: 4.0 },
128        ];
129
130        let query = Query::new(&products);
131        let first_10 = query.limit(10);
132        let page_1 = query.skip(0).limit(10);
133
134        println!("  Limited to {} products ✅", first_10.len());
135        println!("  Page 1 has {} products ✅", page_1.len());
136    }
137
138    // Example from README - Join
139    println!("\nTest 8: Join Example");
140    {
141        let users = vec![
142            User { id: 1, name: "Alice".to_string() },
143            User { id: 2, name: "Bob".to_string() },
144        ];
145
146        let orders = vec![
147            Order { id: 101, user_id: 1, total: 99.99 },
148            Order { id: 102, user_id: 1, total: 149.99 },
149        ];
150
151        let user_orders = JoinQuery::new(&users, &orders).inner_join(
152            User::id_r(),
153            Order::user_id_r(),
154            |user, order| (user.name.clone(), order.total),
155        );
156
157        println!("  Joined {} user-order pairs ✅", user_orders.len());
158    }
159
160    // Example from SQL_COMPARISON - SELECT with WHERE
161    println!("\nTest 9: SQL Comparison - SELECT with WHERE");
162    {
163        #[derive(Clone, Keypaths)]
164        struct Employee {
165            department: String,
166        }
167
168        let employees = vec![
169            Employee { department: "Engineering".to_string() },
170            Employee { department: "Sales".to_string() },
171        ];
172
173        let engineering_query = Query::new(&employees)
174            .where_(Employee::department_r(), |dept| dept == "Engineering");
175        let engineering = engineering_query.all();
176
177        println!("  Found {} engineering employees ✅", engineering.len());
178    }
179
180    // Example from USAGE.md - Complex Filtering
181    println!("\nTest 10: USAGE - Complex Filtering");
182    {
183        let products = vec![
184            Product { id: 1, name: "Laptop".to_string(), price: 999.99, category: "Electronics".to_string(), stock: 15, rating: 4.5 },
185        ];
186
187        let results_query = Query::new(&products)
188            .where_(Product::category_r(), |cat| cat == "Electronics")
189            .where_(Product::price_r(), |&price| price >= 100.0 && price <= 500.0)
190            .where_(Product::stock_r(), |&stock| stock > 10);
191        let results = results_query.order_by_float(Product::price_r());
192
193        println!("  Filtered {} products ✅", results.len());
194    }
195
196    println!("\n✅ All documentation examples compile and run successfully!");
197}
examples/join_query_builder.rs (line 205)
193fn main() {
194    println!("=== Join Query Builder Demo ===\n");
195
196    let (users, orders, products) = create_sample_data();
197
198    println!("Database:");
199    println!("  Users: {}", users.len());
200    println!("  Orders: {}", orders.len());
201    println!("  Products: {}\n", products.len());
202
203    // Join 1: Inner join Users and Orders
204    println!("--- Join 1: Users with Their Orders ---");
205    let user_orders = JoinQuery::new(&users, &orders).inner_join(
206        User::id_r(),
207        Order::user_id_r(),
208        |user, order| UserOrder {
209            user_name: user.name.clone(),
210            user_email: user.email.clone(),
211            order_id: order.id,
212            quantity: order.quantity,
213            total: order.total,
214        },
215    );
216
217    for uo in &user_orders {
218        println!(
219            "  • {} - Order #{} - {} items - ${:.2}",
220            uo.user_name, uo.order_id, uo.quantity, uo.total
221        );
222    }
223    println!("Total: {} user-order pairs", user_orders.len());
224
225    // Join 2: Three-way join (Orders -> Users, Orders -> Products)
226    println!("\n--- Join 2: Complete Order Details (3-Way Join) ---");
227    
228    // First join: Orders with Users
229    let orders_with_users = JoinQuery::new(&orders, &users).inner_join(
230        Order::user_id_r(),
231        User::id_r(),
232        |order, user| (order.clone(), user.clone()),
233    );
234
235    // Second join: (Orders+Users) with Products
236    let mut order_details = Vec::new();
237    for (order, user) in &orders_with_users {
238        for product in &products {
239            if order.product_id == product.id {
240                order_details.push(OrderDetail {
241                    order_id: order.id,
242                    user_name: user.name.clone(),
243                    product_name: product.name.clone(),
244                    quantity: order.quantity,
245                    price: product.price,
246                    total: order.total,
247                });
248            }
249        }
250    }
251
252    for od in &order_details {
253        println!(
254            "  • Order #{}: {} bought {} x {} @ ${:.2} = ${:.2}",
255            od.order_id, od.user_name, od.quantity, od.product_name, od.price, od.total
256        );
257    }
258
259    // Join 3: Left join to show all users (including those without orders)
260    println!("\n--- Join 3: All Users with Order Count (Left Join) ---");
261    
262    // Use left_join to get all users with their orders (or None)
263    let user_order_pairs = JoinQuery::new(&users, &orders).left_join(
264        User::id_r(),
265        Order::user_id_r(),
266        |user, order| (user.clone(), order.map(|o| o.clone())),
267    );
268
269    // Group by user to count orders
270    let mut user_stats: HashMap<u32, (String, String, usize, f64)> = HashMap::new();
271    for (user, order) in &user_order_pairs {
272        let entry = user_stats
273            .entry(user.id)
274            .or_insert_with(|| (user.name.clone(), user.city.clone(), 0, 0.0));
275        if let Some(order) = order {
276            entry.2 += 1; // order count
277            entry.3 += order.total; // total spent
278        }
279    }
280
281    let mut user_order_stats: Vec<_> = user_stats
282        .into_iter()
283        .map(|(_, (name, city, count, total))| UserOrderCount {
284            user_name: name,
285            user_city: city,
286            order_count: count,
287            total_spent: total,
288        })
289        .collect();
290
291    user_order_stats.sort_by(|a, b| a.user_name.cmp(&b.user_name));
292
293    for stat in &user_order_stats {
294        if stat.order_count > 0 {
295            println!(
296                "  • {} ({}) - {} orders - ${:.2} total",
297                stat.user_name, stat.user_city, stat.order_count, stat.total_spent
298            );
299        } else {
300            println!("  • {} ({}) - No orders yet", stat.user_name, stat.user_city);
301        }
302    }
303
304    // Join 4: Aggregated join - Category sales analysis
305    println!("\n--- Join 4: Sales by Product Category ---");
306
307    // Join orders with products to get category information
308    let order_products = JoinQuery::new(&orders, &products).inner_join(
309        Order::product_id_r(),
310        Product::id_r(),
311        |order, product| (order.clone(), product.clone()),
312    );
313
314    // Aggregate by category
315    let mut category_stats: HashMap<String, (Vec<u32>, f64, std::collections::HashSet<u32>)> =
316        HashMap::new();
317
318    for (order, product) in &order_products {
319        let entry = category_stats
320            .entry(product.category.clone())
321            .or_insert_with(|| (Vec::new(), 0.0, std::collections::HashSet::new()));
322        entry.0.push(order.id);
323        entry.1 += order.total;
324        entry.2.insert(order.user_id);
325    }
326
327    let mut category_sales: Vec<CategorySales> = category_stats
328        .into_iter()
329        .map(|(category, (orders, revenue, customers))| CategorySales {
330            category,
331            total_orders: orders.len(),
332            total_revenue: revenue,
333            unique_customers: customers.len(),
334        })
335        .collect();
336
337    category_sales.sort_by(|a, b| {
338        b.total_revenue
339            .partial_cmp(&a.total_revenue)
340            .unwrap_or(std::cmp::Ordering::Equal)
341    });
342
343    for cs in &category_sales {
344        println!(
345            "  • {}: {} orders - ${:.2} revenue - {} customers",
346            cs.category, cs.total_orders, cs.total_revenue, cs.unique_customers
347        );
348    }
349
350    // Join 5: Filtered join - High value orders
351    println!("\n--- Join 5: High Value Orders (>$100) with User Details ---");
352    let high_value_orders = JoinQuery::new(&orders, &users).inner_join_where(
353        Order::user_id_r(),
354        User::id_r(),
355        |order, _user| order.total > 100.0,
356        |order, user| (user.name.clone(), order.id, order.total),
357    );
358
359    for (name, order_id, total) in &high_value_orders {
360        println!("  • {} - Order #{} - ${:.2}", name, order_id, total);
361    }
362
363    // Join 6: Users in same city analysis
364    println!("\n--- Join 6: Users from Same City ---");
365    let user_pairs = JoinQuery::new(&users, &users).inner_join_where(
366        User::city_r(),
367        User::city_r(),
368        |u1, u2| u1.id < u2.id, // Avoid duplicates and self-pairs
369        |u1, u2| (u1.name.clone(), u2.name.clone(), u1.city.clone()),
370    );
371
372    for (name1, name2, city) in &user_pairs {
373        println!("  • {} and {} both live in {}", name1, name2, city);
374    }
375
376    // Join 7: Product popularity
377    println!("\n--- Join 7: Product Popularity Ranking ---");
378    
379    // Join orders with products
380    let product_order_pairs = JoinQuery::new(&products, &orders).inner_join(
381        Product::id_r(),
382        Order::product_id_r(),
383        |product, order| (product.clone(), order.clone()),
384    );
385
386    // Aggregate by product
387    let mut product_sales: HashMap<u32, (String, usize, u32, f64)> = HashMap::new();
388    for (product, order) in &product_order_pairs {
389        let entry = product_sales
390            .entry(product.id)
391            .or_insert_with(|| (product.name.clone(), 0, 0, 0.0));
392        entry.1 += 1; // order count
393        entry.2 += order.quantity; // total quantity
394        entry.3 += order.total; // total revenue
395    }
396
397    let mut popularity: Vec<_> = product_sales.into_iter().collect();
398    popularity.sort_by(|a, b| b.1 .1.cmp(&a.1 .1)); // sort by order count
399
400    for (_, (name, order_count, total_qty, revenue)) in &popularity {
401        println!(
402            "  • {} - {} orders - {} units - ${:.2}",
403            name, order_count, total_qty, revenue
404        );
405    }
406
407    // Join 8: User spending by city
408    println!("\n--- Join 8: Total Spending by City ---");
409    
410    // Join users with orders to get city and spending info
411    let user_city_orders = JoinQuery::new(&users, &orders).inner_join(
412        User::id_r(),
413        Order::user_id_r(),
414        |user, order| (user.city.clone(), order.total, user.id),
415    );
416
417    // Aggregate by city
418    let mut city_spending: HashMap<String, (f64, std::collections::HashSet<u32>)> = HashMap::new();
419    for (city, total, user_id) in &user_city_orders {
420        let entry = city_spending
421            .entry(city.clone())
422            .or_insert_with(|| (0.0, std::collections::HashSet::new()));
423        entry.0 += total;
424        entry.1.insert(*user_id);
425    }
426
427    let mut city_stats: Vec<_> = city_spending
428        .into_iter()
429        .map(|(city, (total, customers))| (city, total, customers.len()))
430        .collect();
431    
432    city_stats.sort_by(|a, b| {
433        b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)
434    });
435
436    for (city, total, customer_count) in &city_stats {
437        println!(
438            "  • {} - ${:.2} total - {} customers - ${:.2} avg",
439            city,
440            total,
441            customer_count,
442            total / *customer_count as f64
443        );
444    }
445
446    // Statistics summary
447    println!("\n=== Summary Statistics ===");
448    println!("Total orders: {}", orders.len());
449    
450    let total_revenue: f64 = orders.iter().map(|o| o.total).sum();
451    println!("Total revenue: ${:.2}", total_revenue);
452    println!("Average order value: ${:.2}", total_revenue / orders.len() as f64);
453    
454    // Count unique customers using a join
455    let unique_customers: std::collections::HashSet<u32> = 
456        orders.iter().map(|o| o.user_id).collect();
457    println!("Active customers: {}", unique_customers.len());
458    println!(
459        "Average orders per customer: {:.1}",
460        orders.len() as f64 / unique_customers.len() as f64
461    );
462
463    println!("\n✓ Join query builder demo complete!");
464}
examples/sql_comparison.rs (line 333)
84fn main() {
85    let employees = create_employees();
86    let departments = create_departments();
87    let projects = create_projects();
88
89    println!("\n╔════════════════════════════════════════════════════════════════════════╗");
90    println!("║     SQL vs Rust Query Builder Comparison                              ║");
91    println!("║     Demonstrating equivalent operations                               ║");
92    println!("╚════════════════════════════════════════════════════════════════════════╝");
93
94    // ============================================================================
95    // EXAMPLE 1: Simple SELECT with WHERE
96    // ============================================================================
97    print_separator("Example 1: SELECT with WHERE clause");
98    print_query(
99        "SELECT * FROM employees WHERE department = 'Engineering';",
100        "Filter employees by department"
101    );
102
103    let eng_query = Query::new(&employees)
104        .where_(Employee::department_r(), |dept| dept == "Engineering");
105    let engineering_employees = eng_query.all();
106
107    for emp in &engineering_employees {
108        println!("  ID: {}, Name: {}, Salary: ${:.2}", emp.id, emp.name, emp.salary);
109    }
110    println!("\nRust Query Builder:");
111    println!("Query::new(&employees)");
112    println!("    .where_(Employee::department_r(), |dept| dept == \"Engineering\")");
113    println!("    .all()");
114    println!("✓ Found {} employees", engineering_employees.len());
115
116    // ============================================================================
117    // EXAMPLE 2: SELECT specific columns
118    // ============================================================================
119    print_separator("Example 2: SELECT specific columns (Projection)");
120    print_query(
121        "SELECT name FROM employees WHERE salary > 80000;",
122        "Get names of high-earning employees"
123    );
124
125    let high_earners = Query::new(&employees)
126        .where_(Employee::salary_r(), |&salary| salary > 80000.0)
127        .select(Employee::name_r());
128
129    for name in &high_earners {
130        println!("  {}", name);
131    }
132    println!("\nRust Query Builder:");
133    println!("Query::new(&employees)");
134    println!("    .where_(Employee::salary_r(), |&salary| salary > 80000.0)");
135    println!("    .select(Employee::name_r())");
136    println!("✓ Found {} employees", high_earners.len());
137
138    // ============================================================================
139    // EXAMPLE 3: COUNT
140    // ============================================================================
141    print_separator("Example 3: COUNT aggregation");
142    print_query(
143        "SELECT COUNT(*) FROM employees WHERE age < 30;",
144        "Count young employees"
145    );
146
147    let young_count = Query::new(&employees)
148        .where_(Employee::age_r(), |&age| age < 30)
149        .count();
150
151    println!("  Count: {}", young_count);
152    println!("\nRust Query Builder:");
153    println!("Query::new(&employees)");
154    println!("    .where_(Employee::age_r(), |&age| age < 30)");
155    println!("    .count()");
156    println!("✓ Result: {}", young_count);
157
158    // ============================================================================
159    // EXAMPLE 4: SUM, AVG, MIN, MAX
160    // ============================================================================
161    print_separator("Example 4: Aggregate functions (SUM, AVG, MIN, MAX)");
162    print_query(
163        "SELECT \n\
164         SUM(salary) as total_salary,\n\
165         AVG(salary) as avg_salary,\n\
166         MIN(salary) as min_salary,\n\
167         MAX(salary) as max_salary\n\
168         FROM employees WHERE department = 'Engineering';",
169        "Engineering department salary statistics"
170    );
171
172    let eng_query = Query::new(&employees)
173        .where_(Employee::department_r(), |dept| dept == "Engineering");
174
175    let total = eng_query.sum(Employee::salary_r());
176    let avg = eng_query.avg(Employee::salary_r()).unwrap_or(0.0);
177    let min = eng_query.min_float(Employee::salary_r()).unwrap_or(0.0);
178    let max = eng_query.max_float(Employee::salary_r()).unwrap_or(0.0);
179
180    println!("  Total Salary: ${:.2}", total);
181    println!("  Avg Salary:   ${:.2}", avg);
182    println!("  Min Salary:   ${:.2}", min);
183    println!("  Max Salary:   ${:.2}", max);
184
185    println!("\nRust Query Builder:");
186    println!("let query = Query::new(&employees)");
187    println!("    .where_(Employee::department_r(), |dept| dept == \"Engineering\");");
188    println!("query.sum(Employee::salary_r())  // ${:.2}", total);
189    println!("query.avg(Employee::salary_r())  // ${:.2}", avg);
190    println!("query.min_float(Employee::salary_r())  // ${:.2}", min);
191    println!("query.max_float(Employee::salary_r())  // ${:.2}", max);
192
193    // ============================================================================
194    // EXAMPLE 5: GROUP BY with aggregation
195    // ============================================================================
196    print_separator("Example 5: GROUP BY with aggregation");
197    print_query(
198        "SELECT \n\
199         department,\n\
200         COUNT(*) as emp_count,\n\
201         AVG(salary) as avg_salary\n\
202         FROM employees\n\
203         GROUP BY department;",
204        "Statistics by department"
205    );
206
207    let by_dept = Query::new(&employees).group_by(Employee::department_r());
208
209    for (dept, emps) in &by_dept {
210        let dept_query = Query::new(emps);
211        let count = emps.len();
212        let avg_sal = dept_query.avg(Employee::salary_r()).unwrap_or(0.0);
213        println!("  {}: {} employees, avg salary ${:.2}", dept, count, avg_sal);
214    }
215
216    println!("\nRust Query Builder:");
217    println!("let by_dept = Query::new(&employees).group_by(Employee::department_r());");
218    println!("for (dept, emps) in &by_dept {{");
219    println!("    let dept_query = Query::new(emps);");
220    println!("    dept_query.avg(Employee::salary_r())");
221    println!("}}");
222
223    // ============================================================================
224    // EXAMPLE 6: ORDER BY
225    // ============================================================================
226    print_separator("Example 6: ORDER BY");
227    print_query(
228        "SELECT name, salary FROM employees\n\
229         WHERE department = 'Sales'\n\
230         ORDER BY salary DESC;",
231        "Sales employees ordered by salary (descending)"
232    );
233
234    let sales_sorted = Query::new(&employees)
235        .where_(Employee::department_r(), |dept| dept == "Sales")
236        .order_by_float_desc(Employee::salary_r());
237
238    for emp in &sales_sorted {
239        println!("  {}: ${:.2}", emp.name, emp.salary);
240    }
241
242    println!("\nRust Query Builder:");
243    println!("Query::new(&employees)");
244    println!("    .where_(Employee::department_r(), |dept| dept == \"Sales\")");
245    println!("    .order_by_float_desc(Employee::salary_r())");
246
247    // ============================================================================
248    // EXAMPLE 7: Multiple WHERE conditions (AND)
249    // ============================================================================
250    print_separator("Example 7: Multiple WHERE conditions (AND)");
251    print_query(
252        "SELECT * FROM employees\n\
253         WHERE salary > 70000 AND age < 35;",
254        "High-earning young employees"
255    );
256
257    let filter_query = Query::new(&employees)
258        .where_(Employee::salary_r(), |&salary| salary > 70000.0)
259        .where_(Employee::age_r(), |&age| age < 35);
260    let filtered = filter_query.all();
261
262    for emp in &filtered {
263        println!("  {}: Age {}, Salary ${:.2}", emp.name, emp.age, emp.salary);
264    }
265
266    println!("\nRust Query Builder:");
267    println!("Query::new(&employees)");
268    println!("    .where_(Employee::salary_r(), |&salary| salary > 70000.0)");
269    println!("    .where_(Employee::age_r(), |&age| age < 35)");
270    println!("    .all()");
271    println!("✓ Found {} employees", filtered.len());
272
273    // ============================================================================
274    // EXAMPLE 8: LIMIT (TOP N)
275    // ============================================================================
276    print_separator("Example 8: LIMIT / TOP N");
277    print_query(
278        "SELECT TOP 3 name, salary FROM employees\n\
279         ORDER BY salary DESC;",
280        "Top 3 highest paid employees"
281    );
282
283    let top_earners = Query::new(&employees)
284        .order_by_float_desc(Employee::salary_r());
285
286    for (i, emp) in top_earners.iter().take(3).enumerate() {
287        println!("  {}. {}: ${:.2}", i + 1, emp.name, emp.salary);
288    }
289
290    println!("\nRust Query Builder:");
291    println!("Query::new(&employees)");
292    println!("    .order_by_float_desc(Employee::salary_r())");
293    println!("    .into_iter().take(3)");
294
295    // ============================================================================
296    // EXAMPLE 9: OFFSET and LIMIT (Pagination)
297    // ============================================================================
298    print_separator("Example 9: OFFSET and LIMIT (Pagination)");
299    print_query(
300        "SELECT name FROM employees\n\
301         ORDER BY name\n\
302         LIMIT 3 OFFSET 3;",
303        "Page 2 of employee names (3 per page)"
304    );
305
306    let page_2 = Query::new(&employees)
307        .order_by(Employee::name_r())
308        .into_iter()
309        .skip(3)
310        .take(3)
311        .collect::<Vec<_>>();
312
313    for emp in &page_2 {
314        println!("  {}", emp.name);
315    }
316
317    println!("\nRust Query Builder:");
318    println!("Query::new(&employees)");
319    println!("    .order_by(Employee::name_r())");
320    println!("    .into_iter().skip(3).take(3)");
321
322    // ============================================================================
323    // EXAMPLE 10: INNER JOIN
324    // ============================================================================
325    print_separator("Example 10: INNER JOIN");
326    print_query(
327        "SELECT e.name, d.name as dept_name, d.budget\n\
328         FROM employees e\n\
329         INNER JOIN departments d ON e.department = d.name;",
330        "Employees with their department info"
331    );
332
333    let emp_dept = JoinQuery::new(&employees, &departments).inner_join(
334        Employee::department_r(),
335        Department::name_r(),
336        |emp, dept| (emp.name.clone(), dept.name.clone(), dept.budget),
337    );
338
339    for (emp_name, dept_name, budget) in emp_dept.iter().take(5) {
340        println!("  {} works in {} (Budget: ${:.0})", emp_name, dept_name, budget);
341    }
342    println!("  ... (showing first 5)");
343
344    println!("\nRust Query Builder:");
345    println!("JoinQuery::new(&employees, &departments).inner_join(");
346    println!("    Employee::department_r(),");
347    println!("    Department::name_r(),");
348    println!("    |emp, dept| (emp.name.clone(), dept.name.clone(), dept.budget)");
349    println!(")");
350    println!("✓ Found {} matches", emp_dept.len());
351
352    // ============================================================================
353    // EXAMPLE 11: GROUP BY with HAVING equivalent
354    // ============================================================================
355    print_separator("Example 11: GROUP BY with filtering (HAVING equivalent)");
356    print_query(
357        "SELECT city, COUNT(*) as emp_count, AVG(salary) as avg_salary\n\
358         FROM employees\n\
359         GROUP BY city\n\
360         HAVING COUNT(*) > 1;",
361        "Cities with multiple employees"
362    );
363
364    let by_city = Query::new(&employees).group_by(Employee::city_r());
365    let mut city_stats: Vec<_> = by_city
366        .iter()
367        .filter(|(_, emps)| emps.len() > 1) // HAVING equivalent
368        .map(|(city, emps)| {
369            let avg_sal = Query::new(emps).avg(Employee::salary_r()).unwrap_or(0.0);
370            (city.clone(), emps.len(), avg_sal)
371        })
372        .collect();
373    
374    city_stats.sort_by(|a, b| b.1.cmp(&a.1)); // Sort by count
375
376    for (city, count, avg_sal) in &city_stats {
377        println!("  {}: {} employees, avg salary ${:.2}", city, count, avg_sal);
378    }
379
380    println!("\nRust Query Builder:");
381    println!("let by_city = Query::new(&employees).group_by(Employee::city_r());");
382    println!("by_city.iter()");
383    println!("    .filter(|(_, emps)| emps.len() > 1)  // HAVING equivalent");
384    println!("    .map(|(city, emps)| {{");
385    println!("        let avg = Query::new(emps).avg(Employee::salary_r());");
386    println!("        (city, emps.len(), avg)");
387    println!("    }})");
388
389    // ============================================================================
390    // EXAMPLE 12: Complex query with multiple operations
391    // ============================================================================
392    print_separator("Example 12: Complex multi-operation query");
393    print_query(
394        "SELECT name, salary, age\n\
395         FROM employees\n\
396         WHERE department IN ('Engineering', 'Sales')\n\
397         AND salary BETWEEN 80000 AND 100000\n\
398         AND age >= 30\n\
399         ORDER BY salary DESC;",
400        "Experienced mid-to-senior level employees in core departments"
401    );
402
403    let complex_query = Query::new(&employees)
404        .where_(Employee::department_r(), |dept| dept == "Engineering" || dept == "Sales")
405        .where_(Employee::salary_r(), |&sal| sal >= 80000.0 && sal <= 100000.0)
406        .where_(Employee::age_r(), |&age| age >= 30)
407        .order_by_float_desc(Employee::salary_r());
408
409    for emp in &complex_query {
410        println!("  {}: Age {}, {} dept, ${:.2}", emp.name, emp.age, emp.department, emp.salary);
411    }
412
413    println!("\nRust Query Builder:");
414    println!("Query::new(&employees)");
415    println!("    .where_(Employee::department_r(), |dept| dept == \"Engineering\" || dept == \"Sales\")");
416    println!("    .where_(Employee::salary_r(), |&sal| sal >= 80000.0 && sal <= 100000.0)");
417    println!("    .where_(Employee::age_r(), |&age| age >= 30)");
418    println!("    .order_by_float_desc(Employee::salary_r())");
419    println!("✓ Found {} employees", complex_query.len());
420
421    // ============================================================================
422    // EXAMPLE 13: Three-table JOIN
423    // ============================================================================
424    print_separator("Example 13: Three-table JOIN");
425    print_query(
426        "SELECT p.name as project, d.name as department, d.location\n\
427         FROM projects p\n\
428         INNER JOIN departments d ON p.department_id = d.id;",
429        "Projects with their department details"
430    );
431
432    let proj_dept = JoinQuery::new(&projects, &departments).inner_join(
433        Project::department_id_r(),
434        Department::id_r(),
435        |proj, dept| {
436            (proj.name.clone(), dept.name.clone(), dept.location.clone(), proj.budget)
437        },
438    );
439
440    for (proj_name, dept_name, location, budget) in &proj_dept {
441        println!("  {}: {} dept in {} (${:.0})", proj_name, dept_name, location, budget);
442    }
443
444    println!("\nRust Query Builder:");
445    println!("JoinQuery::new(&projects, &departments).inner_join(");
446    println!("    Project::department_id_r(),");
447    println!("    Department::id_r(),");
448    println!("    |proj, dept| (proj.name, dept.name, dept.location, proj.budget)");
449    println!(")");
450
451    // ============================================================================
452    // EXAMPLE 14: Subquery equivalent (using intermediate results)
453    // ============================================================================
454    print_separator("Example 14: Subquery equivalent");
455    print_query(
456        "SELECT * FROM employees\n\
457         WHERE salary > (SELECT AVG(salary) FROM employees);",
458        "Employees earning above average"
459    );
460
461    let avg_salary = Query::new(&employees)
462        .avg(Employee::salary_r())
463        .unwrap_or(0.0);
464
465    let above_avg_query = Query::new(&employees)
466        .where_(Employee::salary_r(), move |&sal| sal > avg_salary);
467    let above_avg = above_avg_query.all();
468
469    println!("  Average salary: ${:.2}", avg_salary);
470    for emp in &above_avg {
471        println!("  {}: ${:.2} ({:.1}% above average)", 
472            emp.name, emp.salary, ((emp.salary - avg_salary) / avg_salary * 100.0));
473    }
474
475    println!("\nRust Query Builder:");
476    println!("let avg = Query::new(&employees).avg(Employee::salary_r()).unwrap_or(0.0);");
477    println!("Query::new(&employees)");
478    println!("    .where_(Employee::salary_r(), |&sal| sal > avg)");
479    println!("    .all()");
480    println!("✓ Found {} employees above average", above_avg.len());
481
482    // ============================================================================
483    // EXAMPLE 15: Advanced aggregation (revenue calculation)
484    // ============================================================================
485    print_separator("Example 15: Advanced aggregation");
486    print_query(
487        "SELECT \n\
488         d.name,\n\
489         d.budget as dept_budget,\n\
490         SUM(p.budget) as total_project_budget,\n\
491         (d.budget - SUM(p.budget)) as remaining_budget\n\
492         FROM departments d\n\
493         LEFT JOIN projects p ON d.id = p.department_id\n\
494         GROUP BY d.name, d.budget;",
495        "Department budget utilization"
496    );
497
498    // Join departments with projects
499    let dept_projects = JoinQuery::new(&departments, &projects).left_join(
500        Department::id_r(),
501        Project::department_id_r(),
502        |dept, proj| (dept.clone(), proj.map(|p| p.clone())),
503    );
504
505    // Aggregate by department
506    let mut dept_budget_map: HashMap<String, (f64, Vec<f64>)> = HashMap::new();
507    for (dept, proj) in dept_projects {
508        let entry = dept_budget_map
509            .entry(dept.name.clone())
510            .or_insert((dept.budget, Vec::new()));
511        if let Some(p) = proj {
512            entry.1.push(p.budget);
513        }
514    }
515
516    for (dept_name, (total_budget, project_budgets)) in dept_budget_map.iter() {
517        let used: f64 = project_budgets.iter().sum();
518        let remaining = total_budget - used;
519        println!("  {}: Budget ${:.0}, Used ${:.0}, Remaining ${:.0} ({:.1}% utilized)",
520            dept_name, total_budget, used, remaining, (used / total_budget * 100.0));
521    }
522
523    println!("\nRust Query Builder:");
524    println!("let dept_projects = JoinQuery::new(&departments, &projects)");
525    println!("    .left_join(Department::id_r(), Project::department_id_r(), ...)");
526    println!("// Then aggregate using HashMap");
527
528    // ============================================================================
529    // Summary
530    // ============================================================================
531    print_separator("Summary");
532    println!("\n✓ All 15 SQL queries successfully replicated with Rust Query Builder!");
533    println!("\nDemonstrated equivalents for:");
534    println!("  • SELECT with WHERE");
535    println!("  • Projection (SELECT specific columns)");
536    println!("  • Aggregations (COUNT, SUM, AVG, MIN, MAX)");
537    println!("  • GROUP BY");
538    println!("  • ORDER BY");
539    println!("  • LIMIT and OFFSET");
540    println!("  • INNER JOIN and LEFT JOIN");
541    println!("  • Complex conditions (AND, OR, BETWEEN)");
542    println!("  • Subqueries");
543    println!("  • Multi-table operations");
544    println!("\n🎯 Type-safe, compile-time checked, and zero runtime overhead!");
545    println!();
546}
examples/memory_safety_verification.rs (line 252)
98fn main() {
99    println!("\n╔═══════════════════════════════════════════════════════════════╗");
100    println!("║  Memory Safety Verification                                   ║");
101    println!("║  Proving 'static doesn't cause memory leaks                   ║");
102    println!("╚═══════════════════════════════════════════════════════════════╝\n");
103
104    println!("🔍 Understanding 'static:\n");
105    println!("  • T: 'static means: Type T doesn't contain non-'static references");
106    println!("  • It does NOT mean: Data lives for entire program");
107    println!("  • It's needed for: Storing types in trait objects");
108    println!("  • Safety: Compiler ensures no dangling references\n");
109
110    // ============================================================================
111    // TEST 1: Basic Query - Verify Cleanup
112    // ============================================================================
113    println!("═══════════════════════════════════════════════════════════════");
114    println!("Test 1: Basic WHERE query - verify all data is dropped");
115    println!("═══════════════════════════════════════════════════════════════\n");
116
117    reset_stats();
118    {
119        let employees = vec![
120            create_employee(1, "Alice", "Engineering", 95000.0),
121            create_employee(2, "Bob", "Engineering", 87000.0),
122            create_employee(3, "Carol", "Sales", 75000.0),
123        ];
124        print_memory_status("After creating employees");
125
126        {
127            let query = Query::new(&employees)
128                .where_(Employee::department_r(), |dept| dept == "Engineering");
129            let results = query.all();
130            
131            println!("  Found {} engineering employees", results.len());
132            print_memory_status("During query execution");
133        }
134        
135        print_memory_status("After query scope ends");
136    }
137    
138    print_memory_status("After employees scope ends");
139    println!();
140
141    // ============================================================================
142    // TEST 2: Multiple Queries - No Accumulation
143    // ============================================================================
144    println!("═══════════════════════════════════════════════════════════════");
145    println!("Test 2: Multiple queries - verify no memory accumulation");
146    println!("═══════════════════════════════════════════════════════════════\n");
147
148    reset_stats();
149    {
150        let employees = vec![
151            create_employee(1, "Alice", "Engineering", 95000.0),
152            create_employee(2, "Bob", "Sales", 75000.0),
153        ];
154
155        let initial_stats = get_stats();
156        println!("  Initial allocations: {}", initial_stats.0);
157
158        // Run 10 queries
159        for i in 1..=10 {
160            let query = Query::new(&employees)
161                .where_(Employee::salary_r(), |&s| s > 70000.0);
162            let _results = query.all();
163            
164            if i % 3 == 0 {
165                let (allocs, drops) = get_stats();
166                println!("  After {} queries - Allocated: {}, Dropped: {}", i, allocs, drops);
167            }
168        }
169
170        let final_stats = get_stats();
171        println!("\n  After 10 queries:");
172        println!("    Allocations: {} (should be same as initial)", final_stats.0);
173        println!("    ✅ No memory accumulation from queries!");
174    }
175
176    print_memory_status("After all queries and employees dropped");
177    println!();
178
179    // ============================================================================
180    // TEST 3: ORDER BY (requires Clone) - Track Cloning
181    // ============================================================================
182    println!("═══════════════════════════════════════════════════════════════");
183    println!("Test 3: ORDER BY (with Clone) - verify controlled cloning");
184    println!("═══════════════════════════════════════════════════════════════\n");
185
186    reset_stats();
187    {
188        let employees = vec![
189            create_employee(1, "Alice", "Engineering", 95000.0),
190            create_employee(2, "Bob", "Sales", 87000.0),
191            create_employee(3, "Carol", "Marketing", 75000.0),
192        ];
193        
194        let (before_allocs, _) = get_stats();
195        println!("  Before sorting: {} allocations", before_allocs);
196
197        {
198            let sorted = Query::new(&employees)
199                .order_by_float_desc(Employee::salary_r());
200            
201            let (after_allocs, _) = get_stats();
202            println!("  After sorting: {} allocations", after_allocs);
203            println!("  Cloned items: {} (expected: {})", after_allocs - before_allocs, employees.len());
204            println!("  Sorted {} employees by salary", sorted.len());
205            
206            // sorted goes out of scope here
207        }
208        
209        print_memory_status("After sorted results dropped");
210    }
211    
212    print_memory_status("After employees dropped");
213    println!();
214
215    // ============================================================================
216    // TEST 4: JOIN Operations - No Leaks
217    // ============================================================================
218    println!("═══════════════════════════════════════════════════════════════");
219    println!("Test 4: JOIN operations - verify no memory leaks");
220    println!("═══════════════════════════════════════════════════════════════\n");
221
222    #[derive(Keypaths)]
223    struct Department {
224        id: u32,
225        name: String,
226        drop_tracker: DropTracker,
227    }
228
229    reset_stats();
230    {
231        let employees = vec![
232            create_employee(1, "Alice", "Engineering", 95000.0),
233            create_employee(2, "Bob", "Sales", 87000.0),
234        ];
235
236        let departments = vec![
237            Department {
238                id: 1,
239                name: "Engineering".to_string(),
240                drop_tracker: DropTracker::new(),
241            },
242            Department {
243                id: 2,
244                name: "Sales".to_string(),
245                drop_tracker: DropTracker::new(),
246            },
247        ];
248
249        print_memory_status("After creating data");
250
251        {
252            let results = JoinQuery::new(&employees, &departments)
253                .inner_join(
254                    Employee::department_r(),
255                    Department::name_r(),
256                    |emp, dept| (emp.name.clone(), dept.name.clone()),
257                );
258            
259            println!("  Joined {} pairs", results.len());
260            print_memory_status("During join results");
261        }
262
263        print_memory_status("After join results dropped");
264    }
265
266    print_memory_status("After all data dropped");
267    println!();
268
269    // ============================================================================
270    // TEST 5: Large Scale - Memory Behavior
271    // ============================================================================
272    println!("═══════════════════════════════════════════════════════════════");
273    println!("Test 5: Large scale (1000 items) - verify cleanup");
274    println!("═══════════════════════════════════════════════════════════════\n");
275
276    reset_stats();
277    {
278        let mut large_dataset = Vec::new();
279        for i in 0..1000 {
280            large_dataset.push(create_employee(
281                i,
282                &format!("Employee {}", i),
283                if i % 3 == 0 { "Engineering" } else if i % 3 == 1 { "Sales" } else { "Marketing" },
284                50000.0 + (i as f64 * 100.0),
285            ));
286        }
287
288        let (initial_allocs, _) = get_stats();
289        println!("  Created 1000 employees: {} allocations (~10MB)", initial_allocs);
290
291        // Run complex query
292        {
293            let query = Query::new(&large_dataset)
294                .where_(Employee::salary_r(), |&s| s > 80000.0)
295                .where_(Employee::department_r(), |d| d == "Engineering");
296            let results = query.all();
297            
298            println!("  Filtered to {} employees", results.len());
299            
300            let (during_allocs, _) = get_stats();
301            let extra = during_allocs - initial_allocs;
302            println!("  Extra allocations during query: {} (should be 0)", extra);
303            
304            if extra == 0 {
305                println!("  ✅ Zero-copy filtering confirmed!");
306            }
307        }
308
309        print_memory_status("After query results dropped");
310    }
311
312    print_memory_status("After 1000 employees dropped");
313    println!();
314
315    // ============================================================================
316    // TEST 6: Explanation of 'static
317    // ============================================================================
318    println!("═══════════════════════════════════════════════════════════════");
319    println!("Explanation: Why 'static is safe and doesn't leak");
320    println!("═══════════════════════════════════════════════════════════════\n");
321
322    println!("❓ What does T: 'static mean?\n");
323    println!("  WRONG ❌: \"T lives for the entire program\"");
324    println!("  RIGHT ✅: \"T doesn't contain non-'static references\"\n");
325
326    println!("Examples:\n");
327    println!("  struct OwnedData {{          // T: 'static ✅");
328    println!("      id: u32,                 // Owned data");
329    println!("      name: String,            // Owned data");
330    println!("  }}");
331    println!();
332    println!("  struct WithReference<'a> {{  // NOT 'static ❌");
333    println!("      data: &'a String,        // Contains reference");
334    println!("  }}");
335    println!();
336
337    println!("Why we use T: 'static:\n");
338    println!("  1. Store type in trait objects: Box<dyn Fn(&T) -> bool>");
339    println!("  2. Prevent dangling references in closures");
340    println!("  3. Ensure type safety at compile time");
341    println!();
342
343    println!("Lifetime of data:\n");
344    println!("  • Data is owned by your Vec<T>");
345    println!("  • Query just borrows &'a [T]");
346    println!("  • When Vec<T> is dropped, all T are dropped");
347    println!("  • No memory leaks possible!\n");
348
349    // ============================================================================
350    // TEST 7: Drop Order Verification
351    // ============================================================================
352    println!("═══════════════════════════════════════════════════════════════");
353    println!("Test 7: Drop order - verify proper RAII");
354    println!("═══════════════════════════════════════════════════════════════\n");
355
356    reset_stats();
357    
358    println!("Creating scoped data...");
359    {
360        let employees = vec![
361            create_employee(1, "Alice", "Engineering", 95000.0),
362            create_employee(2, "Bob", "Sales", 87000.0),
363        ];
364        println!("  Created 2 employees");
365
366        {
367            println!("  Creating query...");
368            let query = Query::new(&employees)
369                .where_(Employee::department_r(), |dept| dept == "Engineering");
370            
371            {
372                println!("  Executing query...");
373                let results = query.all();
374                println!("    Found {} results", results.len());
375                println!("  Query results going out of scope...");
376            }
377            println!("  Results dropped (just Vec<&Employee>, no Employee drops)");
378            
379            println!("  Query going out of scope...");
380        }
381        println!("  Query dropped (just filters, no Employee drops)");
382        
383        println!("  Employees vector going out of scope...");
384    }
385    println!("  Employees dropped - NOW Employees are freed!\n");
386    
387    let (allocs, drops) = get_stats();
388    println!("Final stats:");
389    println!("  Allocated: {}", allocs);
390    println!("  Dropped: {}", drops);
391    println!("  Leaked: {}", allocs - drops);
392    
393    if allocs == drops {
394        println!("\n✅ Perfect! All allocated memory was freed!");
395    } else {
396        println!("\n❌ Memory leak detected!");
397    }
398
399    // ============================================================================
400    // TEST 8: Arc/Rc Compatibility
401    // ============================================================================
402    println!("\n═══════════════════════════════════════════════════════════════");
403    println!("Test 8: Arc/Rc compatibility - shared ownership works");
404    println!("═══════════════════════════════════════════════════════════════\n");
405
406    {
407        use std::sync::Arc;
408        
409        #[derive(Keypaths)]
410        struct SharedData {
411            id: u32,
412            value: Arc<String>,  // Shared ownership
413        }
414
415        let shared_string = Arc::new("Shared Value".to_string());
416        println!("  Arc strong count: {}", Arc::strong_count(&shared_string));
417
418        let data = vec![
419            SharedData { id: 1, value: Arc::clone(&shared_string) },
420            SharedData { id: 2, value: Arc::clone(&shared_string) },
421        ];
422        
423        println!("  Arc strong count after creating data: {}", Arc::strong_count(&shared_string));
424
425        {
426            let query = Query::new(&data)
427                .where_(SharedData::id_r(), |&id| id > 0);
428            let results = query.all();
429            println!("  Found {} items", results.len());
430            println!("  Arc strong count during query: {}", Arc::strong_count(&shared_string));
431        }
432
433        println!("  Arc strong count after query: {}", Arc::strong_count(&shared_string));
434    }
435    
436    println!("  ✅ Arc reference counting works correctly!\n");
437
438    // ============================================================================
439    // TEST 9: Large Data Without Clone - Zero Copy
440    // ============================================================================
441    println!("═══════════════════════════════════════════════════════════════");
442    println!("Test 9: Large data without Clone - verify zero-copy");
443    println!("═══════════════════════════════════════════════════════════════\n");
444
445    #[derive(Keypaths)]  // NO Clone!
446    struct LargeRecord {
447        id: u32,
448        // Simulate 1MB of data that we DON'T want to clone
449        huge_data: Vec<u8>,
450    }
451
452    {
453        println!("  Creating 10 records (1MB each = 10MB total)...");
454        let large_records: Vec<LargeRecord> = (0..10)
455            .map(|i| LargeRecord {
456                id: i,
457                huge_data: vec![i as u8; 1_000_000], // 1MB each
458            })
459            .collect();
460
461        println!("  Total memory: ~10MB");
462
463        {
464            println!("\n  Running query without Clone...");
465            let query = Query::new(&large_records)
466                .where_(LargeRecord::id_r(), |&id| id < 5);
467            let results = query.all();  // Vec<&LargeRecord> - NO CLONING!
468            
469            println!("  Found {} records", results.len());
470            println!("  Memory copied: 0 bytes (just references)");
471            println!("  ✅ Zero-copy achieved!");
472        }
473
474        println!("\n  Query dropped - no memory freed (no cloning happened)");
475    }
476    
477    println!("  Records dropped - 10MB freed\n");
478
479    // ============================================================================
480    // TEST 10: Lifetime Safety
481    // ============================================================================
482    println!("═══════════════════════════════════════════════════════════════");
483    println!("Test 10: Lifetime safety - compiler prevents dangling refs");
484    println!("═══════════════════════════════════════════════════════════════\n");
485
486    println!("  The following code WILL NOT COMPILE (by design):\n");
487    println!("  ```rust");
488    println!("  let query;");
489    println!("  {{");
490    println!("      let data = vec![...];");
491    println!("      query = Query::new(&data);  // data borrowed here");
492    println!("  }}  // data dropped");
493    println!("  let results = query.all();  // ❌ ERROR: data doesn't live long enough");
494    println!("  ```\n");
495    println!("  ✅ Rust's borrow checker prevents use-after-free!");
496    println!("  ✅ 'static bound + lifetimes = memory safety guaranteed!\n");
497
498    // ============================================================================
499    // Summary
500    // ============================================================================
501    println!("═══════════════════════════════════════════════════════════════");
502    println!("Summary: Memory Safety Guarantees");
503    println!("═══════════════════════════════════════════════════════════════\n");
504
505    let (total_allocs, total_drops) = get_stats();
506    let leaked = total_allocs.saturating_sub(total_drops);
507
508    println!("Overall Statistics:");
509    println!("  Total allocations: {}", total_allocs);
510    println!("  Total drops: {}", total_drops);
511    println!("  Memory leaks: {}", leaked);
512
513    if leaked == 0 {
514        println!("\n🎉 VERIFIED: Zero memory leaks!\n");
515    } else {
516        println!("\n⚠️  WARNING: Potential memory leak detected!\n");
517    }
518
519    println!("Guarantees Verified:");
520    println!("  ✅ 'static doesn't cause data to live forever");
521    println!("  ✅ All allocated memory is properly freed");
522    println!("  ✅ No memory leaks from queries");
523    println!("  ✅ Query only holds references, not ownership");
524    println!("  ✅ Rust's borrow checker prevents dangling references");
525    println!("  ✅ RAII ensures proper cleanup");
526    println!("  ✅ Zero-copy operations don't allocate");
527    println!("  ✅ Clone operations are explicit and controlled\n");
528
529    println!("Performance Benefits:");
530    println!("  ✅ Filtering: 0 bytes copied (v0.2.0) vs 10MB (v0.1.0)");
531    println!("  ✅ Counting: 0 bytes copied");
532    println!("  ✅ Aggregations: 0 bytes copied");
533    println!("  ✅ Only ordering/grouping clone when needed\n");
534
535    println!("Safety Guarantees:");
536    println!("  ✅ Compile-time prevention of dangling references");
537    println!("  ✅ No use-after-free possible");
538    println!("  ✅ No double-free possible");
539    println!("  ✅ Automatic cleanup via RAII\n");
540
541    println!("✓ All memory safety tests PASSED!\n");
542}
Source

pub fn inner_join<K, O, F>( &self, left_key: KeyPaths<L, K>, right_key: KeyPaths<R, K>, mapper: F, ) -> Vec<O>
where K: Eq + Hash + Clone + 'static, F: Fn(&L, &R) -> O,

Performs an inner join between two collections.

Returns only the pairs where the join keys match. Uses a hash-based algorithm for O(n + m) performance.

§Arguments
  • left_key - Key-path to the join field in the left collection
  • right_key - Key-path to the join field in the right collection
  • mapper - Function to transform matching pairs into the result type
§Example
let results = JoinQuery::new(&users, &orders)
    .inner_join(
        User::id_r(),
        Order::user_id_r(),
        |user, order| UserOrder {
            user_name: user.name.clone(),
            order_total: order.total,
        }
    );
Examples found in repository?
examples/without_clone.rs (lines 149-156)
28fn main() {
29    println!("╔════════════════════════════════════════════════════════════╗");
30    println!("║  Query Builder Without Clone - Performance Optimization   ║");
31    println!("╚════════════════════════════════════════════════════════════╝\n");
32
33    let employees = vec![
34        Employee {
35            id: 1,
36            name: "Alice".to_string(),
37            email: "alice@example.com".to_string(),
38            department: "Engineering".to_string(),
39            salary: 95000.0,
40            large_data: vec![0; 1000], // Large data - expensive to clone!
41        },
42        Employee {
43            id: 2,
44            name: "Bob".to_string(),
45            email: "bob@example.com".to_string(),
46            department: "Engineering".to_string(),
47            salary: 87000.0,
48            large_data: vec![0; 1000],
49        },
50        Employee {
51            id: 3,
52            name: "Carol".to_string(),
53            email: "carol@example.com".to_string(),
54            department: "Sales".to_string(),
55            salary: 75000.0,
56            large_data: vec![0; 1000],
57        },
58    ];
59
60    let departments = vec![
61        Department {
62            id: 1,
63            name: "Engineering".to_string(),
64            budget: 1000000.0,
65        },
66        Department {
67            id: 2,
68            name: "Sales".to_string(),
69            budget: 500000.0,
70        },
71    ];
72
73    println!("✅ Operations that DON'T require Clone:\n");
74
75    // 1. WHERE filtering - returns Vec<&T>
76    println!("1. WHERE filtering (returns references)");
77    let query = Query::new(&employees)
78        .where_(Employee::department_r(), |dept| dept == "Engineering");
79    let engineering = query.all();
80    println!("   Found {} engineering employees", engineering.len());
81    for emp in &engineering {
82        println!("     - {}: ${:.0}", emp.name, emp.salary);
83    }
84
85    // 2. COUNT - no cloning needed
86    println!("\n2. COUNT aggregation");
87    let count = Query::new(&employees)
88        .where_(Employee::salary_r(), |&sal| sal > 80000.0)
89        .count();
90    println!("   {} employees earn over $80k", count);
91
92    // 3. SELECT - only clones the selected field
93    println!("\n3. SELECT (only selected fields are cloned)");
94    let names: Vec<String> = Query::new(&employees)
95        .select(Employee::name_r());
96    println!("   Employee names: {:?}", names);
97
98    // 4. FIRST - returns Option<&T>
99    println!("\n4. FIRST (returns reference)");
100    let query = Query::new(&employees)
101        .where_(Employee::salary_r(), |&sal| sal > 90000.0);
102    if let Some(emp) = query.first() {
103        println!("   First high earner: {} (${:.0})", emp.name, emp.salary);
104    }
105
106    // 5. SUM/AVG aggregations - no cloning
107    println!("\n5. Aggregations (SUM/AVG)");
108    let eng_query = Query::new(&employees)
109        .where_(Employee::department_r(), |dept| dept == "Engineering");
110    let total = eng_query.sum(Employee::salary_r());
111    let avg = eng_query.avg(Employee::salary_r()).unwrap_or(0.0);
112    println!("   Engineering total: ${:.0}", total);
113    println!("   Engineering average: ${:.0}", avg);
114
115    // 6. MIN/MAX - no cloning
116    println!("\n6. MIN/MAX");
117    let min = Query::new(&employees).min_float(Employee::salary_r());
118    let max = Query::new(&employees).max_float(Employee::salary_r());
119    println!("   Salary range: ${:.0} - ${:.0}", min.unwrap(), max.unwrap());
120
121    // 7. LIMIT - returns Vec<&T>
122    println!("\n7. LIMIT (returns references)");
123    let query = Query::new(&employees);
124    let first_two = query.limit(2);
125    println!("   First 2 employees:");
126    for emp in &first_two {
127        println!("     - {}", emp.name);
128    }
129
130    // 8. SKIP/Pagination - returns Vec<&T>
131    println!("\n8. SKIP/Pagination (returns references)");
132    let query = Query::new(&employees);
133    let page_2 = query.skip(2).limit(1);
134    println!("   Page 2:");
135    for emp in &page_2 {
136        println!("     - {}", emp.name);
137    }
138
139    // 9. EXISTS - just checks
140    println!("\n9. EXISTS check");
141    let has_sales = Query::new(&employees)
142        .where_(Employee::department_r(), |dept| dept == "Sales")
143        .exists();
144    println!("   Has Sales employees: {}", has_sales);
145
146    // 10. JOIN - no Clone required on L or R!
147    println!("\n10. JOIN operations (no Clone required!)");
148    let results = JoinQuery::new(&employees, &departments)
149        .inner_join(
150            Employee::department_r(),
151            Department::name_r(),
152            |emp, dept| {
153                // Mapper only clones what it needs for the result
154                (emp.name.clone(), dept.budget)
155            },
156        );
157    println!("   Employee-Department pairs:");
158    for (name, budget) in &results {
159        println!("     - {} works in dept with ${:.0} budget", name, budget);
160    }
161
162    println!("\n╔════════════════════════════════════════════════════════════╗");
163    println!("║  Operations that REQUIRE Clone (only when needed)         ║");
164    println!("╚════════════════════════════════════════════════════════════╝\n");
165
166    println!("⚠️  The following operations require Clone because they return owned Vec<T>:");
167    println!("   - order_by() / order_by_desc()");
168    println!("   - order_by_float() / order_by_float_desc()");
169    println!("   - group_by()");
170    println!("\n   To use these, add #[derive(Clone)] to your struct:");
171    println!("   ```rust");
172    println!("   #[derive(Clone, Keypaths)]  // Add Clone here");
173    println!("   struct Employee {{ ... }}");
174    println!("   ```");
175
176    println!("\n╔════════════════════════════════════════════════════════════╗");
177    println!("║  Performance Benefits                                      ║");
178    println!("╚════════════════════════════════════════════════════════════╝\n");
179
180    println!("✅ Zero cloning for most operations");
181    println!("✅ Work with large structs efficiently");
182    println!("✅ No unnecessary memory allocations");
183    println!("✅ Only clone when you actually need owned data");
184    println!("✅ Pay for what you use");
185
186    println!("\n✓ Example complete! Most operations work without Clone.\n");
187}
More examples
Hide additional examples
examples/doc_examples.rs (lines 151-155)
30fn main() {
31    println!("Testing documentation examples...\n");
32
33    // Example from README - Quick Start
34    println!("Test 1: README Quick Start Example");
35    {
36        let products = vec![
37            Product { id: 1, name: "Laptop".to_string(), price: 999.99, category: "Electronics".to_string(), stock: 15, rating: 4.5 },
38            Product { id: 2, name: "Mouse".to_string(), price: 29.99, category: "Electronics".to_string(), stock: 50, rating: 4.0 },
39            Product { id: 3, name: "Desk".to_string(), price: 299.99, category: "Furniture".to_string(), stock: 10, rating: 4.8 },
40        ];
41
42        let affordable_query = Query::new(&products)
43            .where_(Product::category_r(), |cat| cat == "Electronics")
44            .where_(Product::price_r(), |&price| price < 100.0);
45        let affordable_electronics = affordable_query.all();
46
47        println!("  Found {} affordable electronics ✅", affordable_electronics.len());
48    }
49
50    // Example from README - Filtering
51    println!("\nTest 2: Filtering Example");
52    {
53        let products = vec![
54            Product { id: 1, name: "Laptop".to_string(), price: 999.99, category: "Electronics".to_string(), stock: 15, rating: 4.5 },
55        ];
56
57        let electronics_query = Query::new(&products)
58            .where_(Product::category_r(), |cat| cat == "Electronics");
59        let electronics = electronics_query.all();
60
61        println!("  Found {} electronics ✅", electronics.len());
62    }
63
64    // Example from README - Selecting
65    println!("\nTest 3: Selecting Fields Example");
66    {
67        let products = vec![
68            Product { id: 1, name: "Laptop".to_string(), price: 999.99, category: "Electronics".to_string(), stock: 15, rating: 4.5 },
69            Product { id: 2, name: "Mouse".to_string(), price: 29.99, category: "Electronics".to_string(), stock: 50, rating: 4.0 },
70        ];
71
72        let names: Vec<String> = Query::new(&products)
73            .select(Product::name_r());
74
75        println!("  Selected {} names ✅", names.len());
76    }
77
78    // Example from README - Ordering
79    println!("\nTest 4: Ordering Example");
80    {
81        let products = vec![
82            Product { id: 1, name: "Laptop".to_string(), price: 999.99, category: "Electronics".to_string(), stock: 15, rating: 4.5 },
83            Product { id: 2, name: "Mouse".to_string(), price: 29.99, category: "Electronics".to_string(), stock: 50, rating: 4.0 },
84        ];
85
86        let by_price = Query::new(&products).order_by_float(Product::price_r());
87        println!("  Ordered {} products ✅", by_price.len());
88    }
89
90    // Example from README - Aggregations
91    println!("\nTest 5: Aggregations Example");
92    {
93        let products = vec![
94            Product { id: 1, name: "Laptop".to_string(), price: 999.99, category: "Electronics".to_string(), stock: 15, rating: 4.5 },
95            Product { id: 2, name: "Mouse".to_string(), price: 29.99, category: "Electronics".to_string(), stock: 50, rating: 4.0 },
96        ];
97
98        let electronics_query = Query::new(&products)
99            .where_(Product::category_r(), |cat| cat == "Electronics");
100
101        let count = electronics_query.count();
102        let total_value: f64 = electronics_query.sum(Product::price_r());
103        let avg_price = electronics_query.avg(Product::price_r()).unwrap_or(0.0);
104
105        println!("  Count: {}, Total: ${:.2}, Avg: ${:.2} ✅", count, total_value, avg_price);
106    }
107
108    // Example from README - Grouping
109    println!("\nTest 6: Grouping Example");
110    {
111        let products = vec![
112            Product { id: 1, name: "Laptop".to_string(), price: 999.99, category: "Electronics".to_string(), stock: 15, rating: 4.5 },
113            Product { id: 2, name: "Mouse".to_string(), price: 29.99, category: "Electronics".to_string(), stock: 50, rating: 4.0 },
114            Product { id: 3, name: "Desk".to_string(), price: 299.99, category: "Furniture".to_string(), stock: 10, rating: 4.8 },
115        ];
116
117        let by_category = Query::new(&products).group_by(Product::category_r());
118        println!("  Grouped into {} categories ✅", by_category.len());
119    }
120
121    // Example from README - Pagination
122    println!("\nTest 7: Pagination Example");
123    {
124        let products = vec![
125            Product { id: 1, name: "P1".to_string(), price: 10.0, category: "A".to_string(), stock: 1, rating: 4.0 },
126            Product { id: 2, name: "P2".to_string(), price: 20.0, category: "A".to_string(), stock: 1, rating: 4.0 },
127            Product { id: 3, name: "P3".to_string(), price: 30.0, category: "A".to_string(), stock: 1, rating: 4.0 },
128        ];
129
130        let query = Query::new(&products);
131        let first_10 = query.limit(10);
132        let page_1 = query.skip(0).limit(10);
133
134        println!("  Limited to {} products ✅", first_10.len());
135        println!("  Page 1 has {} products ✅", page_1.len());
136    }
137
138    // Example from README - Join
139    println!("\nTest 8: Join Example");
140    {
141        let users = vec![
142            User { id: 1, name: "Alice".to_string() },
143            User { id: 2, name: "Bob".to_string() },
144        ];
145
146        let orders = vec![
147            Order { id: 101, user_id: 1, total: 99.99 },
148            Order { id: 102, user_id: 1, total: 149.99 },
149        ];
150
151        let user_orders = JoinQuery::new(&users, &orders).inner_join(
152            User::id_r(),
153            Order::user_id_r(),
154            |user, order| (user.name.clone(), order.total),
155        );
156
157        println!("  Joined {} user-order pairs ✅", user_orders.len());
158    }
159
160    // Example from SQL_COMPARISON - SELECT with WHERE
161    println!("\nTest 9: SQL Comparison - SELECT with WHERE");
162    {
163        #[derive(Clone, Keypaths)]
164        struct Employee {
165            department: String,
166        }
167
168        let employees = vec![
169            Employee { department: "Engineering".to_string() },
170            Employee { department: "Sales".to_string() },
171        ];
172
173        let engineering_query = Query::new(&employees)
174            .where_(Employee::department_r(), |dept| dept == "Engineering");
175        let engineering = engineering_query.all();
176
177        println!("  Found {} engineering employees ✅", engineering.len());
178    }
179
180    // Example from USAGE.md - Complex Filtering
181    println!("\nTest 10: USAGE - Complex Filtering");
182    {
183        let products = vec![
184            Product { id: 1, name: "Laptop".to_string(), price: 999.99, category: "Electronics".to_string(), stock: 15, rating: 4.5 },
185        ];
186
187        let results_query = Query::new(&products)
188            .where_(Product::category_r(), |cat| cat == "Electronics")
189            .where_(Product::price_r(), |&price| price >= 100.0 && price <= 500.0)
190            .where_(Product::stock_r(), |&stock| stock > 10);
191        let results = results_query.order_by_float(Product::price_r());
192
193        println!("  Filtered {} products ✅", results.len());
194    }
195
196    println!("\n✅ All documentation examples compile and run successfully!");
197}
examples/join_query_builder.rs (lines 205-215)
193fn main() {
194    println!("=== Join Query Builder Demo ===\n");
195
196    let (users, orders, products) = create_sample_data();
197
198    println!("Database:");
199    println!("  Users: {}", users.len());
200    println!("  Orders: {}", orders.len());
201    println!("  Products: {}\n", products.len());
202
203    // Join 1: Inner join Users and Orders
204    println!("--- Join 1: Users with Their Orders ---");
205    let user_orders = JoinQuery::new(&users, &orders).inner_join(
206        User::id_r(),
207        Order::user_id_r(),
208        |user, order| UserOrder {
209            user_name: user.name.clone(),
210            user_email: user.email.clone(),
211            order_id: order.id,
212            quantity: order.quantity,
213            total: order.total,
214        },
215    );
216
217    for uo in &user_orders {
218        println!(
219            "  • {} - Order #{} - {} items - ${:.2}",
220            uo.user_name, uo.order_id, uo.quantity, uo.total
221        );
222    }
223    println!("Total: {} user-order pairs", user_orders.len());
224
225    // Join 2: Three-way join (Orders -> Users, Orders -> Products)
226    println!("\n--- Join 2: Complete Order Details (3-Way Join) ---");
227    
228    // First join: Orders with Users
229    let orders_with_users = JoinQuery::new(&orders, &users).inner_join(
230        Order::user_id_r(),
231        User::id_r(),
232        |order, user| (order.clone(), user.clone()),
233    );
234
235    // Second join: (Orders+Users) with Products
236    let mut order_details = Vec::new();
237    for (order, user) in &orders_with_users {
238        for product in &products {
239            if order.product_id == product.id {
240                order_details.push(OrderDetail {
241                    order_id: order.id,
242                    user_name: user.name.clone(),
243                    product_name: product.name.clone(),
244                    quantity: order.quantity,
245                    price: product.price,
246                    total: order.total,
247                });
248            }
249        }
250    }
251
252    for od in &order_details {
253        println!(
254            "  • Order #{}: {} bought {} x {} @ ${:.2} = ${:.2}",
255            od.order_id, od.user_name, od.quantity, od.product_name, od.price, od.total
256        );
257    }
258
259    // Join 3: Left join to show all users (including those without orders)
260    println!("\n--- Join 3: All Users with Order Count (Left Join) ---");
261    
262    // Use left_join to get all users with their orders (or None)
263    let user_order_pairs = JoinQuery::new(&users, &orders).left_join(
264        User::id_r(),
265        Order::user_id_r(),
266        |user, order| (user.clone(), order.map(|o| o.clone())),
267    );
268
269    // Group by user to count orders
270    let mut user_stats: HashMap<u32, (String, String, usize, f64)> = HashMap::new();
271    for (user, order) in &user_order_pairs {
272        let entry = user_stats
273            .entry(user.id)
274            .or_insert_with(|| (user.name.clone(), user.city.clone(), 0, 0.0));
275        if let Some(order) = order {
276            entry.2 += 1; // order count
277            entry.3 += order.total; // total spent
278        }
279    }
280
281    let mut user_order_stats: Vec<_> = user_stats
282        .into_iter()
283        .map(|(_, (name, city, count, total))| UserOrderCount {
284            user_name: name,
285            user_city: city,
286            order_count: count,
287            total_spent: total,
288        })
289        .collect();
290
291    user_order_stats.sort_by(|a, b| a.user_name.cmp(&b.user_name));
292
293    for stat in &user_order_stats {
294        if stat.order_count > 0 {
295            println!(
296                "  • {} ({}) - {} orders - ${:.2} total",
297                stat.user_name, stat.user_city, stat.order_count, stat.total_spent
298            );
299        } else {
300            println!("  • {} ({}) - No orders yet", stat.user_name, stat.user_city);
301        }
302    }
303
304    // Join 4: Aggregated join - Category sales analysis
305    println!("\n--- Join 4: Sales by Product Category ---");
306
307    // Join orders with products to get category information
308    let order_products = JoinQuery::new(&orders, &products).inner_join(
309        Order::product_id_r(),
310        Product::id_r(),
311        |order, product| (order.clone(), product.clone()),
312    );
313
314    // Aggregate by category
315    let mut category_stats: HashMap<String, (Vec<u32>, f64, std::collections::HashSet<u32>)> =
316        HashMap::new();
317
318    for (order, product) in &order_products {
319        let entry = category_stats
320            .entry(product.category.clone())
321            .or_insert_with(|| (Vec::new(), 0.0, std::collections::HashSet::new()));
322        entry.0.push(order.id);
323        entry.1 += order.total;
324        entry.2.insert(order.user_id);
325    }
326
327    let mut category_sales: Vec<CategorySales> = category_stats
328        .into_iter()
329        .map(|(category, (orders, revenue, customers))| CategorySales {
330            category,
331            total_orders: orders.len(),
332            total_revenue: revenue,
333            unique_customers: customers.len(),
334        })
335        .collect();
336
337    category_sales.sort_by(|a, b| {
338        b.total_revenue
339            .partial_cmp(&a.total_revenue)
340            .unwrap_or(std::cmp::Ordering::Equal)
341    });
342
343    for cs in &category_sales {
344        println!(
345            "  • {}: {} orders - ${:.2} revenue - {} customers",
346            cs.category, cs.total_orders, cs.total_revenue, cs.unique_customers
347        );
348    }
349
350    // Join 5: Filtered join - High value orders
351    println!("\n--- Join 5: High Value Orders (>$100) with User Details ---");
352    let high_value_orders = JoinQuery::new(&orders, &users).inner_join_where(
353        Order::user_id_r(),
354        User::id_r(),
355        |order, _user| order.total > 100.0,
356        |order, user| (user.name.clone(), order.id, order.total),
357    );
358
359    for (name, order_id, total) in &high_value_orders {
360        println!("  • {} - Order #{} - ${:.2}", name, order_id, total);
361    }
362
363    // Join 6: Users in same city analysis
364    println!("\n--- Join 6: Users from Same City ---");
365    let user_pairs = JoinQuery::new(&users, &users).inner_join_where(
366        User::city_r(),
367        User::city_r(),
368        |u1, u2| u1.id < u2.id, // Avoid duplicates and self-pairs
369        |u1, u2| (u1.name.clone(), u2.name.clone(), u1.city.clone()),
370    );
371
372    for (name1, name2, city) in &user_pairs {
373        println!("  • {} and {} both live in {}", name1, name2, city);
374    }
375
376    // Join 7: Product popularity
377    println!("\n--- Join 7: Product Popularity Ranking ---");
378    
379    // Join orders with products
380    let product_order_pairs = JoinQuery::new(&products, &orders).inner_join(
381        Product::id_r(),
382        Order::product_id_r(),
383        |product, order| (product.clone(), order.clone()),
384    );
385
386    // Aggregate by product
387    let mut product_sales: HashMap<u32, (String, usize, u32, f64)> = HashMap::new();
388    for (product, order) in &product_order_pairs {
389        let entry = product_sales
390            .entry(product.id)
391            .or_insert_with(|| (product.name.clone(), 0, 0, 0.0));
392        entry.1 += 1; // order count
393        entry.2 += order.quantity; // total quantity
394        entry.3 += order.total; // total revenue
395    }
396
397    let mut popularity: Vec<_> = product_sales.into_iter().collect();
398    popularity.sort_by(|a, b| b.1 .1.cmp(&a.1 .1)); // sort by order count
399
400    for (_, (name, order_count, total_qty, revenue)) in &popularity {
401        println!(
402            "  • {} - {} orders - {} units - ${:.2}",
403            name, order_count, total_qty, revenue
404        );
405    }
406
407    // Join 8: User spending by city
408    println!("\n--- Join 8: Total Spending by City ---");
409    
410    // Join users with orders to get city and spending info
411    let user_city_orders = JoinQuery::new(&users, &orders).inner_join(
412        User::id_r(),
413        Order::user_id_r(),
414        |user, order| (user.city.clone(), order.total, user.id),
415    );
416
417    // Aggregate by city
418    let mut city_spending: HashMap<String, (f64, std::collections::HashSet<u32>)> = HashMap::new();
419    for (city, total, user_id) in &user_city_orders {
420        let entry = city_spending
421            .entry(city.clone())
422            .or_insert_with(|| (0.0, std::collections::HashSet::new()));
423        entry.0 += total;
424        entry.1.insert(*user_id);
425    }
426
427    let mut city_stats: Vec<_> = city_spending
428        .into_iter()
429        .map(|(city, (total, customers))| (city, total, customers.len()))
430        .collect();
431    
432    city_stats.sort_by(|a, b| {
433        b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)
434    });
435
436    for (city, total, customer_count) in &city_stats {
437        println!(
438            "  • {} - ${:.2} total - {} customers - ${:.2} avg",
439            city,
440            total,
441            customer_count,
442            total / *customer_count as f64
443        );
444    }
445
446    // Statistics summary
447    println!("\n=== Summary Statistics ===");
448    println!("Total orders: {}", orders.len());
449    
450    let total_revenue: f64 = orders.iter().map(|o| o.total).sum();
451    println!("Total revenue: ${:.2}", total_revenue);
452    println!("Average order value: ${:.2}", total_revenue / orders.len() as f64);
453    
454    // Count unique customers using a join
455    let unique_customers: std::collections::HashSet<u32> = 
456        orders.iter().map(|o| o.user_id).collect();
457    println!("Active customers: {}", unique_customers.len());
458    println!(
459        "Average orders per customer: {:.1}",
460        orders.len() as f64 / unique_customers.len() as f64
461    );
462
463    println!("\n✓ Join query builder demo complete!");
464}
examples/sql_comparison.rs (lines 333-337)
84fn main() {
85    let employees = create_employees();
86    let departments = create_departments();
87    let projects = create_projects();
88
89    println!("\n╔════════════════════════════════════════════════════════════════════════╗");
90    println!("║     SQL vs Rust Query Builder Comparison                              ║");
91    println!("║     Demonstrating equivalent operations                               ║");
92    println!("╚════════════════════════════════════════════════════════════════════════╝");
93
94    // ============================================================================
95    // EXAMPLE 1: Simple SELECT with WHERE
96    // ============================================================================
97    print_separator("Example 1: SELECT with WHERE clause");
98    print_query(
99        "SELECT * FROM employees WHERE department = 'Engineering';",
100        "Filter employees by department"
101    );
102
103    let eng_query = Query::new(&employees)
104        .where_(Employee::department_r(), |dept| dept == "Engineering");
105    let engineering_employees = eng_query.all();
106
107    for emp in &engineering_employees {
108        println!("  ID: {}, Name: {}, Salary: ${:.2}", emp.id, emp.name, emp.salary);
109    }
110    println!("\nRust Query Builder:");
111    println!("Query::new(&employees)");
112    println!("    .where_(Employee::department_r(), |dept| dept == \"Engineering\")");
113    println!("    .all()");
114    println!("✓ Found {} employees", engineering_employees.len());
115
116    // ============================================================================
117    // EXAMPLE 2: SELECT specific columns
118    // ============================================================================
119    print_separator("Example 2: SELECT specific columns (Projection)");
120    print_query(
121        "SELECT name FROM employees WHERE salary > 80000;",
122        "Get names of high-earning employees"
123    );
124
125    let high_earners = Query::new(&employees)
126        .where_(Employee::salary_r(), |&salary| salary > 80000.0)
127        .select(Employee::name_r());
128
129    for name in &high_earners {
130        println!("  {}", name);
131    }
132    println!("\nRust Query Builder:");
133    println!("Query::new(&employees)");
134    println!("    .where_(Employee::salary_r(), |&salary| salary > 80000.0)");
135    println!("    .select(Employee::name_r())");
136    println!("✓ Found {} employees", high_earners.len());
137
138    // ============================================================================
139    // EXAMPLE 3: COUNT
140    // ============================================================================
141    print_separator("Example 3: COUNT aggregation");
142    print_query(
143        "SELECT COUNT(*) FROM employees WHERE age < 30;",
144        "Count young employees"
145    );
146
147    let young_count = Query::new(&employees)
148        .where_(Employee::age_r(), |&age| age < 30)
149        .count();
150
151    println!("  Count: {}", young_count);
152    println!("\nRust Query Builder:");
153    println!("Query::new(&employees)");
154    println!("    .where_(Employee::age_r(), |&age| age < 30)");
155    println!("    .count()");
156    println!("✓ Result: {}", young_count);
157
158    // ============================================================================
159    // EXAMPLE 4: SUM, AVG, MIN, MAX
160    // ============================================================================
161    print_separator("Example 4: Aggregate functions (SUM, AVG, MIN, MAX)");
162    print_query(
163        "SELECT \n\
164         SUM(salary) as total_salary,\n\
165         AVG(salary) as avg_salary,\n\
166         MIN(salary) as min_salary,\n\
167         MAX(salary) as max_salary\n\
168         FROM employees WHERE department = 'Engineering';",
169        "Engineering department salary statistics"
170    );
171
172    let eng_query = Query::new(&employees)
173        .where_(Employee::department_r(), |dept| dept == "Engineering");
174
175    let total = eng_query.sum(Employee::salary_r());
176    let avg = eng_query.avg(Employee::salary_r()).unwrap_or(0.0);
177    let min = eng_query.min_float(Employee::salary_r()).unwrap_or(0.0);
178    let max = eng_query.max_float(Employee::salary_r()).unwrap_or(0.0);
179
180    println!("  Total Salary: ${:.2}", total);
181    println!("  Avg Salary:   ${:.2}", avg);
182    println!("  Min Salary:   ${:.2}", min);
183    println!("  Max Salary:   ${:.2}", max);
184
185    println!("\nRust Query Builder:");
186    println!("let query = Query::new(&employees)");
187    println!("    .where_(Employee::department_r(), |dept| dept == \"Engineering\");");
188    println!("query.sum(Employee::salary_r())  // ${:.2}", total);
189    println!("query.avg(Employee::salary_r())  // ${:.2}", avg);
190    println!("query.min_float(Employee::salary_r())  // ${:.2}", min);
191    println!("query.max_float(Employee::salary_r())  // ${:.2}", max);
192
193    // ============================================================================
194    // EXAMPLE 5: GROUP BY with aggregation
195    // ============================================================================
196    print_separator("Example 5: GROUP BY with aggregation");
197    print_query(
198        "SELECT \n\
199         department,\n\
200         COUNT(*) as emp_count,\n\
201         AVG(salary) as avg_salary\n\
202         FROM employees\n\
203         GROUP BY department;",
204        "Statistics by department"
205    );
206
207    let by_dept = Query::new(&employees).group_by(Employee::department_r());
208
209    for (dept, emps) in &by_dept {
210        let dept_query = Query::new(emps);
211        let count = emps.len();
212        let avg_sal = dept_query.avg(Employee::salary_r()).unwrap_or(0.0);
213        println!("  {}: {} employees, avg salary ${:.2}", dept, count, avg_sal);
214    }
215
216    println!("\nRust Query Builder:");
217    println!("let by_dept = Query::new(&employees).group_by(Employee::department_r());");
218    println!("for (dept, emps) in &by_dept {{");
219    println!("    let dept_query = Query::new(emps);");
220    println!("    dept_query.avg(Employee::salary_r())");
221    println!("}}");
222
223    // ============================================================================
224    // EXAMPLE 6: ORDER BY
225    // ============================================================================
226    print_separator("Example 6: ORDER BY");
227    print_query(
228        "SELECT name, salary FROM employees\n\
229         WHERE department = 'Sales'\n\
230         ORDER BY salary DESC;",
231        "Sales employees ordered by salary (descending)"
232    );
233
234    let sales_sorted = Query::new(&employees)
235        .where_(Employee::department_r(), |dept| dept == "Sales")
236        .order_by_float_desc(Employee::salary_r());
237
238    for emp in &sales_sorted {
239        println!("  {}: ${:.2}", emp.name, emp.salary);
240    }
241
242    println!("\nRust Query Builder:");
243    println!("Query::new(&employees)");
244    println!("    .where_(Employee::department_r(), |dept| dept == \"Sales\")");
245    println!("    .order_by_float_desc(Employee::salary_r())");
246
247    // ============================================================================
248    // EXAMPLE 7: Multiple WHERE conditions (AND)
249    // ============================================================================
250    print_separator("Example 7: Multiple WHERE conditions (AND)");
251    print_query(
252        "SELECT * FROM employees\n\
253         WHERE salary > 70000 AND age < 35;",
254        "High-earning young employees"
255    );
256
257    let filter_query = Query::new(&employees)
258        .where_(Employee::salary_r(), |&salary| salary > 70000.0)
259        .where_(Employee::age_r(), |&age| age < 35);
260    let filtered = filter_query.all();
261
262    for emp in &filtered {
263        println!("  {}: Age {}, Salary ${:.2}", emp.name, emp.age, emp.salary);
264    }
265
266    println!("\nRust Query Builder:");
267    println!("Query::new(&employees)");
268    println!("    .where_(Employee::salary_r(), |&salary| salary > 70000.0)");
269    println!("    .where_(Employee::age_r(), |&age| age < 35)");
270    println!("    .all()");
271    println!("✓ Found {} employees", filtered.len());
272
273    // ============================================================================
274    // EXAMPLE 8: LIMIT (TOP N)
275    // ============================================================================
276    print_separator("Example 8: LIMIT / TOP N");
277    print_query(
278        "SELECT TOP 3 name, salary FROM employees\n\
279         ORDER BY salary DESC;",
280        "Top 3 highest paid employees"
281    );
282
283    let top_earners = Query::new(&employees)
284        .order_by_float_desc(Employee::salary_r());
285
286    for (i, emp) in top_earners.iter().take(3).enumerate() {
287        println!("  {}. {}: ${:.2}", i + 1, emp.name, emp.salary);
288    }
289
290    println!("\nRust Query Builder:");
291    println!("Query::new(&employees)");
292    println!("    .order_by_float_desc(Employee::salary_r())");
293    println!("    .into_iter().take(3)");
294
295    // ============================================================================
296    // EXAMPLE 9: OFFSET and LIMIT (Pagination)
297    // ============================================================================
298    print_separator("Example 9: OFFSET and LIMIT (Pagination)");
299    print_query(
300        "SELECT name FROM employees\n\
301         ORDER BY name\n\
302         LIMIT 3 OFFSET 3;",
303        "Page 2 of employee names (3 per page)"
304    );
305
306    let page_2 = Query::new(&employees)
307        .order_by(Employee::name_r())
308        .into_iter()
309        .skip(3)
310        .take(3)
311        .collect::<Vec<_>>();
312
313    for emp in &page_2 {
314        println!("  {}", emp.name);
315    }
316
317    println!("\nRust Query Builder:");
318    println!("Query::new(&employees)");
319    println!("    .order_by(Employee::name_r())");
320    println!("    .into_iter().skip(3).take(3)");
321
322    // ============================================================================
323    // EXAMPLE 10: INNER JOIN
324    // ============================================================================
325    print_separator("Example 10: INNER JOIN");
326    print_query(
327        "SELECT e.name, d.name as dept_name, d.budget\n\
328         FROM employees e\n\
329         INNER JOIN departments d ON e.department = d.name;",
330        "Employees with their department info"
331    );
332
333    let emp_dept = JoinQuery::new(&employees, &departments).inner_join(
334        Employee::department_r(),
335        Department::name_r(),
336        |emp, dept| (emp.name.clone(), dept.name.clone(), dept.budget),
337    );
338
339    for (emp_name, dept_name, budget) in emp_dept.iter().take(5) {
340        println!("  {} works in {} (Budget: ${:.0})", emp_name, dept_name, budget);
341    }
342    println!("  ... (showing first 5)");
343
344    println!("\nRust Query Builder:");
345    println!("JoinQuery::new(&employees, &departments).inner_join(");
346    println!("    Employee::department_r(),");
347    println!("    Department::name_r(),");
348    println!("    |emp, dept| (emp.name.clone(), dept.name.clone(), dept.budget)");
349    println!(")");
350    println!("✓ Found {} matches", emp_dept.len());
351
352    // ============================================================================
353    // EXAMPLE 11: GROUP BY with HAVING equivalent
354    // ============================================================================
355    print_separator("Example 11: GROUP BY with filtering (HAVING equivalent)");
356    print_query(
357        "SELECT city, COUNT(*) as emp_count, AVG(salary) as avg_salary\n\
358         FROM employees\n\
359         GROUP BY city\n\
360         HAVING COUNT(*) > 1;",
361        "Cities with multiple employees"
362    );
363
364    let by_city = Query::new(&employees).group_by(Employee::city_r());
365    let mut city_stats: Vec<_> = by_city
366        .iter()
367        .filter(|(_, emps)| emps.len() > 1) // HAVING equivalent
368        .map(|(city, emps)| {
369            let avg_sal = Query::new(emps).avg(Employee::salary_r()).unwrap_or(0.0);
370            (city.clone(), emps.len(), avg_sal)
371        })
372        .collect();
373    
374    city_stats.sort_by(|a, b| b.1.cmp(&a.1)); // Sort by count
375
376    for (city, count, avg_sal) in &city_stats {
377        println!("  {}: {} employees, avg salary ${:.2}", city, count, avg_sal);
378    }
379
380    println!("\nRust Query Builder:");
381    println!("let by_city = Query::new(&employees).group_by(Employee::city_r());");
382    println!("by_city.iter()");
383    println!("    .filter(|(_, emps)| emps.len() > 1)  // HAVING equivalent");
384    println!("    .map(|(city, emps)| {{");
385    println!("        let avg = Query::new(emps).avg(Employee::salary_r());");
386    println!("        (city, emps.len(), avg)");
387    println!("    }})");
388
389    // ============================================================================
390    // EXAMPLE 12: Complex query with multiple operations
391    // ============================================================================
392    print_separator("Example 12: Complex multi-operation query");
393    print_query(
394        "SELECT name, salary, age\n\
395         FROM employees\n\
396         WHERE department IN ('Engineering', 'Sales')\n\
397         AND salary BETWEEN 80000 AND 100000\n\
398         AND age >= 30\n\
399         ORDER BY salary DESC;",
400        "Experienced mid-to-senior level employees in core departments"
401    );
402
403    let complex_query = Query::new(&employees)
404        .where_(Employee::department_r(), |dept| dept == "Engineering" || dept == "Sales")
405        .where_(Employee::salary_r(), |&sal| sal >= 80000.0 && sal <= 100000.0)
406        .where_(Employee::age_r(), |&age| age >= 30)
407        .order_by_float_desc(Employee::salary_r());
408
409    for emp in &complex_query {
410        println!("  {}: Age {}, {} dept, ${:.2}", emp.name, emp.age, emp.department, emp.salary);
411    }
412
413    println!("\nRust Query Builder:");
414    println!("Query::new(&employees)");
415    println!("    .where_(Employee::department_r(), |dept| dept == \"Engineering\" || dept == \"Sales\")");
416    println!("    .where_(Employee::salary_r(), |&sal| sal >= 80000.0 && sal <= 100000.0)");
417    println!("    .where_(Employee::age_r(), |&age| age >= 30)");
418    println!("    .order_by_float_desc(Employee::salary_r())");
419    println!("✓ Found {} employees", complex_query.len());
420
421    // ============================================================================
422    // EXAMPLE 13: Three-table JOIN
423    // ============================================================================
424    print_separator("Example 13: Three-table JOIN");
425    print_query(
426        "SELECT p.name as project, d.name as department, d.location\n\
427         FROM projects p\n\
428         INNER JOIN departments d ON p.department_id = d.id;",
429        "Projects with their department details"
430    );
431
432    let proj_dept = JoinQuery::new(&projects, &departments).inner_join(
433        Project::department_id_r(),
434        Department::id_r(),
435        |proj, dept| {
436            (proj.name.clone(), dept.name.clone(), dept.location.clone(), proj.budget)
437        },
438    );
439
440    for (proj_name, dept_name, location, budget) in &proj_dept {
441        println!("  {}: {} dept in {} (${:.0})", proj_name, dept_name, location, budget);
442    }
443
444    println!("\nRust Query Builder:");
445    println!("JoinQuery::new(&projects, &departments).inner_join(");
446    println!("    Project::department_id_r(),");
447    println!("    Department::id_r(),");
448    println!("    |proj, dept| (proj.name, dept.name, dept.location, proj.budget)");
449    println!(")");
450
451    // ============================================================================
452    // EXAMPLE 14: Subquery equivalent (using intermediate results)
453    // ============================================================================
454    print_separator("Example 14: Subquery equivalent");
455    print_query(
456        "SELECT * FROM employees\n\
457         WHERE salary > (SELECT AVG(salary) FROM employees);",
458        "Employees earning above average"
459    );
460
461    let avg_salary = Query::new(&employees)
462        .avg(Employee::salary_r())
463        .unwrap_or(0.0);
464
465    let above_avg_query = Query::new(&employees)
466        .where_(Employee::salary_r(), move |&sal| sal > avg_salary);
467    let above_avg = above_avg_query.all();
468
469    println!("  Average salary: ${:.2}", avg_salary);
470    for emp in &above_avg {
471        println!("  {}: ${:.2} ({:.1}% above average)", 
472            emp.name, emp.salary, ((emp.salary - avg_salary) / avg_salary * 100.0));
473    }
474
475    println!("\nRust Query Builder:");
476    println!("let avg = Query::new(&employees).avg(Employee::salary_r()).unwrap_or(0.0);");
477    println!("Query::new(&employees)");
478    println!("    .where_(Employee::salary_r(), |&sal| sal > avg)");
479    println!("    .all()");
480    println!("✓ Found {} employees above average", above_avg.len());
481
482    // ============================================================================
483    // EXAMPLE 15: Advanced aggregation (revenue calculation)
484    // ============================================================================
485    print_separator("Example 15: Advanced aggregation");
486    print_query(
487        "SELECT \n\
488         d.name,\n\
489         d.budget as dept_budget,\n\
490         SUM(p.budget) as total_project_budget,\n\
491         (d.budget - SUM(p.budget)) as remaining_budget\n\
492         FROM departments d\n\
493         LEFT JOIN projects p ON d.id = p.department_id\n\
494         GROUP BY d.name, d.budget;",
495        "Department budget utilization"
496    );
497
498    // Join departments with projects
499    let dept_projects = JoinQuery::new(&departments, &projects).left_join(
500        Department::id_r(),
501        Project::department_id_r(),
502        |dept, proj| (dept.clone(), proj.map(|p| p.clone())),
503    );
504
505    // Aggregate by department
506    let mut dept_budget_map: HashMap<String, (f64, Vec<f64>)> = HashMap::new();
507    for (dept, proj) in dept_projects {
508        let entry = dept_budget_map
509            .entry(dept.name.clone())
510            .or_insert((dept.budget, Vec::new()));
511        if let Some(p) = proj {
512            entry.1.push(p.budget);
513        }
514    }
515
516    for (dept_name, (total_budget, project_budgets)) in dept_budget_map.iter() {
517        let used: f64 = project_budgets.iter().sum();
518        let remaining = total_budget - used;
519        println!("  {}: Budget ${:.0}, Used ${:.0}, Remaining ${:.0} ({:.1}% utilized)",
520            dept_name, total_budget, used, remaining, (used / total_budget * 100.0));
521    }
522
523    println!("\nRust Query Builder:");
524    println!("let dept_projects = JoinQuery::new(&departments, &projects)");
525    println!("    .left_join(Department::id_r(), Project::department_id_r(), ...)");
526    println!("// Then aggregate using HashMap");
527
528    // ============================================================================
529    // Summary
530    // ============================================================================
531    print_separator("Summary");
532    println!("\n✓ All 15 SQL queries successfully replicated with Rust Query Builder!");
533    println!("\nDemonstrated equivalents for:");
534    println!("  • SELECT with WHERE");
535    println!("  • Projection (SELECT specific columns)");
536    println!("  • Aggregations (COUNT, SUM, AVG, MIN, MAX)");
537    println!("  • GROUP BY");
538    println!("  • ORDER BY");
539    println!("  • LIMIT and OFFSET");
540    println!("  • INNER JOIN and LEFT JOIN");
541    println!("  • Complex conditions (AND, OR, BETWEEN)");
542    println!("  • Subqueries");
543    println!("  • Multi-table operations");
544    println!("\n🎯 Type-safe, compile-time checked, and zero runtime overhead!");
545    println!();
546}
examples/memory_safety_verification.rs (lines 253-257)
98fn main() {
99    println!("\n╔═══════════════════════════════════════════════════════════════╗");
100    println!("║  Memory Safety Verification                                   ║");
101    println!("║  Proving 'static doesn't cause memory leaks                   ║");
102    println!("╚═══════════════════════════════════════════════════════════════╝\n");
103
104    println!("🔍 Understanding 'static:\n");
105    println!("  • T: 'static means: Type T doesn't contain non-'static references");
106    println!("  • It does NOT mean: Data lives for entire program");
107    println!("  • It's needed for: Storing types in trait objects");
108    println!("  • Safety: Compiler ensures no dangling references\n");
109
110    // ============================================================================
111    // TEST 1: Basic Query - Verify Cleanup
112    // ============================================================================
113    println!("═══════════════════════════════════════════════════════════════");
114    println!("Test 1: Basic WHERE query - verify all data is dropped");
115    println!("═══════════════════════════════════════════════════════════════\n");
116
117    reset_stats();
118    {
119        let employees = vec![
120            create_employee(1, "Alice", "Engineering", 95000.0),
121            create_employee(2, "Bob", "Engineering", 87000.0),
122            create_employee(3, "Carol", "Sales", 75000.0),
123        ];
124        print_memory_status("After creating employees");
125
126        {
127            let query = Query::new(&employees)
128                .where_(Employee::department_r(), |dept| dept == "Engineering");
129            let results = query.all();
130            
131            println!("  Found {} engineering employees", results.len());
132            print_memory_status("During query execution");
133        }
134        
135        print_memory_status("After query scope ends");
136    }
137    
138    print_memory_status("After employees scope ends");
139    println!();
140
141    // ============================================================================
142    // TEST 2: Multiple Queries - No Accumulation
143    // ============================================================================
144    println!("═══════════════════════════════════════════════════════════════");
145    println!("Test 2: Multiple queries - verify no memory accumulation");
146    println!("═══════════════════════════════════════════════════════════════\n");
147
148    reset_stats();
149    {
150        let employees = vec![
151            create_employee(1, "Alice", "Engineering", 95000.0),
152            create_employee(2, "Bob", "Sales", 75000.0),
153        ];
154
155        let initial_stats = get_stats();
156        println!("  Initial allocations: {}", initial_stats.0);
157
158        // Run 10 queries
159        for i in 1..=10 {
160            let query = Query::new(&employees)
161                .where_(Employee::salary_r(), |&s| s > 70000.0);
162            let _results = query.all();
163            
164            if i % 3 == 0 {
165                let (allocs, drops) = get_stats();
166                println!("  After {} queries - Allocated: {}, Dropped: {}", i, allocs, drops);
167            }
168        }
169
170        let final_stats = get_stats();
171        println!("\n  After 10 queries:");
172        println!("    Allocations: {} (should be same as initial)", final_stats.0);
173        println!("    ✅ No memory accumulation from queries!");
174    }
175
176    print_memory_status("After all queries and employees dropped");
177    println!();
178
179    // ============================================================================
180    // TEST 3: ORDER BY (requires Clone) - Track Cloning
181    // ============================================================================
182    println!("═══════════════════════════════════════════════════════════════");
183    println!("Test 3: ORDER BY (with Clone) - verify controlled cloning");
184    println!("═══════════════════════════════════════════════════════════════\n");
185
186    reset_stats();
187    {
188        let employees = vec![
189            create_employee(1, "Alice", "Engineering", 95000.0),
190            create_employee(2, "Bob", "Sales", 87000.0),
191            create_employee(3, "Carol", "Marketing", 75000.0),
192        ];
193        
194        let (before_allocs, _) = get_stats();
195        println!("  Before sorting: {} allocations", before_allocs);
196
197        {
198            let sorted = Query::new(&employees)
199                .order_by_float_desc(Employee::salary_r());
200            
201            let (after_allocs, _) = get_stats();
202            println!("  After sorting: {} allocations", after_allocs);
203            println!("  Cloned items: {} (expected: {})", after_allocs - before_allocs, employees.len());
204            println!("  Sorted {} employees by salary", sorted.len());
205            
206            // sorted goes out of scope here
207        }
208        
209        print_memory_status("After sorted results dropped");
210    }
211    
212    print_memory_status("After employees dropped");
213    println!();
214
215    // ============================================================================
216    // TEST 4: JOIN Operations - No Leaks
217    // ============================================================================
218    println!("═══════════════════════════════════════════════════════════════");
219    println!("Test 4: JOIN operations - verify no memory leaks");
220    println!("═══════════════════════════════════════════════════════════════\n");
221
222    #[derive(Keypaths)]
223    struct Department {
224        id: u32,
225        name: String,
226        drop_tracker: DropTracker,
227    }
228
229    reset_stats();
230    {
231        let employees = vec![
232            create_employee(1, "Alice", "Engineering", 95000.0),
233            create_employee(2, "Bob", "Sales", 87000.0),
234        ];
235
236        let departments = vec![
237            Department {
238                id: 1,
239                name: "Engineering".to_string(),
240                drop_tracker: DropTracker::new(),
241            },
242            Department {
243                id: 2,
244                name: "Sales".to_string(),
245                drop_tracker: DropTracker::new(),
246            },
247        ];
248
249        print_memory_status("After creating data");
250
251        {
252            let results = JoinQuery::new(&employees, &departments)
253                .inner_join(
254                    Employee::department_r(),
255                    Department::name_r(),
256                    |emp, dept| (emp.name.clone(), dept.name.clone()),
257                );
258            
259            println!("  Joined {} pairs", results.len());
260            print_memory_status("During join results");
261        }
262
263        print_memory_status("After join results dropped");
264    }
265
266    print_memory_status("After all data dropped");
267    println!();
268
269    // ============================================================================
270    // TEST 5: Large Scale - Memory Behavior
271    // ============================================================================
272    println!("═══════════════════════════════════════════════════════════════");
273    println!("Test 5: Large scale (1000 items) - verify cleanup");
274    println!("═══════════════════════════════════════════════════════════════\n");
275
276    reset_stats();
277    {
278        let mut large_dataset = Vec::new();
279        for i in 0..1000 {
280            large_dataset.push(create_employee(
281                i,
282                &format!("Employee {}", i),
283                if i % 3 == 0 { "Engineering" } else if i % 3 == 1 { "Sales" } else { "Marketing" },
284                50000.0 + (i as f64 * 100.0),
285            ));
286        }
287
288        let (initial_allocs, _) = get_stats();
289        println!("  Created 1000 employees: {} allocations (~10MB)", initial_allocs);
290
291        // Run complex query
292        {
293            let query = Query::new(&large_dataset)
294                .where_(Employee::salary_r(), |&s| s > 80000.0)
295                .where_(Employee::department_r(), |d| d == "Engineering");
296            let results = query.all();
297            
298            println!("  Filtered to {} employees", results.len());
299            
300            let (during_allocs, _) = get_stats();
301            let extra = during_allocs - initial_allocs;
302            println!("  Extra allocations during query: {} (should be 0)", extra);
303            
304            if extra == 0 {
305                println!("  ✅ Zero-copy filtering confirmed!");
306            }
307        }
308
309        print_memory_status("After query results dropped");
310    }
311
312    print_memory_status("After 1000 employees dropped");
313    println!();
314
315    // ============================================================================
316    // TEST 6: Explanation of 'static
317    // ============================================================================
318    println!("═══════════════════════════════════════════════════════════════");
319    println!("Explanation: Why 'static is safe and doesn't leak");
320    println!("═══════════════════════════════════════════════════════════════\n");
321
322    println!("❓ What does T: 'static mean?\n");
323    println!("  WRONG ❌: \"T lives for the entire program\"");
324    println!("  RIGHT ✅: \"T doesn't contain non-'static references\"\n");
325
326    println!("Examples:\n");
327    println!("  struct OwnedData {{          // T: 'static ✅");
328    println!("      id: u32,                 // Owned data");
329    println!("      name: String,            // Owned data");
330    println!("  }}");
331    println!();
332    println!("  struct WithReference<'a> {{  // NOT 'static ❌");
333    println!("      data: &'a String,        // Contains reference");
334    println!("  }}");
335    println!();
336
337    println!("Why we use T: 'static:\n");
338    println!("  1. Store type in trait objects: Box<dyn Fn(&T) -> bool>");
339    println!("  2. Prevent dangling references in closures");
340    println!("  3. Ensure type safety at compile time");
341    println!();
342
343    println!("Lifetime of data:\n");
344    println!("  • Data is owned by your Vec<T>");
345    println!("  • Query just borrows &'a [T]");
346    println!("  • When Vec<T> is dropped, all T are dropped");
347    println!("  • No memory leaks possible!\n");
348
349    // ============================================================================
350    // TEST 7: Drop Order Verification
351    // ============================================================================
352    println!("═══════════════════════════════════════════════════════════════");
353    println!("Test 7: Drop order - verify proper RAII");
354    println!("═══════════════════════════════════════════════════════════════\n");
355
356    reset_stats();
357    
358    println!("Creating scoped data...");
359    {
360        let employees = vec![
361            create_employee(1, "Alice", "Engineering", 95000.0),
362            create_employee(2, "Bob", "Sales", 87000.0),
363        ];
364        println!("  Created 2 employees");
365
366        {
367            println!("  Creating query...");
368            let query = Query::new(&employees)
369                .where_(Employee::department_r(), |dept| dept == "Engineering");
370            
371            {
372                println!("  Executing query...");
373                let results = query.all();
374                println!("    Found {} results", results.len());
375                println!("  Query results going out of scope...");
376            }
377            println!("  Results dropped (just Vec<&Employee>, no Employee drops)");
378            
379            println!("  Query going out of scope...");
380        }
381        println!("  Query dropped (just filters, no Employee drops)");
382        
383        println!("  Employees vector going out of scope...");
384    }
385    println!("  Employees dropped - NOW Employees are freed!\n");
386    
387    let (allocs, drops) = get_stats();
388    println!("Final stats:");
389    println!("  Allocated: {}", allocs);
390    println!("  Dropped: {}", drops);
391    println!("  Leaked: {}", allocs - drops);
392    
393    if allocs == drops {
394        println!("\n✅ Perfect! All allocated memory was freed!");
395    } else {
396        println!("\n❌ Memory leak detected!");
397    }
398
399    // ============================================================================
400    // TEST 8: Arc/Rc Compatibility
401    // ============================================================================
402    println!("\n═══════════════════════════════════════════════════════════════");
403    println!("Test 8: Arc/Rc compatibility - shared ownership works");
404    println!("═══════════════════════════════════════════════════════════════\n");
405
406    {
407        use std::sync::Arc;
408        
409        #[derive(Keypaths)]
410        struct SharedData {
411            id: u32,
412            value: Arc<String>,  // Shared ownership
413        }
414
415        let shared_string = Arc::new("Shared Value".to_string());
416        println!("  Arc strong count: {}", Arc::strong_count(&shared_string));
417
418        let data = vec![
419            SharedData { id: 1, value: Arc::clone(&shared_string) },
420            SharedData { id: 2, value: Arc::clone(&shared_string) },
421        ];
422        
423        println!("  Arc strong count after creating data: {}", Arc::strong_count(&shared_string));
424
425        {
426            let query = Query::new(&data)
427                .where_(SharedData::id_r(), |&id| id > 0);
428            let results = query.all();
429            println!("  Found {} items", results.len());
430            println!("  Arc strong count during query: {}", Arc::strong_count(&shared_string));
431        }
432
433        println!("  Arc strong count after query: {}", Arc::strong_count(&shared_string));
434    }
435    
436    println!("  ✅ Arc reference counting works correctly!\n");
437
438    // ============================================================================
439    // TEST 9: Large Data Without Clone - Zero Copy
440    // ============================================================================
441    println!("═══════════════════════════════════════════════════════════════");
442    println!("Test 9: Large data without Clone - verify zero-copy");
443    println!("═══════════════════════════════════════════════════════════════\n");
444
445    #[derive(Keypaths)]  // NO Clone!
446    struct LargeRecord {
447        id: u32,
448        // Simulate 1MB of data that we DON'T want to clone
449        huge_data: Vec<u8>,
450    }
451
452    {
453        println!("  Creating 10 records (1MB each = 10MB total)...");
454        let large_records: Vec<LargeRecord> = (0..10)
455            .map(|i| LargeRecord {
456                id: i,
457                huge_data: vec![i as u8; 1_000_000], // 1MB each
458            })
459            .collect();
460
461        println!("  Total memory: ~10MB");
462
463        {
464            println!("\n  Running query without Clone...");
465            let query = Query::new(&large_records)
466                .where_(LargeRecord::id_r(), |&id| id < 5);
467            let results = query.all();  // Vec<&LargeRecord> - NO CLONING!
468            
469            println!("  Found {} records", results.len());
470            println!("  Memory copied: 0 bytes (just references)");
471            println!("  ✅ Zero-copy achieved!");
472        }
473
474        println!("\n  Query dropped - no memory freed (no cloning happened)");
475    }
476    
477    println!("  Records dropped - 10MB freed\n");
478
479    // ============================================================================
480    // TEST 10: Lifetime Safety
481    // ============================================================================
482    println!("═══════════════════════════════════════════════════════════════");
483    println!("Test 10: Lifetime safety - compiler prevents dangling refs");
484    println!("═══════════════════════════════════════════════════════════════\n");
485
486    println!("  The following code WILL NOT COMPILE (by design):\n");
487    println!("  ```rust");
488    println!("  let query;");
489    println!("  {{");
490    println!("      let data = vec![...];");
491    println!("      query = Query::new(&data);  // data borrowed here");
492    println!("  }}  // data dropped");
493    println!("  let results = query.all();  // ❌ ERROR: data doesn't live long enough");
494    println!("  ```\n");
495    println!("  ✅ Rust's borrow checker prevents use-after-free!");
496    println!("  ✅ 'static bound + lifetimes = memory safety guaranteed!\n");
497
498    // ============================================================================
499    // Summary
500    // ============================================================================
501    println!("═══════════════════════════════════════════════════════════════");
502    println!("Summary: Memory Safety Guarantees");
503    println!("═══════════════════════════════════════════════════════════════\n");
504
505    let (total_allocs, total_drops) = get_stats();
506    let leaked = total_allocs.saturating_sub(total_drops);
507
508    println!("Overall Statistics:");
509    println!("  Total allocations: {}", total_allocs);
510    println!("  Total drops: {}", total_drops);
511    println!("  Memory leaks: {}", leaked);
512
513    if leaked == 0 {
514        println!("\n🎉 VERIFIED: Zero memory leaks!\n");
515    } else {
516        println!("\n⚠️  WARNING: Potential memory leak detected!\n");
517    }
518
519    println!("Guarantees Verified:");
520    println!("  ✅ 'static doesn't cause data to live forever");
521    println!("  ✅ All allocated memory is properly freed");
522    println!("  ✅ No memory leaks from queries");
523    println!("  ✅ Query only holds references, not ownership");
524    println!("  ✅ Rust's borrow checker prevents dangling references");
525    println!("  ✅ RAII ensures proper cleanup");
526    println!("  ✅ Zero-copy operations don't allocate");
527    println!("  ✅ Clone operations are explicit and controlled\n");
528
529    println!("Performance Benefits:");
530    println!("  ✅ Filtering: 0 bytes copied (v0.2.0) vs 10MB (v0.1.0)");
531    println!("  ✅ Counting: 0 bytes copied");
532    println!("  ✅ Aggregations: 0 bytes copied");
533    println!("  ✅ Only ordering/grouping clone when needed\n");
534
535    println!("Safety Guarantees:");
536    println!("  ✅ Compile-time prevention of dangling references");
537    println!("  ✅ No use-after-free possible");
538    println!("  ✅ No double-free possible");
539    println!("  ✅ Automatic cleanup via RAII\n");
540
541    println!("✓ All memory safety tests PASSED!\n");
542}
Source

pub fn left_join<K, O, F>( &self, left_key: KeyPaths<L, K>, right_key: KeyPaths<R, K>, mapper: F, ) -> Vec<O>
where K: Eq + Hash + Clone + 'static, F: Fn(&L, Option<&R>) -> O,

Performs a left join between two collections.

Returns all items from the left collection with optional matching items from the right collection. If no match is found, the right item is None.

§Arguments
  • left_key - Key-path to the join field in the left collection
  • right_key - Key-path to the join field in the right collection
  • mapper - Function to transform pairs into the result type (right item may be None)
§Example
let results = JoinQuery::new(&users, &orders)
    .left_join(
        User::id_r(),
        Order::user_id_r(),
        |user, order| match order {
            Some(o) => format!("{} has order {}", user.name, o.id),
            None => format!("{} has no orders", user.name),
        }
    );
Examples found in repository?
examples/join_query_builder.rs (lines 263-267)
193fn main() {
194    println!("=== Join Query Builder Demo ===\n");
195
196    let (users, orders, products) = create_sample_data();
197
198    println!("Database:");
199    println!("  Users: {}", users.len());
200    println!("  Orders: {}", orders.len());
201    println!("  Products: {}\n", products.len());
202
203    // Join 1: Inner join Users and Orders
204    println!("--- Join 1: Users with Their Orders ---");
205    let user_orders = JoinQuery::new(&users, &orders).inner_join(
206        User::id_r(),
207        Order::user_id_r(),
208        |user, order| UserOrder {
209            user_name: user.name.clone(),
210            user_email: user.email.clone(),
211            order_id: order.id,
212            quantity: order.quantity,
213            total: order.total,
214        },
215    );
216
217    for uo in &user_orders {
218        println!(
219            "  • {} - Order #{} - {} items - ${:.2}",
220            uo.user_name, uo.order_id, uo.quantity, uo.total
221        );
222    }
223    println!("Total: {} user-order pairs", user_orders.len());
224
225    // Join 2: Three-way join (Orders -> Users, Orders -> Products)
226    println!("\n--- Join 2: Complete Order Details (3-Way Join) ---");
227    
228    // First join: Orders with Users
229    let orders_with_users = JoinQuery::new(&orders, &users).inner_join(
230        Order::user_id_r(),
231        User::id_r(),
232        |order, user| (order.clone(), user.clone()),
233    );
234
235    // Second join: (Orders+Users) with Products
236    let mut order_details = Vec::new();
237    for (order, user) in &orders_with_users {
238        for product in &products {
239            if order.product_id == product.id {
240                order_details.push(OrderDetail {
241                    order_id: order.id,
242                    user_name: user.name.clone(),
243                    product_name: product.name.clone(),
244                    quantity: order.quantity,
245                    price: product.price,
246                    total: order.total,
247                });
248            }
249        }
250    }
251
252    for od in &order_details {
253        println!(
254            "  • Order #{}: {} bought {} x {} @ ${:.2} = ${:.2}",
255            od.order_id, od.user_name, od.quantity, od.product_name, od.price, od.total
256        );
257    }
258
259    // Join 3: Left join to show all users (including those without orders)
260    println!("\n--- Join 3: All Users with Order Count (Left Join) ---");
261    
262    // Use left_join to get all users with their orders (or None)
263    let user_order_pairs = JoinQuery::new(&users, &orders).left_join(
264        User::id_r(),
265        Order::user_id_r(),
266        |user, order| (user.clone(), order.map(|o| o.clone())),
267    );
268
269    // Group by user to count orders
270    let mut user_stats: HashMap<u32, (String, String, usize, f64)> = HashMap::new();
271    for (user, order) in &user_order_pairs {
272        let entry = user_stats
273            .entry(user.id)
274            .or_insert_with(|| (user.name.clone(), user.city.clone(), 0, 0.0));
275        if let Some(order) = order {
276            entry.2 += 1; // order count
277            entry.3 += order.total; // total spent
278        }
279    }
280
281    let mut user_order_stats: Vec<_> = user_stats
282        .into_iter()
283        .map(|(_, (name, city, count, total))| UserOrderCount {
284            user_name: name,
285            user_city: city,
286            order_count: count,
287            total_spent: total,
288        })
289        .collect();
290
291    user_order_stats.sort_by(|a, b| a.user_name.cmp(&b.user_name));
292
293    for stat in &user_order_stats {
294        if stat.order_count > 0 {
295            println!(
296                "  • {} ({}) - {} orders - ${:.2} total",
297                stat.user_name, stat.user_city, stat.order_count, stat.total_spent
298            );
299        } else {
300            println!("  • {} ({}) - No orders yet", stat.user_name, stat.user_city);
301        }
302    }
303
304    // Join 4: Aggregated join - Category sales analysis
305    println!("\n--- Join 4: Sales by Product Category ---");
306
307    // Join orders with products to get category information
308    let order_products = JoinQuery::new(&orders, &products).inner_join(
309        Order::product_id_r(),
310        Product::id_r(),
311        |order, product| (order.clone(), product.clone()),
312    );
313
314    // Aggregate by category
315    let mut category_stats: HashMap<String, (Vec<u32>, f64, std::collections::HashSet<u32>)> =
316        HashMap::new();
317
318    for (order, product) in &order_products {
319        let entry = category_stats
320            .entry(product.category.clone())
321            .or_insert_with(|| (Vec::new(), 0.0, std::collections::HashSet::new()));
322        entry.0.push(order.id);
323        entry.1 += order.total;
324        entry.2.insert(order.user_id);
325    }
326
327    let mut category_sales: Vec<CategorySales> = category_stats
328        .into_iter()
329        .map(|(category, (orders, revenue, customers))| CategorySales {
330            category,
331            total_orders: orders.len(),
332            total_revenue: revenue,
333            unique_customers: customers.len(),
334        })
335        .collect();
336
337    category_sales.sort_by(|a, b| {
338        b.total_revenue
339            .partial_cmp(&a.total_revenue)
340            .unwrap_or(std::cmp::Ordering::Equal)
341    });
342
343    for cs in &category_sales {
344        println!(
345            "  • {}: {} orders - ${:.2} revenue - {} customers",
346            cs.category, cs.total_orders, cs.total_revenue, cs.unique_customers
347        );
348    }
349
350    // Join 5: Filtered join - High value orders
351    println!("\n--- Join 5: High Value Orders (>$100) with User Details ---");
352    let high_value_orders = JoinQuery::new(&orders, &users).inner_join_where(
353        Order::user_id_r(),
354        User::id_r(),
355        |order, _user| order.total > 100.0,
356        |order, user| (user.name.clone(), order.id, order.total),
357    );
358
359    for (name, order_id, total) in &high_value_orders {
360        println!("  • {} - Order #{} - ${:.2}", name, order_id, total);
361    }
362
363    // Join 6: Users in same city analysis
364    println!("\n--- Join 6: Users from Same City ---");
365    let user_pairs = JoinQuery::new(&users, &users).inner_join_where(
366        User::city_r(),
367        User::city_r(),
368        |u1, u2| u1.id < u2.id, // Avoid duplicates and self-pairs
369        |u1, u2| (u1.name.clone(), u2.name.clone(), u1.city.clone()),
370    );
371
372    for (name1, name2, city) in &user_pairs {
373        println!("  • {} and {} both live in {}", name1, name2, city);
374    }
375
376    // Join 7: Product popularity
377    println!("\n--- Join 7: Product Popularity Ranking ---");
378    
379    // Join orders with products
380    let product_order_pairs = JoinQuery::new(&products, &orders).inner_join(
381        Product::id_r(),
382        Order::product_id_r(),
383        |product, order| (product.clone(), order.clone()),
384    );
385
386    // Aggregate by product
387    let mut product_sales: HashMap<u32, (String, usize, u32, f64)> = HashMap::new();
388    for (product, order) in &product_order_pairs {
389        let entry = product_sales
390            .entry(product.id)
391            .or_insert_with(|| (product.name.clone(), 0, 0, 0.0));
392        entry.1 += 1; // order count
393        entry.2 += order.quantity; // total quantity
394        entry.3 += order.total; // total revenue
395    }
396
397    let mut popularity: Vec<_> = product_sales.into_iter().collect();
398    popularity.sort_by(|a, b| b.1 .1.cmp(&a.1 .1)); // sort by order count
399
400    for (_, (name, order_count, total_qty, revenue)) in &popularity {
401        println!(
402            "  • {} - {} orders - {} units - ${:.2}",
403            name, order_count, total_qty, revenue
404        );
405    }
406
407    // Join 8: User spending by city
408    println!("\n--- Join 8: Total Spending by City ---");
409    
410    // Join users with orders to get city and spending info
411    let user_city_orders = JoinQuery::new(&users, &orders).inner_join(
412        User::id_r(),
413        Order::user_id_r(),
414        |user, order| (user.city.clone(), order.total, user.id),
415    );
416
417    // Aggregate by city
418    let mut city_spending: HashMap<String, (f64, std::collections::HashSet<u32>)> = HashMap::new();
419    for (city, total, user_id) in &user_city_orders {
420        let entry = city_spending
421            .entry(city.clone())
422            .or_insert_with(|| (0.0, std::collections::HashSet::new()));
423        entry.0 += total;
424        entry.1.insert(*user_id);
425    }
426
427    let mut city_stats: Vec<_> = city_spending
428        .into_iter()
429        .map(|(city, (total, customers))| (city, total, customers.len()))
430        .collect();
431    
432    city_stats.sort_by(|a, b| {
433        b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)
434    });
435
436    for (city, total, customer_count) in &city_stats {
437        println!(
438            "  • {} - ${:.2} total - {} customers - ${:.2} avg",
439            city,
440            total,
441            customer_count,
442            total / *customer_count as f64
443        );
444    }
445
446    // Statistics summary
447    println!("\n=== Summary Statistics ===");
448    println!("Total orders: {}", orders.len());
449    
450    let total_revenue: f64 = orders.iter().map(|o| o.total).sum();
451    println!("Total revenue: ${:.2}", total_revenue);
452    println!("Average order value: ${:.2}", total_revenue / orders.len() as f64);
453    
454    // Count unique customers using a join
455    let unique_customers: std::collections::HashSet<u32> = 
456        orders.iter().map(|o| o.user_id).collect();
457    println!("Active customers: {}", unique_customers.len());
458    println!(
459        "Average orders per customer: {:.1}",
460        orders.len() as f64 / unique_customers.len() as f64
461    );
462
463    println!("\n✓ Join query builder demo complete!");
464}
More examples
Hide additional examples
examples/sql_comparison.rs (lines 499-503)
84fn main() {
85    let employees = create_employees();
86    let departments = create_departments();
87    let projects = create_projects();
88
89    println!("\n╔════════════════════════════════════════════════════════════════════════╗");
90    println!("║     SQL vs Rust Query Builder Comparison                              ║");
91    println!("║     Demonstrating equivalent operations                               ║");
92    println!("╚════════════════════════════════════════════════════════════════════════╝");
93
94    // ============================================================================
95    // EXAMPLE 1: Simple SELECT with WHERE
96    // ============================================================================
97    print_separator("Example 1: SELECT with WHERE clause");
98    print_query(
99        "SELECT * FROM employees WHERE department = 'Engineering';",
100        "Filter employees by department"
101    );
102
103    let eng_query = Query::new(&employees)
104        .where_(Employee::department_r(), |dept| dept == "Engineering");
105    let engineering_employees = eng_query.all();
106
107    for emp in &engineering_employees {
108        println!("  ID: {}, Name: {}, Salary: ${:.2}", emp.id, emp.name, emp.salary);
109    }
110    println!("\nRust Query Builder:");
111    println!("Query::new(&employees)");
112    println!("    .where_(Employee::department_r(), |dept| dept == \"Engineering\")");
113    println!("    .all()");
114    println!("✓ Found {} employees", engineering_employees.len());
115
116    // ============================================================================
117    // EXAMPLE 2: SELECT specific columns
118    // ============================================================================
119    print_separator("Example 2: SELECT specific columns (Projection)");
120    print_query(
121        "SELECT name FROM employees WHERE salary > 80000;",
122        "Get names of high-earning employees"
123    );
124
125    let high_earners = Query::new(&employees)
126        .where_(Employee::salary_r(), |&salary| salary > 80000.0)
127        .select(Employee::name_r());
128
129    for name in &high_earners {
130        println!("  {}", name);
131    }
132    println!("\nRust Query Builder:");
133    println!("Query::new(&employees)");
134    println!("    .where_(Employee::salary_r(), |&salary| salary > 80000.0)");
135    println!("    .select(Employee::name_r())");
136    println!("✓ Found {} employees", high_earners.len());
137
138    // ============================================================================
139    // EXAMPLE 3: COUNT
140    // ============================================================================
141    print_separator("Example 3: COUNT aggregation");
142    print_query(
143        "SELECT COUNT(*) FROM employees WHERE age < 30;",
144        "Count young employees"
145    );
146
147    let young_count = Query::new(&employees)
148        .where_(Employee::age_r(), |&age| age < 30)
149        .count();
150
151    println!("  Count: {}", young_count);
152    println!("\nRust Query Builder:");
153    println!("Query::new(&employees)");
154    println!("    .where_(Employee::age_r(), |&age| age < 30)");
155    println!("    .count()");
156    println!("✓ Result: {}", young_count);
157
158    // ============================================================================
159    // EXAMPLE 4: SUM, AVG, MIN, MAX
160    // ============================================================================
161    print_separator("Example 4: Aggregate functions (SUM, AVG, MIN, MAX)");
162    print_query(
163        "SELECT \n\
164         SUM(salary) as total_salary,\n\
165         AVG(salary) as avg_salary,\n\
166         MIN(salary) as min_salary,\n\
167         MAX(salary) as max_salary\n\
168         FROM employees WHERE department = 'Engineering';",
169        "Engineering department salary statistics"
170    );
171
172    let eng_query = Query::new(&employees)
173        .where_(Employee::department_r(), |dept| dept == "Engineering");
174
175    let total = eng_query.sum(Employee::salary_r());
176    let avg = eng_query.avg(Employee::salary_r()).unwrap_or(0.0);
177    let min = eng_query.min_float(Employee::salary_r()).unwrap_or(0.0);
178    let max = eng_query.max_float(Employee::salary_r()).unwrap_or(0.0);
179
180    println!("  Total Salary: ${:.2}", total);
181    println!("  Avg Salary:   ${:.2}", avg);
182    println!("  Min Salary:   ${:.2}", min);
183    println!("  Max Salary:   ${:.2}", max);
184
185    println!("\nRust Query Builder:");
186    println!("let query = Query::new(&employees)");
187    println!("    .where_(Employee::department_r(), |dept| dept == \"Engineering\");");
188    println!("query.sum(Employee::salary_r())  // ${:.2}", total);
189    println!("query.avg(Employee::salary_r())  // ${:.2}", avg);
190    println!("query.min_float(Employee::salary_r())  // ${:.2}", min);
191    println!("query.max_float(Employee::salary_r())  // ${:.2}", max);
192
193    // ============================================================================
194    // EXAMPLE 5: GROUP BY with aggregation
195    // ============================================================================
196    print_separator("Example 5: GROUP BY with aggregation");
197    print_query(
198        "SELECT \n\
199         department,\n\
200         COUNT(*) as emp_count,\n\
201         AVG(salary) as avg_salary\n\
202         FROM employees\n\
203         GROUP BY department;",
204        "Statistics by department"
205    );
206
207    let by_dept = Query::new(&employees).group_by(Employee::department_r());
208
209    for (dept, emps) in &by_dept {
210        let dept_query = Query::new(emps);
211        let count = emps.len();
212        let avg_sal = dept_query.avg(Employee::salary_r()).unwrap_or(0.0);
213        println!("  {}: {} employees, avg salary ${:.2}", dept, count, avg_sal);
214    }
215
216    println!("\nRust Query Builder:");
217    println!("let by_dept = Query::new(&employees).group_by(Employee::department_r());");
218    println!("for (dept, emps) in &by_dept {{");
219    println!("    let dept_query = Query::new(emps);");
220    println!("    dept_query.avg(Employee::salary_r())");
221    println!("}}");
222
223    // ============================================================================
224    // EXAMPLE 6: ORDER BY
225    // ============================================================================
226    print_separator("Example 6: ORDER BY");
227    print_query(
228        "SELECT name, salary FROM employees\n\
229         WHERE department = 'Sales'\n\
230         ORDER BY salary DESC;",
231        "Sales employees ordered by salary (descending)"
232    );
233
234    let sales_sorted = Query::new(&employees)
235        .where_(Employee::department_r(), |dept| dept == "Sales")
236        .order_by_float_desc(Employee::salary_r());
237
238    for emp in &sales_sorted {
239        println!("  {}: ${:.2}", emp.name, emp.salary);
240    }
241
242    println!("\nRust Query Builder:");
243    println!("Query::new(&employees)");
244    println!("    .where_(Employee::department_r(), |dept| dept == \"Sales\")");
245    println!("    .order_by_float_desc(Employee::salary_r())");
246
247    // ============================================================================
248    // EXAMPLE 7: Multiple WHERE conditions (AND)
249    // ============================================================================
250    print_separator("Example 7: Multiple WHERE conditions (AND)");
251    print_query(
252        "SELECT * FROM employees\n\
253         WHERE salary > 70000 AND age < 35;",
254        "High-earning young employees"
255    );
256
257    let filter_query = Query::new(&employees)
258        .where_(Employee::salary_r(), |&salary| salary > 70000.0)
259        .where_(Employee::age_r(), |&age| age < 35);
260    let filtered = filter_query.all();
261
262    for emp in &filtered {
263        println!("  {}: Age {}, Salary ${:.2}", emp.name, emp.age, emp.salary);
264    }
265
266    println!("\nRust Query Builder:");
267    println!("Query::new(&employees)");
268    println!("    .where_(Employee::salary_r(), |&salary| salary > 70000.0)");
269    println!("    .where_(Employee::age_r(), |&age| age < 35)");
270    println!("    .all()");
271    println!("✓ Found {} employees", filtered.len());
272
273    // ============================================================================
274    // EXAMPLE 8: LIMIT (TOP N)
275    // ============================================================================
276    print_separator("Example 8: LIMIT / TOP N");
277    print_query(
278        "SELECT TOP 3 name, salary FROM employees\n\
279         ORDER BY salary DESC;",
280        "Top 3 highest paid employees"
281    );
282
283    let top_earners = Query::new(&employees)
284        .order_by_float_desc(Employee::salary_r());
285
286    for (i, emp) in top_earners.iter().take(3).enumerate() {
287        println!("  {}. {}: ${:.2}", i + 1, emp.name, emp.salary);
288    }
289
290    println!("\nRust Query Builder:");
291    println!("Query::new(&employees)");
292    println!("    .order_by_float_desc(Employee::salary_r())");
293    println!("    .into_iter().take(3)");
294
295    // ============================================================================
296    // EXAMPLE 9: OFFSET and LIMIT (Pagination)
297    // ============================================================================
298    print_separator("Example 9: OFFSET and LIMIT (Pagination)");
299    print_query(
300        "SELECT name FROM employees\n\
301         ORDER BY name\n\
302         LIMIT 3 OFFSET 3;",
303        "Page 2 of employee names (3 per page)"
304    );
305
306    let page_2 = Query::new(&employees)
307        .order_by(Employee::name_r())
308        .into_iter()
309        .skip(3)
310        .take(3)
311        .collect::<Vec<_>>();
312
313    for emp in &page_2 {
314        println!("  {}", emp.name);
315    }
316
317    println!("\nRust Query Builder:");
318    println!("Query::new(&employees)");
319    println!("    .order_by(Employee::name_r())");
320    println!("    .into_iter().skip(3).take(3)");
321
322    // ============================================================================
323    // EXAMPLE 10: INNER JOIN
324    // ============================================================================
325    print_separator("Example 10: INNER JOIN");
326    print_query(
327        "SELECT e.name, d.name as dept_name, d.budget\n\
328         FROM employees e\n\
329         INNER JOIN departments d ON e.department = d.name;",
330        "Employees with their department info"
331    );
332
333    let emp_dept = JoinQuery::new(&employees, &departments).inner_join(
334        Employee::department_r(),
335        Department::name_r(),
336        |emp, dept| (emp.name.clone(), dept.name.clone(), dept.budget),
337    );
338
339    for (emp_name, dept_name, budget) in emp_dept.iter().take(5) {
340        println!("  {} works in {} (Budget: ${:.0})", emp_name, dept_name, budget);
341    }
342    println!("  ... (showing first 5)");
343
344    println!("\nRust Query Builder:");
345    println!("JoinQuery::new(&employees, &departments).inner_join(");
346    println!("    Employee::department_r(),");
347    println!("    Department::name_r(),");
348    println!("    |emp, dept| (emp.name.clone(), dept.name.clone(), dept.budget)");
349    println!(")");
350    println!("✓ Found {} matches", emp_dept.len());
351
352    // ============================================================================
353    // EXAMPLE 11: GROUP BY with HAVING equivalent
354    // ============================================================================
355    print_separator("Example 11: GROUP BY with filtering (HAVING equivalent)");
356    print_query(
357        "SELECT city, COUNT(*) as emp_count, AVG(salary) as avg_salary\n\
358         FROM employees\n\
359         GROUP BY city\n\
360         HAVING COUNT(*) > 1;",
361        "Cities with multiple employees"
362    );
363
364    let by_city = Query::new(&employees).group_by(Employee::city_r());
365    let mut city_stats: Vec<_> = by_city
366        .iter()
367        .filter(|(_, emps)| emps.len() > 1) // HAVING equivalent
368        .map(|(city, emps)| {
369            let avg_sal = Query::new(emps).avg(Employee::salary_r()).unwrap_or(0.0);
370            (city.clone(), emps.len(), avg_sal)
371        })
372        .collect();
373    
374    city_stats.sort_by(|a, b| b.1.cmp(&a.1)); // Sort by count
375
376    for (city, count, avg_sal) in &city_stats {
377        println!("  {}: {} employees, avg salary ${:.2}", city, count, avg_sal);
378    }
379
380    println!("\nRust Query Builder:");
381    println!("let by_city = Query::new(&employees).group_by(Employee::city_r());");
382    println!("by_city.iter()");
383    println!("    .filter(|(_, emps)| emps.len() > 1)  // HAVING equivalent");
384    println!("    .map(|(city, emps)| {{");
385    println!("        let avg = Query::new(emps).avg(Employee::salary_r());");
386    println!("        (city, emps.len(), avg)");
387    println!("    }})");
388
389    // ============================================================================
390    // EXAMPLE 12: Complex query with multiple operations
391    // ============================================================================
392    print_separator("Example 12: Complex multi-operation query");
393    print_query(
394        "SELECT name, salary, age\n\
395         FROM employees\n\
396         WHERE department IN ('Engineering', 'Sales')\n\
397         AND salary BETWEEN 80000 AND 100000\n\
398         AND age >= 30\n\
399         ORDER BY salary DESC;",
400        "Experienced mid-to-senior level employees in core departments"
401    );
402
403    let complex_query = Query::new(&employees)
404        .where_(Employee::department_r(), |dept| dept == "Engineering" || dept == "Sales")
405        .where_(Employee::salary_r(), |&sal| sal >= 80000.0 && sal <= 100000.0)
406        .where_(Employee::age_r(), |&age| age >= 30)
407        .order_by_float_desc(Employee::salary_r());
408
409    for emp in &complex_query {
410        println!("  {}: Age {}, {} dept, ${:.2}", emp.name, emp.age, emp.department, emp.salary);
411    }
412
413    println!("\nRust Query Builder:");
414    println!("Query::new(&employees)");
415    println!("    .where_(Employee::department_r(), |dept| dept == \"Engineering\" || dept == \"Sales\")");
416    println!("    .where_(Employee::salary_r(), |&sal| sal >= 80000.0 && sal <= 100000.0)");
417    println!("    .where_(Employee::age_r(), |&age| age >= 30)");
418    println!("    .order_by_float_desc(Employee::salary_r())");
419    println!("✓ Found {} employees", complex_query.len());
420
421    // ============================================================================
422    // EXAMPLE 13: Three-table JOIN
423    // ============================================================================
424    print_separator("Example 13: Three-table JOIN");
425    print_query(
426        "SELECT p.name as project, d.name as department, d.location\n\
427         FROM projects p\n\
428         INNER JOIN departments d ON p.department_id = d.id;",
429        "Projects with their department details"
430    );
431
432    let proj_dept = JoinQuery::new(&projects, &departments).inner_join(
433        Project::department_id_r(),
434        Department::id_r(),
435        |proj, dept| {
436            (proj.name.clone(), dept.name.clone(), dept.location.clone(), proj.budget)
437        },
438    );
439
440    for (proj_name, dept_name, location, budget) in &proj_dept {
441        println!("  {}: {} dept in {} (${:.0})", proj_name, dept_name, location, budget);
442    }
443
444    println!("\nRust Query Builder:");
445    println!("JoinQuery::new(&projects, &departments).inner_join(");
446    println!("    Project::department_id_r(),");
447    println!("    Department::id_r(),");
448    println!("    |proj, dept| (proj.name, dept.name, dept.location, proj.budget)");
449    println!(")");
450
451    // ============================================================================
452    // EXAMPLE 14: Subquery equivalent (using intermediate results)
453    // ============================================================================
454    print_separator("Example 14: Subquery equivalent");
455    print_query(
456        "SELECT * FROM employees\n\
457         WHERE salary > (SELECT AVG(salary) FROM employees);",
458        "Employees earning above average"
459    );
460
461    let avg_salary = Query::new(&employees)
462        .avg(Employee::salary_r())
463        .unwrap_or(0.0);
464
465    let above_avg_query = Query::new(&employees)
466        .where_(Employee::salary_r(), move |&sal| sal > avg_salary);
467    let above_avg = above_avg_query.all();
468
469    println!("  Average salary: ${:.2}", avg_salary);
470    for emp in &above_avg {
471        println!("  {}: ${:.2} ({:.1}% above average)", 
472            emp.name, emp.salary, ((emp.salary - avg_salary) / avg_salary * 100.0));
473    }
474
475    println!("\nRust Query Builder:");
476    println!("let avg = Query::new(&employees).avg(Employee::salary_r()).unwrap_or(0.0);");
477    println!("Query::new(&employees)");
478    println!("    .where_(Employee::salary_r(), |&sal| sal > avg)");
479    println!("    .all()");
480    println!("✓ Found {} employees above average", above_avg.len());
481
482    // ============================================================================
483    // EXAMPLE 15: Advanced aggregation (revenue calculation)
484    // ============================================================================
485    print_separator("Example 15: Advanced aggregation");
486    print_query(
487        "SELECT \n\
488         d.name,\n\
489         d.budget as dept_budget,\n\
490         SUM(p.budget) as total_project_budget,\n\
491         (d.budget - SUM(p.budget)) as remaining_budget\n\
492         FROM departments d\n\
493         LEFT JOIN projects p ON d.id = p.department_id\n\
494         GROUP BY d.name, d.budget;",
495        "Department budget utilization"
496    );
497
498    // Join departments with projects
499    let dept_projects = JoinQuery::new(&departments, &projects).left_join(
500        Department::id_r(),
501        Project::department_id_r(),
502        |dept, proj| (dept.clone(), proj.map(|p| p.clone())),
503    );
504
505    // Aggregate by department
506    let mut dept_budget_map: HashMap<String, (f64, Vec<f64>)> = HashMap::new();
507    for (dept, proj) in dept_projects {
508        let entry = dept_budget_map
509            .entry(dept.name.clone())
510            .or_insert((dept.budget, Vec::new()));
511        if let Some(p) = proj {
512            entry.1.push(p.budget);
513        }
514    }
515
516    for (dept_name, (total_budget, project_budgets)) in dept_budget_map.iter() {
517        let used: f64 = project_budgets.iter().sum();
518        let remaining = total_budget - used;
519        println!("  {}: Budget ${:.0}, Used ${:.0}, Remaining ${:.0} ({:.1}% utilized)",
520            dept_name, total_budget, used, remaining, (used / total_budget * 100.0));
521    }
522
523    println!("\nRust Query Builder:");
524    println!("let dept_projects = JoinQuery::new(&departments, &projects)");
525    println!("    .left_join(Department::id_r(), Project::department_id_r(), ...)");
526    println!("// Then aggregate using HashMap");
527
528    // ============================================================================
529    // Summary
530    // ============================================================================
531    print_separator("Summary");
532    println!("\n✓ All 15 SQL queries successfully replicated with Rust Query Builder!");
533    println!("\nDemonstrated equivalents for:");
534    println!("  • SELECT with WHERE");
535    println!("  • Projection (SELECT specific columns)");
536    println!("  • Aggregations (COUNT, SUM, AVG, MIN, MAX)");
537    println!("  • GROUP BY");
538    println!("  • ORDER BY");
539    println!("  • LIMIT and OFFSET");
540    println!("  • INNER JOIN and LEFT JOIN");
541    println!("  • Complex conditions (AND, OR, BETWEEN)");
542    println!("  • Subqueries");
543    println!("  • Multi-table operations");
544    println!("\n🎯 Type-safe, compile-time checked, and zero runtime overhead!");
545    println!();
546}
Source

pub fn inner_join_where<K, O, F, P>( &self, left_key: KeyPaths<L, K>, right_key: KeyPaths<R, K>, predicate: P, mapper: F, ) -> Vec<O>
where K: Eq + Hash + Clone + 'static, F: Fn(&L, &R) -> O, P: Fn(&L, &R) -> bool,

Performs an inner join with an additional filter predicate.

Like inner_join, but only includes pairs that satisfy both the join condition and the additional predicate.

§Arguments
  • left_key - Key-path to the join field in the left collection
  • right_key - Key-path to the join field in the right collection
  • predicate - Additional condition that must be true for pairs to be included
  • mapper - Function to transform matching pairs into the result type
§Example
// Join orders with products, but only high-value orders
let results = JoinQuery::new(&orders, &products)
    .inner_join_where(
        Order::product_id_r(),
        Product::id_r(),
        |order, _product| order.total > 100.0,
        |order, product| (product.name.clone(), order.total)
    );
Examples found in repository?
examples/join_query_builder.rs (lines 352-357)
193fn main() {
194    println!("=== Join Query Builder Demo ===\n");
195
196    let (users, orders, products) = create_sample_data();
197
198    println!("Database:");
199    println!("  Users: {}", users.len());
200    println!("  Orders: {}", orders.len());
201    println!("  Products: {}\n", products.len());
202
203    // Join 1: Inner join Users and Orders
204    println!("--- Join 1: Users with Their Orders ---");
205    let user_orders = JoinQuery::new(&users, &orders).inner_join(
206        User::id_r(),
207        Order::user_id_r(),
208        |user, order| UserOrder {
209            user_name: user.name.clone(),
210            user_email: user.email.clone(),
211            order_id: order.id,
212            quantity: order.quantity,
213            total: order.total,
214        },
215    );
216
217    for uo in &user_orders {
218        println!(
219            "  • {} - Order #{} - {} items - ${:.2}",
220            uo.user_name, uo.order_id, uo.quantity, uo.total
221        );
222    }
223    println!("Total: {} user-order pairs", user_orders.len());
224
225    // Join 2: Three-way join (Orders -> Users, Orders -> Products)
226    println!("\n--- Join 2: Complete Order Details (3-Way Join) ---");
227    
228    // First join: Orders with Users
229    let orders_with_users = JoinQuery::new(&orders, &users).inner_join(
230        Order::user_id_r(),
231        User::id_r(),
232        |order, user| (order.clone(), user.clone()),
233    );
234
235    // Second join: (Orders+Users) with Products
236    let mut order_details = Vec::new();
237    for (order, user) in &orders_with_users {
238        for product in &products {
239            if order.product_id == product.id {
240                order_details.push(OrderDetail {
241                    order_id: order.id,
242                    user_name: user.name.clone(),
243                    product_name: product.name.clone(),
244                    quantity: order.quantity,
245                    price: product.price,
246                    total: order.total,
247                });
248            }
249        }
250    }
251
252    for od in &order_details {
253        println!(
254            "  • Order #{}: {} bought {} x {} @ ${:.2} = ${:.2}",
255            od.order_id, od.user_name, od.quantity, od.product_name, od.price, od.total
256        );
257    }
258
259    // Join 3: Left join to show all users (including those without orders)
260    println!("\n--- Join 3: All Users with Order Count (Left Join) ---");
261    
262    // Use left_join to get all users with their orders (or None)
263    let user_order_pairs = JoinQuery::new(&users, &orders).left_join(
264        User::id_r(),
265        Order::user_id_r(),
266        |user, order| (user.clone(), order.map(|o| o.clone())),
267    );
268
269    // Group by user to count orders
270    let mut user_stats: HashMap<u32, (String, String, usize, f64)> = HashMap::new();
271    for (user, order) in &user_order_pairs {
272        let entry = user_stats
273            .entry(user.id)
274            .or_insert_with(|| (user.name.clone(), user.city.clone(), 0, 0.0));
275        if let Some(order) = order {
276            entry.2 += 1; // order count
277            entry.3 += order.total; // total spent
278        }
279    }
280
281    let mut user_order_stats: Vec<_> = user_stats
282        .into_iter()
283        .map(|(_, (name, city, count, total))| UserOrderCount {
284            user_name: name,
285            user_city: city,
286            order_count: count,
287            total_spent: total,
288        })
289        .collect();
290
291    user_order_stats.sort_by(|a, b| a.user_name.cmp(&b.user_name));
292
293    for stat in &user_order_stats {
294        if stat.order_count > 0 {
295            println!(
296                "  • {} ({}) - {} orders - ${:.2} total",
297                stat.user_name, stat.user_city, stat.order_count, stat.total_spent
298            );
299        } else {
300            println!("  • {} ({}) - No orders yet", stat.user_name, stat.user_city);
301        }
302    }
303
304    // Join 4: Aggregated join - Category sales analysis
305    println!("\n--- Join 4: Sales by Product Category ---");
306
307    // Join orders with products to get category information
308    let order_products = JoinQuery::new(&orders, &products).inner_join(
309        Order::product_id_r(),
310        Product::id_r(),
311        |order, product| (order.clone(), product.clone()),
312    );
313
314    // Aggregate by category
315    let mut category_stats: HashMap<String, (Vec<u32>, f64, std::collections::HashSet<u32>)> =
316        HashMap::new();
317
318    for (order, product) in &order_products {
319        let entry = category_stats
320            .entry(product.category.clone())
321            .or_insert_with(|| (Vec::new(), 0.0, std::collections::HashSet::new()));
322        entry.0.push(order.id);
323        entry.1 += order.total;
324        entry.2.insert(order.user_id);
325    }
326
327    let mut category_sales: Vec<CategorySales> = category_stats
328        .into_iter()
329        .map(|(category, (orders, revenue, customers))| CategorySales {
330            category,
331            total_orders: orders.len(),
332            total_revenue: revenue,
333            unique_customers: customers.len(),
334        })
335        .collect();
336
337    category_sales.sort_by(|a, b| {
338        b.total_revenue
339            .partial_cmp(&a.total_revenue)
340            .unwrap_or(std::cmp::Ordering::Equal)
341    });
342
343    for cs in &category_sales {
344        println!(
345            "  • {}: {} orders - ${:.2} revenue - {} customers",
346            cs.category, cs.total_orders, cs.total_revenue, cs.unique_customers
347        );
348    }
349
350    // Join 5: Filtered join - High value orders
351    println!("\n--- Join 5: High Value Orders (>$100) with User Details ---");
352    let high_value_orders = JoinQuery::new(&orders, &users).inner_join_where(
353        Order::user_id_r(),
354        User::id_r(),
355        |order, _user| order.total > 100.0,
356        |order, user| (user.name.clone(), order.id, order.total),
357    );
358
359    for (name, order_id, total) in &high_value_orders {
360        println!("  • {} - Order #{} - ${:.2}", name, order_id, total);
361    }
362
363    // Join 6: Users in same city analysis
364    println!("\n--- Join 6: Users from Same City ---");
365    let user_pairs = JoinQuery::new(&users, &users).inner_join_where(
366        User::city_r(),
367        User::city_r(),
368        |u1, u2| u1.id < u2.id, // Avoid duplicates and self-pairs
369        |u1, u2| (u1.name.clone(), u2.name.clone(), u1.city.clone()),
370    );
371
372    for (name1, name2, city) in &user_pairs {
373        println!("  • {} and {} both live in {}", name1, name2, city);
374    }
375
376    // Join 7: Product popularity
377    println!("\n--- Join 7: Product Popularity Ranking ---");
378    
379    // Join orders with products
380    let product_order_pairs = JoinQuery::new(&products, &orders).inner_join(
381        Product::id_r(),
382        Order::product_id_r(),
383        |product, order| (product.clone(), order.clone()),
384    );
385
386    // Aggregate by product
387    let mut product_sales: HashMap<u32, (String, usize, u32, f64)> = HashMap::new();
388    for (product, order) in &product_order_pairs {
389        let entry = product_sales
390            .entry(product.id)
391            .or_insert_with(|| (product.name.clone(), 0, 0, 0.0));
392        entry.1 += 1; // order count
393        entry.2 += order.quantity; // total quantity
394        entry.3 += order.total; // total revenue
395    }
396
397    let mut popularity: Vec<_> = product_sales.into_iter().collect();
398    popularity.sort_by(|a, b| b.1 .1.cmp(&a.1 .1)); // sort by order count
399
400    for (_, (name, order_count, total_qty, revenue)) in &popularity {
401        println!(
402            "  • {} - {} orders - {} units - ${:.2}",
403            name, order_count, total_qty, revenue
404        );
405    }
406
407    // Join 8: User spending by city
408    println!("\n--- Join 8: Total Spending by City ---");
409    
410    // Join users with orders to get city and spending info
411    let user_city_orders = JoinQuery::new(&users, &orders).inner_join(
412        User::id_r(),
413        Order::user_id_r(),
414        |user, order| (user.city.clone(), order.total, user.id),
415    );
416
417    // Aggregate by city
418    let mut city_spending: HashMap<String, (f64, std::collections::HashSet<u32>)> = HashMap::new();
419    for (city, total, user_id) in &user_city_orders {
420        let entry = city_spending
421            .entry(city.clone())
422            .or_insert_with(|| (0.0, std::collections::HashSet::new()));
423        entry.0 += total;
424        entry.1.insert(*user_id);
425    }
426
427    let mut city_stats: Vec<_> = city_spending
428        .into_iter()
429        .map(|(city, (total, customers))| (city, total, customers.len()))
430        .collect();
431    
432    city_stats.sort_by(|a, b| {
433        b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)
434    });
435
436    for (city, total, customer_count) in &city_stats {
437        println!(
438            "  • {} - ${:.2} total - {} customers - ${:.2} avg",
439            city,
440            total,
441            customer_count,
442            total / *customer_count as f64
443        );
444    }
445
446    // Statistics summary
447    println!("\n=== Summary Statistics ===");
448    println!("Total orders: {}", orders.len());
449    
450    let total_revenue: f64 = orders.iter().map(|o| o.total).sum();
451    println!("Total revenue: ${:.2}", total_revenue);
452    println!("Average order value: ${:.2}", total_revenue / orders.len() as f64);
453    
454    // Count unique customers using a join
455    let unique_customers: std::collections::HashSet<u32> = 
456        orders.iter().map(|o| o.user_id).collect();
457    println!("Active customers: {}", unique_customers.len());
458    println!(
459        "Average orders per customer: {:.1}",
460        orders.len() as f64 / unique_customers.len() as f64
461    );
462
463    println!("\n✓ Join query builder demo complete!");
464}
Source

pub fn right_join<K, O, F>( &self, left_key: KeyPaths<L, K>, right_key: KeyPaths<R, K>, mapper: F, ) -> Vec<O>
where K: Eq + Hash + Clone + 'static, F: Fn(Option<&L>, &R) -> O,

Performs a right join between two collections.

Returns all items from the right collection with optional matching items from the left collection. If no match is found, the left item is None.

§Arguments
  • left_key - Key-path to the join field in the left collection
  • right_key - Key-path to the join field in the right collection
  • mapper - Function to transform pairs into the result type (left item may be None)
§Example
let results = JoinQuery::new(&users, &orders)
    .right_join(
        User::id_r(),
        Order::user_id_r(),
        |user, order| match user {
            Some(u) => format!("Order {} by {}", order.id, u.name),
            None => format!("Order {} by unknown user", order.id),
        }
    );
Source

pub fn cross_join<O, F>(&self, mapper: F) -> Vec<O>
where F: Fn(&L, &R) -> O,

Performs a cross join (Cartesian product) between two collections.

Returns all possible pairs of items from both collections. Warning: This can produce very large result sets (size = left.len() * right.len()).

§Arguments
  • mapper - Function to transform pairs into the result type
§Example
let all_combinations = JoinQuery::new(&colors, &sizes)
    .cross_join(|color, size| ProductVariant {
        color: color.clone(),
        size: size.clone(),
    });

Auto Trait Implementations§

§

impl<'a, L, R> Freeze for JoinQuery<'a, L, R>

§

impl<'a, L, R> RefUnwindSafe for JoinQuery<'a, L, R>

§

impl<'a, L, R> Send for JoinQuery<'a, L, R>
where L: Sync, R: Sync,

§

impl<'a, L, R> Sync for JoinQuery<'a, L, R>
where L: Sync, R: Sync,

§

impl<'a, L, R> Unpin for JoinQuery<'a, L, R>

§

impl<'a, L, R> UnwindSafe for JoinQuery<'a, L, R>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.