@starbemtech/star-db-query-builder 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.claude/skills/star-db-query-builder/SKILL.md +104 -0
  2. package/CHANGELOG.md +81 -46
  3. package/LICENSE +21 -0
  4. package/README.md +194 -94
  5. package/bin/install-skill.js +53 -0
  6. package/dist/src/core/repository.d.ts +92 -9
  7. package/dist/src/core/repository.js +275 -27
  8. package/dist/src/core/repository.js.map +1 -1
  9. package/dist/src/core/types.d.ts +17 -2
  10. package/dist/src/core/utils.d.ts +102 -0
  11. package/dist/src/core/utils.js +287 -70
  12. package/dist/src/core/utils.js.map +1 -1
  13. package/dist/src/db/initDb.d.ts +60 -50
  14. package/dist/src/db/initDb.js +96 -64
  15. package/dist/src/db/initDb.js.map +1 -1
  16. package/dist/src/db/mysqlClient.d.ts +3 -8
  17. package/dist/src/db/mysqlClient.js +9 -11
  18. package/dist/src/db/mysqlClient.js.map +1 -1
  19. package/dist/src/db/pgClient.js +0 -2
  20. package/dist/src/db/pgClient.js.map +1 -1
  21. package/dist/src/monitor/monitor.js +7 -0
  22. package/dist/src/monitor/monitor.js.map +1 -1
  23. package/package.json +28 -20
  24. package/.github/workflows/publish.yml +0 -118
  25. package/.prettierignore +0 -3
  26. package/.prettierrc +0 -5
  27. package/ARCHITECTURE.md +0 -313
  28. package/coverage/base.css +0 -224
  29. package/coverage/block-navigation.js +0 -87
  30. package/coverage/favicon.png +0 -0
  31. package/coverage/index.html +0 -131
  32. package/coverage/lcov-report/base.css +0 -224
  33. package/coverage/lcov-report/block-navigation.js +0 -87
  34. package/coverage/lcov-report/favicon.png +0 -0
  35. package/coverage/lcov-report/index.html +0 -131
  36. package/coverage/lcov-report/mysqlClient.ts.html +0 -685
  37. package/coverage/lcov-report/pgClient.ts.html +0 -823
  38. package/coverage/lcov-report/prettify.css +0 -1
  39. package/coverage/lcov-report/prettify.js +0 -2
  40. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  41. package/coverage/lcov-report/sorter.js +0 -210
  42. package/coverage/lcov.info +0 -533
  43. package/coverage/mysqlClient.ts.html +0 -685
  44. package/coverage/pgClient.ts.html +0 -823
  45. package/coverage/prettify.css +0 -1
  46. package/coverage/prettify.js +0 -2
  47. package/coverage/sort-arrow-sprite.png +0 -0
  48. package/coverage/sorter.js +0 -210
  49. package/dist/src/setupTests.d.ts +0 -26
  50. package/dist/src/setupTests.js +0 -43
  51. package/dist/src/setupTests.js.map +0 -1
  52. package/docs/INDEX.md +0 -145
  53. package/docs/methods/findFirst.md +0 -394
  54. package/docs/methods/findMany.md +0 -587
  55. package/docs/methods/insert.md +0 -536
  56. package/docs/methods/insertMany.md +0 -627
  57. package/docs/methods/joins.md +0 -781
  58. package/docs/methods/rawQuery.md +0 -284
  59. package/docs/methods/transactions.md +0 -737
  60. package/eslint.config.mjs +0 -77
  61. package/index.ts +0 -16
  62. package/jest.config.ts +0 -194
  63. package/scripts/release.sh +0 -123
  64. package/src/core/repository.ts +0 -865
  65. package/src/core/types.ts +0 -97
  66. package/src/core/utils.ts +0 -357
  67. package/src/db/IDatabaseClient.ts +0 -16
  68. package/src/db/__tests__/mysqlClient.test.ts +0 -262
  69. package/src/db/__tests__/pgClient.test.ts +0 -260
  70. package/src/db/initDb.ts +0 -181
  71. package/src/db/mysqlClient.ts +0 -200
  72. package/src/db/pgClient.ts +0 -246
  73. package/src/monitor/monitor.ts +0 -16
  74. package/src/setupTests.ts +0 -45
  75. package/tsconfig.test.json +0 -21
@@ -1,781 +0,0 @@
1
- # joins
2
-
3
- Executes queries with JOIN operations to combine data from multiple tables.
4
-
5
- ## Signature
6
-
7
- ```typescript
8
- joins<T>({
9
- tableName: string,
10
- dbClient: IDatabaseClient,
11
- select: string[],
12
- joins: JoinClause[],
13
- where?: Conditions<T>,
14
- groupBy?: string[],
15
- orderBy?: OrderBy,
16
- limit?: number,
17
- offset?: number,
18
- unaccent?: boolean
19
- }): Promise<T[]>
20
- ```
21
-
22
- ## Parameters
23
-
24
- | Parameter | Type | Required | Description |
25
- | ----------- | ----------------- | -------- | -------------------------------------------------- |
26
- | `tableName` | `string` | ✅ | Name of the main database table |
27
- | `dbClient` | `IDatabaseClient` | ✅ | Database client instance |
28
- | `select` | `string[]` | ✅ | Array of field names to select (must be specified) |
29
- | `joins` | `JoinClause[]` | ✅ | Array of JOIN clauses |
30
- | `where` | `Conditions<T>` | ❌ | Conditions to filter records |
31
- | `groupBy` | `string[]` | ❌ | Fields to group by |
32
- | `orderBy` | `OrderBy` | ❌ | Sort order specification |
33
- | `limit` | `number` | ❌ | Maximum number of records to return |
34
- | `offset` | `number` | ❌ | Number of records to skip |
35
- | `unaccent` | `boolean` | ❌ | Enable unaccent search for PostgreSQL |
36
-
37
- ## JoinClause Interface
38
-
39
- ```typescript
40
- interface JoinClause {
41
- type: 'INNER' | 'LEFT' | 'RIGHT' | 'FULL'
42
- table: string
43
- on: string
44
- }
45
- ```
46
-
47
- ## Return Value
48
-
49
- - **Type**: `Promise<T[]>`
50
- - **Description**: Returns an array of records from the joined tables
51
-
52
- ## Examples
53
-
54
- ### Basic JOIN
55
-
56
- ```typescript
57
- import { joins } from '@starbemtech/star-db-query-builder'
58
-
59
- // Get users with their orders
60
- const usersWithOrders = await joins({
61
- tableName: 'users',
62
- dbClient,
63
- select: [
64
- 'users.id',
65
- 'users.name',
66
- 'users.email',
67
- 'orders.total',
68
- 'orders.created_at',
69
- ],
70
- joins: [
71
- {
72
- type: 'LEFT',
73
- table: 'orders',
74
- on: 'users.id = orders.user_id',
75
- },
76
- ],
77
- where: {
78
- 'users.status': { operator: '=', value: 'active' },
79
- },
80
- })
81
-
82
- console.log(usersWithOrders)
83
- // [
84
- // {
85
- // id: 'user-1',
86
- // name: 'John Doe',
87
- // email: 'john@example.com',
88
- // total: 150.00,
89
- // created_at: '2023-12-01T10:00:00.000Z'
90
- // },
91
- // // ... more results
92
- // ]
93
- ```
94
-
95
- ### Multiple JOINs
96
-
97
- ```typescript
98
- // Complex report with multiple tables
99
- const report = await joins({
100
- tableName: 'users',
101
- dbClient,
102
- select: [
103
- 'users.name',
104
- 'users.email',
105
- 'COUNT(orders.id) as order_count',
106
- 'SUM(orders.total) as total_spent',
107
- 'plans.name as plan_name',
108
- 'plans.price as plan_price',
109
- ],
110
- joins: [
111
- {
112
- type: 'LEFT',
113
- table: 'orders',
114
- on: 'users.id = orders.user_id',
115
- },
116
- {
117
- type: 'LEFT',
118
- table: 'user_plans',
119
- on: 'users.id = user_plans.user_id',
120
- },
121
- {
122
- type: 'LEFT',
123
- table: 'plans',
124
- on: 'user_plans.plan_id = plans.id',
125
- },
126
- ],
127
- where: {
128
- 'users.status': { operator: '=', value: 'active' },
129
- },
130
- groupBy: [
131
- 'users.id',
132
- 'users.name',
133
- 'users.email',
134
- 'plans.name',
135
- 'plans.price',
136
- ],
137
- orderBy: [{ field: 'total_spent', direction: 'DESC' }],
138
- })
139
- ```
140
-
141
- ### Different JOIN Types
142
-
143
- ```typescript
144
- // INNER JOIN - only users with orders
145
- const usersWithOrders = await joins({
146
- tableName: 'users',
147
- dbClient,
148
- select: ['users.name', 'orders.total'],
149
- joins: [
150
- {
151
- type: 'INNER',
152
- table: 'orders',
153
- on: 'users.id = orders.user_id',
154
- },
155
- ],
156
- })
157
-
158
- // LEFT JOIN - all users, with or without orders
159
- const allUsersWithOrders = await joins({
160
- tableName: 'users',
161
- dbClient,
162
- select: ['users.name', 'orders.total'],
163
- joins: [
164
- {
165
- type: 'LEFT',
166
- table: 'orders',
167
- on: 'users.id = orders.user_id',
168
- },
169
- ],
170
- })
171
-
172
- // RIGHT JOIN - all orders, with or without users
173
- const ordersWithUsers = await joins({
174
- tableName: 'users',
175
- dbClient,
176
- select: ['users.name', 'orders.total'],
177
- joins: [
178
- {
179
- type: 'RIGHT',
180
- table: 'orders',
181
- on: 'users.id = orders.user_id',
182
- },
183
- ],
184
- })
185
-
186
- // FULL OUTER JOIN - all users and all orders
187
- const allData = await joins({
188
- tableName: 'users',
189
- dbClient,
190
- select: ['users.name', 'orders.total'],
191
- joins: [
192
- {
193
- type: 'FULL',
194
- table: 'orders',
195
- on: 'users.id = orders.user_id',
196
- },
197
- ],
198
- })
199
- ```
200
-
201
- ### Complex WHERE Conditions with JOINs
202
-
203
- ```typescript
204
- // Users with orders from last month
205
- const recentUsers = await joins({
206
- tableName: 'users',
207
- dbClient,
208
- select: ['users.name', 'users.email', 'orders.total', 'orders.created_at'],
209
- joins: [
210
- {
211
- type: 'INNER',
212
- table: 'orders',
213
- on: 'users.id = orders.user_id',
214
- },
215
- ],
216
- where: {
217
- AND: [
218
- { 'users.status': { operator: '=', value: 'active' } },
219
- {
220
- 'orders.created_at': { operator: '>=', value: new Date('2023-11-01') },
221
- },
222
- { 'orders.total': { operator: '>', value: 100 } },
223
- ],
224
- },
225
- orderBy: [{ field: 'orders.created_at', direction: 'DESC' }],
226
- })
227
- ```
228
-
229
- ### Aggregation with JOINs
230
-
231
- ```typescript
232
- // User statistics with order data
233
- const userStats = await joins({
234
- tableName: 'users',
235
- dbClient,
236
- select: [
237
- 'users.id',
238
- 'users.name',
239
- 'COUNT(orders.id) as order_count',
240
- 'SUM(orders.total) as total_spent',
241
- 'AVG(orders.total) as avg_order_value',
242
- 'MAX(orders.created_at) as last_order_date',
243
- ],
244
- joins: [
245
- {
246
- type: 'LEFT',
247
- table: 'orders',
248
- on: 'users.id = orders.user_id',
249
- },
250
- ],
251
- where: {
252
- 'users.created_at': { operator: '>=', value: new Date('2023-01-01') },
253
- },
254
- groupBy: ['users.id', 'users.name'],
255
- having: {
256
- 'COUNT(orders.id)': { operator: '>', value: 0 },
257
- },
258
- orderBy: [{ field: 'total_spent', direction: 'DESC' }],
259
- })
260
- ```
261
-
262
- ### Pagination with JOINs
263
-
264
- ```typescript
265
- // Paginated user orders
266
- const paginatedOrders = await joins({
267
- tableName: 'users',
268
- dbClient,
269
- select: [
270
- 'users.name',
271
- 'users.email',
272
- 'orders.id',
273
- 'orders.total',
274
- 'orders.status',
275
- 'orders.created_at',
276
- ],
277
- joins: [
278
- {
279
- type: 'INNER',
280
- table: 'orders',
281
- on: 'users.id = orders.user_id',
282
- },
283
- ],
284
- where: {
285
- 'orders.status': { operator: '=', value: 'completed' },
286
- },
287
- limit: 20,
288
- offset: 0,
289
- orderBy: [{ field: 'orders.created_at', direction: 'DESC' }],
290
- })
291
- ```
292
-
293
- ### TypeScript Usage
294
-
295
- ```typescript
296
- interface UserWithOrder {
297
- user_id: string
298
- user_name: string
299
- user_email: string
300
- order_id: string
301
- order_total: number
302
- order_status: string
303
- order_created_at: Date
304
- }
305
-
306
- // Typed usage
307
- const usersWithOrders: UserWithOrder[] = await joins<UserWithOrder>({
308
- tableName: 'users',
309
- dbClient,
310
- select: [
311
- 'users.id as user_id',
312
- 'users.name as user_name',
313
- 'users.email as user_email',
314
- 'orders.id as order_id',
315
- 'orders.total as order_total',
316
- 'orders.status as order_status',
317
- 'orders.created_at as order_created_at',
318
- ],
319
- joins: [
320
- {
321
- type: 'LEFT',
322
- table: 'orders',
323
- on: 'users.id = orders.user_id',
324
- },
325
- ],
326
- where: {
327
- 'users.status': { operator: '=', value: 'active' },
328
- },
329
- })
330
- ```
331
-
332
- ### Error Handling
333
-
334
- ```typescript
335
- try {
336
- const result = await joins({
337
- tableName: 'users',
338
- dbClient,
339
- select: ['users.name', 'orders.total'],
340
- joins: [
341
- {
342
- type: 'LEFT',
343
- table: 'orders',
344
- on: 'users.id = orders.user_id',
345
- },
346
- ],
347
- })
348
-
349
- console.log(`Found ${result.length} records`)
350
- } catch (error) {
351
- console.error('Join query error:', error.message)
352
- // Handle error appropriately
353
- }
354
- ```
355
-
356
- ## Generated SQL Examples
357
-
358
- ### Simple LEFT JOIN
359
-
360
- ```sql
361
- SELECT users.id, users.name, users.email, orders.total, orders.created_at
362
- FROM users
363
- LEFT JOIN orders ON users.id = orders.user_id
364
- WHERE users.status = $1
365
- ```
366
-
367
- ### Multiple JOINs with GROUP BY
368
-
369
- ```sql
370
- SELECT
371
- users.name,
372
- users.email,
373
- COUNT(orders.id) as order_count,
374
- SUM(orders.total) as total_spent,
375
- plans.name as plan_name
376
- FROM users
377
- LEFT JOIN orders ON users.id = orders.user_id
378
- LEFT JOIN user_plans ON users.id = user_plans.user_id
379
- LEFT JOIN plans ON user_plans.plan_id = plans.id
380
- WHERE users.status = $1
381
- GROUP BY users.id, users.name, users.email, plans.name
382
- ORDER BY total_spent DESC
383
- ```
384
-
385
- ### Complex WHERE with JOINs
386
-
387
- ```sql
388
- SELECT users.name, users.email, orders.total, orders.created_at
389
- FROM users
390
- INNER JOIN orders ON users.id = orders.user_id
391
- WHERE (users.status = $1 AND orders.created_at >= $2 AND orders.total > $3)
392
- ORDER BY orders.created_at DESC
393
- ```
394
-
395
- ## Best Practices
396
-
397
- ### 1. Always Specify SELECT Fields
398
-
399
- ```typescript
400
- // Good: Explicit field selection
401
- const result = await joins({
402
- tableName: 'users',
403
- dbClient,
404
- select: ['users.id', 'users.name', 'orders.total'],
405
- joins: [
406
- {
407
- type: 'LEFT',
408
- table: 'orders',
409
- on: 'users.id = orders.user_id',
410
- },
411
- ],
412
- })
413
-
414
- // Avoid: Not specifying select fields
415
- const result = await joins({
416
- tableName: 'users',
417
- dbClient,
418
- select: [], // This will cause issues
419
- joins: [
420
- {
421
- type: 'LEFT',
422
- table: 'orders',
423
- on: 'users.id = orders.user_id',
424
- },
425
- ],
426
- })
427
- ```
428
-
429
- ### 2. Use Table Aliases for Clarity
430
-
431
- ```typescript
432
- // Good: Use table prefixes for clarity
433
- const result = await joins({
434
- tableName: 'users',
435
- dbClient,
436
- select: [
437
- 'users.id as user_id',
438
- 'users.name as user_name',
439
- 'orders.id as order_id',
440
- 'orders.total as order_total',
441
- ],
442
- joins: [
443
- {
444
- type: 'LEFT',
445
- table: 'orders',
446
- on: 'users.id = orders.user_id',
447
- },
448
- ],
449
- })
450
- ```
451
-
452
- ### 3. Choose Appropriate JOIN Types
453
-
454
- ```typescript
455
- // Use INNER JOIN when you need matching records from both tables
456
- const usersWithOrders = await joins({
457
- tableName: 'users',
458
- dbClient,
459
- select: ['users.name', 'orders.total'],
460
- joins: [
461
- {
462
- type: 'INNER', // Only users who have orders
463
- table: 'orders',
464
- on: 'users.id = orders.user_id',
465
- },
466
- ],
467
- })
468
-
469
- // Use LEFT JOIN when you want all records from the main table
470
- const allUsersWithOrders = await joins({
471
- tableName: 'users',
472
- dbClient,
473
- select: ['users.name', 'orders.total'],
474
- joins: [
475
- {
476
- type: 'LEFT', // All users, even those without orders
477
- table: 'orders',
478
- on: 'users.id = orders.user_id',
479
- },
480
- ],
481
- })
482
- ```
483
-
484
- ### 4. Use Proper Indexing
485
-
486
- ```sql
487
- -- Ensure proper indexes exist for JOIN conditions
488
- CREATE INDEX idx_orders_user_id ON orders(user_id);
489
- CREATE INDEX idx_user_plans_user_id ON user_plans(user_id);
490
- CREATE INDEX idx_user_plans_plan_id ON user_plans(plan_id);
491
- ```
492
-
493
- ### 5. Handle NULL Values in JOINs
494
-
495
- ```typescript
496
- // Handle NULL values from LEFT JOINs
497
- const result = await joins({
498
- tableName: 'users',
499
- dbClient,
500
- select: [
501
- 'users.name',
502
- 'COALESCE(orders.total, 0) as total_spent',
503
- 'CASE WHEN orders.id IS NULL THEN 0 ELSE 1 END as has_orders',
504
- ],
505
- joins: [
506
- {
507
- type: 'LEFT',
508
- table: 'orders',
509
- on: 'users.id = orders.user_id',
510
- },
511
- ],
512
- })
513
- ```
514
-
515
- ## Common Use Cases
516
-
517
- ### 1. User Dashboard Data
518
-
519
- ```typescript
520
- const getUserDashboardData = async (userId: string) => {
521
- return joins({
522
- tableName: 'users',
523
- dbClient,
524
- select: [
525
- 'users.name',
526
- 'users.email',
527
- 'COUNT(orders.id) as total_orders',
528
- 'SUM(orders.total) as total_spent',
529
- 'plans.name as current_plan',
530
- 'plans.price as plan_price',
531
- ],
532
- joins: [
533
- {
534
- type: 'LEFT',
535
- table: 'orders',
536
- on: 'users.id = orders.user_id',
537
- },
538
- {
539
- type: 'LEFT',
540
- table: 'user_plans',
541
- on: 'users.id = user_plans.user_id AND user_plans.is_active = true',
542
- },
543
- {
544
- type: 'LEFT',
545
- table: 'plans',
546
- on: 'user_plans.plan_id = plans.id',
547
- },
548
- ],
549
- where: {
550
- 'users.id': { operator: '=', value: userId },
551
- },
552
- groupBy: [
553
- 'users.id',
554
- 'users.name',
555
- 'users.email',
556
- 'plans.name',
557
- 'plans.price',
558
- ],
559
- })
560
- }
561
- ```
562
-
563
- ### 2. Sales Report
564
-
565
- ```typescript
566
- const getSalesReport = async (startDate: Date, endDate: Date) => {
567
- return joins({
568
- tableName: 'orders',
569
- dbClient,
570
- select: [
571
- 'users.name as customer_name',
572
- 'users.email as customer_email',
573
- 'orders.id as order_id',
574
- 'orders.total as order_total',
575
- 'orders.status as order_status',
576
- 'products.name as product_name',
577
- 'order_items.quantity',
578
- 'order_items.price as item_price',
579
- ],
580
- joins: [
581
- {
582
- type: 'INNER',
583
- table: 'users',
584
- on: 'orders.user_id = users.id',
585
- },
586
- {
587
- type: 'INNER',
588
- table: 'order_items',
589
- on: 'orders.id = order_items.order_id',
590
- },
591
- {
592
- type: 'INNER',
593
- table: 'products',
594
- on: 'order_items.product_id = products.id',
595
- },
596
- ],
597
- where: {
598
- AND: [
599
- { 'orders.created_at': { operator: '>=', value: startDate } },
600
- { 'orders.created_at': { operator: '<=', value: endDate } },
601
- { 'orders.status': { operator: '=', value: 'completed' } },
602
- ],
603
- },
604
- orderBy: [{ field: 'orders.created_at', direction: 'DESC' }],
605
- })
606
- }
607
- ```
608
-
609
- ### 3. Product Analytics
610
-
611
- ```typescript
612
- const getProductAnalytics = async () => {
613
- return joins({
614
- tableName: 'products',
615
- dbClient,
616
- select: [
617
- 'products.name as product_name',
618
- 'categories.name as category_name',
619
- 'COUNT(order_items.id) as times_ordered',
620
- 'SUM(order_items.quantity) as total_quantity_sold',
621
- 'SUM(order_items.price * order_items.quantity) as total_revenue',
622
- 'AVG(order_items.price) as avg_price',
623
- ],
624
- joins: [
625
- {
626
- type: 'LEFT',
627
- table: 'categories',
628
- on: 'products.category_id = categories.id',
629
- },
630
- {
631
- type: 'LEFT',
632
- table: 'order_items',
633
- on: 'products.id = order_items.product_id',
634
- },
635
- {
636
- type: 'LEFT',
637
- table: 'orders',
638
- on: 'order_items.order_id = orders.id AND orders.status = "completed"',
639
- },
640
- ],
641
- groupBy: ['products.id', 'products.name', 'categories.name'],
642
- having: {
643
- 'COUNT(order_items.id)': { operator: '>', value: 0 },
644
- },
645
- orderBy: [{ field: 'total_revenue', direction: 'DESC' }],
646
- })
647
- }
648
- ```
649
-
650
- ### 4. User Activity Feed
651
-
652
- ```typescript
653
- const getUserActivityFeed = async (userId: string, limit: number = 50) => {
654
- return joins({
655
- tableName: 'users',
656
- dbClient,
657
- select: [
658
- 'activity_logs.action',
659
- 'activity_logs.description',
660
- 'activity_logs.created_at',
661
- 'orders.id as order_id',
662
- 'orders.total as order_total',
663
- 'products.name as product_name',
664
- ],
665
- joins: [
666
- {
667
- type: 'LEFT',
668
- table: 'activity_logs',
669
- on: 'users.id = activity_logs.user_id',
670
- },
671
- {
672
- type: 'LEFT',
673
- table: 'orders',
674
- on: 'activity_logs.entity_id = orders.id AND activity_logs.entity_type = "order"',
675
- },
676
- {
677
- type: 'LEFT',
678
- table: 'order_items',
679
- on: 'orders.id = order_items.order_id',
680
- },
681
- {
682
- type: 'LEFT',
683
- table: 'products',
684
- on: 'order_items.product_id = products.id',
685
- },
686
- ],
687
- where: {
688
- 'users.id': { operator: '=', value: userId },
689
- },
690
- limit,
691
- orderBy: [{ field: 'activity_logs.created_at', direction: 'DESC' }],
692
- })
693
- }
694
- ```
695
-
696
- ## Performance Considerations
697
-
698
- ### 1. Index Strategy for JOINs
699
-
700
- ```sql
701
- -- Create indexes on JOIN columns
702
- CREATE INDEX idx_orders_user_id ON orders(user_id);
703
- CREATE INDEX idx_order_items_order_id ON order_items(order_id);
704
- CREATE INDEX idx_order_items_product_id ON order_items(product_id);
705
- CREATE INDEX idx_user_plans_user_id ON user_plans(user_id);
706
- CREATE INDEX idx_user_plans_plan_id ON user_plans(plan_id);
707
-
708
- -- Composite indexes for common query patterns
709
- CREATE INDEX idx_orders_user_status_created ON orders(user_id, status, created_at);
710
- CREATE INDEX idx_order_items_order_product ON order_items(order_id, product_id);
711
- ```
712
-
713
- ### 2. Query Optimization
714
-
715
- ```typescript
716
- // Good: Use indexed fields in WHERE clauses
717
- const result = await joins({
718
- tableName: 'users',
719
- dbClient,
720
- select: ['users.name', 'orders.total'],
721
- joins: [
722
- {
723
- type: 'INNER',
724
- table: 'orders',
725
- on: 'users.id = orders.user_id',
726
- },
727
- ],
728
- where: {
729
- 'users.status': { operator: '=', value: 'active' }, // Indexed field
730
- 'orders.created_at': { operator: '>=', value: new Date('2023-01-01') }, // Indexed field
731
- },
732
- })
733
-
734
- // Avoid: Using non-indexed fields in WHERE clauses
735
- const result = await joins({
736
- tableName: 'users',
737
- dbClient,
738
- select: ['users.name', 'orders.total'],
739
- joins: [
740
- {
741
- type: 'INNER',
742
- table: 'orders',
743
- on: 'users.id = orders.user_id',
744
- },
745
- ],
746
- where: {
747
- 'users.bio': { operator: 'LIKE', value: '%developer%' }, // Non-indexed field
748
- },
749
- })
750
- ```
751
-
752
- ### 3. Limit Result Sets
753
-
754
- ```typescript
755
- // Always use LIMIT for large result sets
756
- const result = await joins({
757
- tableName: 'users',
758
- dbClient,
759
- select: ['users.name', 'orders.total'],
760
- joins: [
761
- {
762
- type: 'LEFT',
763
- table: 'orders',
764
- on: 'users.id = orders.user_id',
765
- },
766
- ],
767
- limit: 1000, // Prevent memory issues
768
- orderBy: [{ field: 'users.created_at', direction: 'DESC' }],
769
- })
770
- ```
771
-
772
- ## Error Messages
773
-
774
- Common error messages you might encounter:
775
-
776
- - `Table name is required` - The `tableName` parameter is missing
777
- - `DB client is required` - The `dbClient` parameter is missing
778
- - `column "field_name" does not exist` - Invalid field name in SELECT or WHERE clause
779
- - `relation "table_name" does not exist` - Invalid table name in JOIN clause
780
- - `syntax error at or near "JOIN"` - Invalid JOIN syntax
781
- - Database-specific errors from the underlying database driver