1use rust_queries_builder::JoinQuery;
11use key_paths_derive::Keypaths;
12use std::collections::HashMap;
13
14#[derive(Debug, Clone, Keypaths)]
16struct User {
17 id: u32,
18 name: String,
19 email: String,
20 city: String,
21}
22
23#[derive(Debug, Clone, Keypaths)]
24struct Order {
25 id: u32,
26 user_id: u32,
27 product_id: u32,
28 quantity: u32,
29 total: f64,
30}
31
32#[derive(Debug, Clone, Keypaths)]
33struct Product {
34 id: u32,
35 name: String,
36 price: f64,
37 category: String,
38}
39
40#[derive(Debug, Clone)]
42struct UserOrder {
43 user_name: String,
44 user_email: String,
45 order_id: u32,
46 quantity: u32,
47 total: f64,
48}
49
50#[derive(Debug, Clone)]
51struct OrderDetail {
52 order_id: u32,
53 user_name: String,
54 product_name: String,
55 quantity: u32,
56 price: f64,
57 total: f64,
58}
59
60#[derive(Debug, Clone)]
61struct UserOrderCount {
62 user_name: String,
63 user_city: String,
64 order_count: usize,
65 total_spent: f64,
66}
67
68#[derive(Debug, Clone)]
69struct CategorySales {
70 category: String,
71 total_orders: usize,
72 total_revenue: f64,
73 unique_customers: usize,
74}
75
76fn create_sample_data() -> (Vec<User>, Vec<Order>, Vec<Product>) {
78 let users = vec![
79 User {
80 id: 1,
81 name: "Alice".to_string(),
82 email: "alice@example.com".to_string(),
83 city: "New York".to_string(),
84 },
85 User {
86 id: 2,
87 name: "Bob".to_string(),
88 email: "bob@example.com".to_string(),
89 city: "San Francisco".to_string(),
90 },
91 User {
92 id: 3,
93 name: "Charlie".to_string(),
94 email: "charlie@example.com".to_string(),
95 city: "New York".to_string(),
96 },
97 User {
98 id: 4,
99 name: "Diana".to_string(),
100 email: "diana@example.com".to_string(),
101 city: "Boston".to_string(),
102 },
103 ];
104
105 let products = vec![
106 Product {
107 id: 101,
108 name: "Laptop".to_string(),
109 price: 999.99,
110 category: "Electronics".to_string(),
111 },
112 Product {
113 id: 102,
114 name: "Mouse".to_string(),
115 price: 29.99,
116 category: "Electronics".to_string(),
117 },
118 Product {
119 id: 103,
120 name: "Desk Chair".to_string(),
121 price: 199.99,
122 category: "Furniture".to_string(),
123 },
124 Product {
125 id: 104,
126 name: "Monitor".to_string(),
127 price: 299.99,
128 category: "Electronics".to_string(),
129 },
130 Product {
131 id: 105,
132 name: "Keyboard".to_string(),
133 price: 79.99,
134 category: "Electronics".to_string(),
135 },
136 ];
137
138 let orders = vec![
139 Order {
140 id: 1001,
141 user_id: 1,
142 product_id: 101,
143 quantity: 1,
144 total: 999.99,
145 },
146 Order {
147 id: 1002,
148 user_id: 1,
149 product_id: 102,
150 quantity: 2,
151 total: 59.98,
152 },
153 Order {
154 id: 1003,
155 user_id: 2,
156 product_id: 103,
157 quantity: 1,
158 total: 199.99,
159 },
160 Order {
161 id: 1004,
162 user_id: 2,
163 product_id: 104,
164 quantity: 1,
165 total: 299.99,
166 },
167 Order {
168 id: 1005,
169 user_id: 3,
170 product_id: 102,
171 quantity: 3,
172 total: 89.97,
173 },
174 Order {
175 id: 1006,
176 user_id: 1,
177 product_id: 105,
178 quantity: 1,
179 total: 79.99,
180 },
181 Order {
182 id: 1007,
183 user_id: 3,
184 product_id: 101,
185 quantity: 1,
186 total: 999.99,
187 },
188 ];
189
190 (users, orders, products)
191}
192
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 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 println!("\n--- Join 2: Complete Order Details (3-Way Join) ---");
227
228 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 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 println!("\n--- Join 3: All Users with Order Count (Left Join) ---");
261
262 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 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; entry.3 += order.total; }
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 println!("\n--- Join 4: Sales by Product Category ---");
306
307 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 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 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 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, |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 println!("\n--- Join 7: Product Popularity Ranking ---");
378
379 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 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; entry.2 += order.quantity; entry.3 += order.total; }
396
397 let mut popularity: Vec<_> = product_sales.into_iter().collect();
398 popularity.sort_by(|a, b| b.1 .1.cmp(&a.1 .1)); 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 println!("\n--- Join 8: Total Spending by City ---");
409
410 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 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 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 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}
465