@starbemtech/star-db-query-builder 1.1.0 → 1.3.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 (88) hide show
  1. package/.github/workflows/publish.yml +118 -0
  2. package/ARCHITECTURE.md +313 -0
  3. package/CHANGELOG.md +67 -0
  4. package/README.md +1275 -210
  5. package/coverage/base.css +224 -0
  6. package/coverage/block-navigation.js +87 -0
  7. package/coverage/favicon.png +0 -0
  8. package/coverage/index.html +131 -0
  9. package/coverage/lcov-report/base.css +224 -0
  10. package/coverage/lcov-report/block-navigation.js +87 -0
  11. package/coverage/lcov-report/favicon.png +0 -0
  12. package/coverage/lcov-report/index.html +131 -0
  13. package/coverage/lcov-report/mysqlClient.ts.html +685 -0
  14. package/coverage/lcov-report/pgClient.ts.html +823 -0
  15. package/coverage/lcov-report/prettify.css +1 -0
  16. package/coverage/lcov-report/prettify.js +2 -0
  17. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  18. package/coverage/lcov-report/sorter.js +210 -0
  19. package/coverage/lcov.info +533 -0
  20. package/coverage/mysqlClient.ts.html +685 -0
  21. package/coverage/pgClient.ts.html +823 -0
  22. package/coverage/prettify.css +1 -0
  23. package/coverage/prettify.js +2 -0
  24. package/coverage/sort-arrow-sprite.png +0 -0
  25. package/coverage/sorter.js +210 -0
  26. package/dist/index.d.ts +5 -4
  27. package/dist/index.js +1 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/src/core/repository.d.ts +374 -0
  30. package/dist/src/core/repository.js +677 -0
  31. package/dist/src/core/repository.js.map +1 -0
  32. package/dist/src/{default → core}/types.d.ts +8 -0
  33. package/dist/src/{default → core}/types.js.map +1 -1
  34. package/dist/src/core/utils.d.ts +133 -0
  35. package/dist/src/{default → core}/utils.js +167 -0
  36. package/dist/src/core/utils.js.map +1 -0
  37. package/dist/src/db/IDatabaseClient.d.ts +10 -1
  38. package/dist/src/db/initDb.d.ts +120 -1
  39. package/dist/src/db/initDb.js +119 -0
  40. package/dist/src/db/initDb.js.map +1 -1
  41. package/dist/src/db/mysqlClient.d.ts +23 -1
  42. package/dist/src/db/mysqlClient.js +115 -0
  43. package/dist/src/db/mysqlClient.js.map +1 -1
  44. package/dist/src/db/pgClient.d.ts +22 -1
  45. package/dist/src/db/pgClient.js +127 -0
  46. package/dist/src/db/pgClient.js.map +1 -1
  47. package/dist/src/monitor/monitor.d.ts +6 -1
  48. package/dist/src/monitor/monitor.js +5 -0
  49. package/dist/src/monitor/monitor.js.map +1 -1
  50. package/dist/src/setupTests.d.ts +26 -0
  51. package/dist/src/setupTests.js +43 -0
  52. package/dist/src/setupTests.js.map +1 -0
  53. package/docs/INDEX.md +145 -0
  54. package/docs/methods/findFirst.md +394 -0
  55. package/docs/methods/findMany.md +587 -0
  56. package/docs/methods/insert.md +536 -0
  57. package/docs/methods/insertMany.md +627 -0
  58. package/docs/methods/joins.md +781 -0
  59. package/docs/methods/rawQuery.md +284 -0
  60. package/docs/methods/transactions.md +737 -0
  61. package/eslint.config.mjs +77 -0
  62. package/index.ts +5 -4
  63. package/jest.config.ts +23 -28
  64. package/package.json +53 -30
  65. package/scripts/release.sh +123 -0
  66. package/src/core/repository.ts +865 -0
  67. package/src/{default → core}/types.ts +10 -1
  68. package/src/{default → core}/utils.ts +168 -0
  69. package/src/db/IDatabaseClient.ts +11 -1
  70. package/src/db/__tests__/mysqlClient.test.ts +262 -0
  71. package/src/db/__tests__/pgClient.test.ts +260 -0
  72. package/src/db/initDb.ts +120 -1
  73. package/src/db/mysqlClient.ts +119 -3
  74. package/src/db/pgClient.ts +131 -3
  75. package/src/monitor/monitor.ts +5 -0
  76. package/src/setupTests.ts +45 -0
  77. package/tsconfig.test.json +21 -0
  78. package/.eslintignore +0 -4
  79. package/.eslintrc.json +0 -32
  80. package/dist/.eslintrc.json +0 -32
  81. package/dist/src/default/genericRepository.d.ts +0 -28
  82. package/dist/src/default/genericRepository.js +0 -284
  83. package/dist/src/default/genericRepository.js.map +0 -1
  84. package/dist/src/default/utils.d.ts +0 -9
  85. package/dist/src/default/utils.js.map +0 -1
  86. package/index.d.ts +0 -60
  87. package/src/default/genericRepository.ts +0 -461
  88. /package/dist/src/{default → core}/types.js +0 -0
@@ -0,0 +1,587 @@
1
+ # findMany
2
+
3
+ Finds multiple records that match the specified conditions from a database table.
4
+
5
+ ## Signature
6
+
7
+ ```typescript
8
+ findMany<T>({
9
+ tableName: string,
10
+ dbClient: IDatabaseClient,
11
+ select?: string[],
12
+ where?: Conditions<T>,
13
+ groupBy?: string[],
14
+ orderBy?: OrderBy,
15
+ limit?: number,
16
+ offset?: number,
17
+ unaccent?: boolean
18
+ }): Promise<T[]>
19
+ ```
20
+
21
+ ## Parameters
22
+
23
+ | Parameter | Type | Required | Description |
24
+ | ----------- | ----------------- | -------- | ---------------------------------------------------- |
25
+ | `tableName` | `string` | ✅ | Name of the database table |
26
+ | `dbClient` | `IDatabaseClient` | ✅ | Database client instance |
27
+ | `select` | `string[]` | ❌ | Array of field names to select (default: all fields) |
28
+ | `where` | `Conditions<T>` | ❌ | Conditions to filter records |
29
+ | `groupBy` | `string[]` | ❌ | Fields to group by |
30
+ | `orderBy` | `OrderBy` | ❌ | Sort order specification |
31
+ | `limit` | `number` | ❌ | Maximum number of records to return |
32
+ | `offset` | `number` | ❌ | Number of records to skip |
33
+ | `unaccent` | `boolean` | ❌ | Enable unaccent search for PostgreSQL |
34
+
35
+ ## Return Value
36
+
37
+ - **Type**: `Promise<T[]>`
38
+ - **Description**: Returns an array of matching records (empty array if no records found)
39
+
40
+ ## Examples
41
+
42
+ ### Basic Usage
43
+
44
+ ```typescript
45
+ import { findMany } from '@starbemtech/star-db-query-builder'
46
+
47
+ // Find all active users
48
+ const users = await findMany({
49
+ tableName: 'users',
50
+ dbClient,
51
+ where: {
52
+ status: { operator: '=', value: 'active' },
53
+ },
54
+ })
55
+
56
+ console.log(users) // [{ id: 'user-1', name: 'John', ... }, { id: 'user-2', name: 'Jane', ... }]
57
+ ```
58
+
59
+ ### With Pagination
60
+
61
+ ```typescript
62
+ // Get first 10 users
63
+ const users = await findMany({
64
+ tableName: 'users',
65
+ dbClient,
66
+ limit: 10,
67
+ offset: 0,
68
+ orderBy: [{ field: 'created_at', direction: 'DESC' }],
69
+ })
70
+
71
+ // Get next 10 users (page 2)
72
+ const nextUsers = await findMany({
73
+ tableName: 'users',
74
+ dbClient,
75
+ limit: 10,
76
+ offset: 10,
77
+ orderBy: [{ field: 'created_at', direction: 'DESC' }],
78
+ })
79
+ ```
80
+
81
+ ### With Specific Fields
82
+
83
+ ```typescript
84
+ // Select only specific fields
85
+ const users = await findMany({
86
+ tableName: 'users',
87
+ dbClient,
88
+ select: ['id', 'name', 'email', 'created_at'],
89
+ where: {
90
+ status: { operator: '=', value: 'active' },
91
+ },
92
+ })
93
+ ```
94
+
95
+ ### With Complex Conditions
96
+
97
+ ```typescript
98
+ // Multiple conditions with AND
99
+ const users = await findMany({
100
+ tableName: 'users',
101
+ dbClient,
102
+ where: {
103
+ AND: [
104
+ { status: { operator: '=', value: 'active' } },
105
+ { age: { operator: '>=', value: 18 } },
106
+ { verified: { operator: '=', value: true } },
107
+ ],
108
+ },
109
+ })
110
+
111
+ // Multiple conditions with OR
112
+ const users = await findMany({
113
+ tableName: 'users',
114
+ dbClient,
115
+ where: {
116
+ OR: [
117
+ { status: { operator: '=', value: 'active' } },
118
+ { status: { operator: '=', value: 'pending' } },
119
+ ],
120
+ created_at: {
121
+ operator: '>=',
122
+ value: new Date('2023-01-01'),
123
+ },
124
+ },
125
+ })
126
+ ```
127
+
128
+ ### With Grouping and Aggregation
129
+
130
+ ```typescript
131
+ // Group users by status and count them
132
+ const userStats = await findMany({
133
+ tableName: 'users',
134
+ dbClient,
135
+ select: ['status', 'COUNT(*) as count'],
136
+ groupBy: ['status'],
137
+ })
138
+
139
+ console.log(userStats) // [{ status: 'active', count: 150 }, { status: 'inactive', count: 25 }]
140
+
141
+ // Group by multiple fields
142
+ const userStatsByAge = await findMany({
143
+ tableName: 'users',
144
+ dbClient,
145
+ select: ['status', 'age_group', 'COUNT(*) as count', 'AVG(age) as avg_age'],
146
+ groupBy: ['status', 'age_group'],
147
+ where: { status: { operator: '=', value: 'active' } },
148
+ })
149
+ ```
150
+
151
+ ### With Different Operators
152
+
153
+ ```typescript
154
+ // Using various operators
155
+ const users = await findMany({
156
+ tableName: 'users',
157
+ dbClient,
158
+ where: {
159
+ age: { operator: '>=', value: 18 },
160
+ name: { operator: 'LIKE', value: '%John%' },
161
+ email: { operator: 'IS NOT NULL', value: null },
162
+ status: { operator: 'IN', value: ['active', 'pending'] },
163
+ created_at: {
164
+ operator: 'BETWEEN',
165
+ value: [new Date('2023-01-01'), new Date('2023-12-31')],
166
+ },
167
+ },
168
+ })
169
+ ```
170
+
171
+ ### With Unaccent Search (PostgreSQL)
172
+
173
+ ```typescript
174
+ // Search with unaccent for better text matching
175
+ const users = await findMany({
176
+ tableName: 'users',
177
+ dbClient,
178
+ where: {
179
+ name: { operator: 'ILIKE', value: '%joão%' },
180
+ },
181
+ unaccent: true, // Enables unaccent search
182
+ })
183
+ ```
184
+
185
+ ### TypeScript Usage
186
+
187
+ ```typescript
188
+ interface User {
189
+ id: string
190
+ name: string
191
+ email: string
192
+ age: number
193
+ status: 'active' | 'inactive' | 'pending'
194
+ created_at: Date
195
+ updated_at: Date
196
+ }
197
+
198
+ // Typed usage
199
+ const users: User[] = await findMany<User>({
200
+ tableName: 'users',
201
+ dbClient,
202
+ where: {
203
+ status: { operator: '=', value: 'active' },
204
+ },
205
+ })
206
+
207
+ console.log(`Found ${users.length} active users`)
208
+ ```
209
+
210
+ ### Advanced Pagination
211
+
212
+ ```typescript
213
+ interface PaginationParams {
214
+ page: number
215
+ limit: number
216
+ sortBy?: string
217
+ sortOrder?: 'ASC' | 'DESC'
218
+ }
219
+
220
+ const getUsersPaginated = async (params: PaginationParams) => {
221
+ const offset = (params.page - 1) * params.limit
222
+
223
+ const users = await findMany({
224
+ tableName: 'users',
225
+ dbClient,
226
+ limit: params.limit,
227
+ offset,
228
+ orderBy: params.sortBy
229
+ ? [
230
+ {
231
+ field: params.sortBy,
232
+ direction: params.sortOrder || 'ASC',
233
+ },
234
+ ]
235
+ : undefined,
236
+ })
237
+
238
+ return users
239
+ }
240
+
241
+ // Usage
242
+ const page1Users = await getUsersPaginated({
243
+ page: 1,
244
+ limit: 20,
245
+ sortBy: 'created_at',
246
+ sortOrder: 'DESC',
247
+ })
248
+ ```
249
+
250
+ ### Error Handling
251
+
252
+ ```typescript
253
+ try {
254
+ const users = await findMany({
255
+ tableName: 'users',
256
+ dbClient,
257
+ where: { status: { operator: '=', value: 'active' } },
258
+ })
259
+
260
+ console.log(`Found ${users.length} users`)
261
+ } catch (error) {
262
+ console.error('Database error:', error.message)
263
+ // Handle error appropriately
264
+ }
265
+ ```
266
+
267
+ ## Generated SQL Examples
268
+
269
+ ### Simple Query
270
+
271
+ ```sql
272
+ SELECT * FROM users WHERE status = $1
273
+ ```
274
+
275
+ ### With Pagination
276
+
277
+ ```sql
278
+ SELECT * FROM users
279
+ ORDER BY created_at DESC
280
+ LIMIT 10 OFFSET 20
281
+ ```
282
+
283
+ ### With Specific Fields
284
+
285
+ ```sql
286
+ SELECT id, name, email FROM users WHERE status = $1
287
+ ```
288
+
289
+ ### With Complex Conditions
290
+
291
+ ```sql
292
+ SELECT * FROM users
293
+ WHERE (status = $1 AND age >= $2 AND verified = $3)
294
+ ```
295
+
296
+ ### With Grouping
297
+
298
+ ```sql
299
+ SELECT status, COUNT(*) as count
300
+ FROM users
301
+ GROUP BY status
302
+ ```
303
+
304
+ ### With Unaccent (PostgreSQL)
305
+
306
+ ```sql
307
+ SELECT * FROM users
308
+ WHERE unaccent(name) ILIKE unaccent($1)
309
+ ```
310
+
311
+ ## Best Practices
312
+
313
+ ### 1. Always Use Pagination for Large Datasets
314
+
315
+ ```typescript
316
+ // Good: Use pagination
317
+ const users = await findMany({
318
+ tableName: 'users',
319
+ dbClient,
320
+ limit: 100,
321
+ offset: 0,
322
+ orderBy: [{ field: 'created_at', direction: 'DESC' }],
323
+ })
324
+
325
+ // Avoid: Loading all records at once
326
+ const allUsers = await findMany({
327
+ tableName: 'users',
328
+ dbClient,
329
+ })
330
+ ```
331
+
332
+ ### 2. Use Specific Field Selection
333
+
334
+ ```typescript
335
+ // Good: Select only needed fields
336
+ const users = await findMany({
337
+ tableName: 'users',
338
+ dbClient,
339
+ select: ['id', 'name', 'email'],
340
+ where: { status: { operator: '=', value: 'active' } },
341
+ })
342
+
343
+ // Avoid: Selecting all fields when not needed
344
+ const users = await findMany({
345
+ tableName: 'users',
346
+ dbClient,
347
+ where: { status: { operator: '=', value: 'active' } },
348
+ })
349
+ ```
350
+
351
+ ### 3. Use Appropriate Indexes
352
+
353
+ Ensure your database has indexes on fields used in WHERE clauses:
354
+
355
+ ```sql
356
+ -- Example indexes for common queries
357
+ CREATE INDEX idx_users_status ON users(status);
358
+ CREATE INDEX idx_users_created_at ON users(created_at);
359
+ CREATE INDEX idx_users_email ON users(email);
360
+ CREATE INDEX idx_users_age ON users(age);
361
+ ```
362
+
363
+ ### 4. Handle Empty Results
364
+
365
+ ```typescript
366
+ const users = await findMany({
367
+ tableName: 'users',
368
+ dbClient,
369
+ where: { status: { operator: '=', value: 'nonexistent' } },
370
+ })
371
+
372
+ if (users.length === 0) {
373
+ console.log('No users found')
374
+ } else {
375
+ console.log(`Found ${users.length} users`)
376
+ }
377
+ ```
378
+
379
+ ### 5. Use TypeScript for Type Safety
380
+
381
+ ```typescript
382
+ interface UserSearchParams {
383
+ status?: string
384
+ minAge?: number
385
+ maxAge?: number
386
+ searchTerm?: string
387
+ }
388
+
389
+ const searchUsers = async (params: UserSearchParams): Promise<User[]> => {
390
+ const where: Conditions<User> = {}
391
+
392
+ if (params.status) {
393
+ where.status = { operator: '=', value: params.status }
394
+ }
395
+
396
+ if (params.minAge || params.maxAge) {
397
+ if (params.minAge && params.maxAge) {
398
+ where.age = {
399
+ operator: 'BETWEEN',
400
+ value: [params.minAge, params.maxAge],
401
+ }
402
+ } else if (params.minAge) {
403
+ where.age = { operator: '>=', value: params.minAge }
404
+ } else if (params.maxAge) {
405
+ where.age = { operator: '<=', value: params.maxAge }
406
+ }
407
+ }
408
+
409
+ if (params.searchTerm) {
410
+ where.name = { operator: 'ILIKE', value: `%${params.searchTerm}%` }
411
+ }
412
+
413
+ return findMany<User>({
414
+ tableName: 'users',
415
+ dbClient,
416
+ where,
417
+ })
418
+ }
419
+ ```
420
+
421
+ ## Common Use Cases
422
+
423
+ ### 1. User Management
424
+
425
+ ```typescript
426
+ // Get all active users with pagination
427
+ const getActiveUsers = async (page: number = 1, limit: number = 20) => {
428
+ const offset = (page - 1) * limit
429
+
430
+ return findMany({
431
+ tableName: 'users',
432
+ dbClient,
433
+ select: ['id', 'name', 'email', 'created_at'],
434
+ where: { status: { operator: '=', value: 'active' } },
435
+ limit,
436
+ offset,
437
+ orderBy: [{ field: 'created_at', direction: 'DESC' }],
438
+ })
439
+ }
440
+ ```
441
+
442
+ ### 2. Search Functionality
443
+
444
+ ```typescript
445
+ const searchUsers = async (searchTerm: string) => {
446
+ return findMany({
447
+ tableName: 'users',
448
+ dbClient,
449
+ where: {
450
+ OR: [
451
+ { name: { operator: 'ILIKE', value: `%${searchTerm}%` } },
452
+ { email: { operator: 'ILIKE', value: `%${searchTerm}%` } },
453
+ ],
454
+ },
455
+ orderBy: [{ field: 'name', direction: 'ASC' }],
456
+ })
457
+ }
458
+ ```
459
+
460
+ ### 3. Analytics and Reporting
461
+
462
+ ```typescript
463
+ // Get user statistics by status
464
+ const getUserStats = async () => {
465
+ return findMany({
466
+ tableName: 'users',
467
+ dbClient,
468
+ select: ['status', 'COUNT(*) as count'],
469
+ groupBy: ['status'],
470
+ })
471
+ }
472
+
473
+ // Get monthly user registrations
474
+ const getMonthlyRegistrations = async (year: number) => {
475
+ return findMany({
476
+ tableName: 'users',
477
+ dbClient,
478
+ select: ['EXTRACT(MONTH FROM created_at) as month', 'COUNT(*) as count'],
479
+ where: {
480
+ created_at: {
481
+ operator: 'BETWEEN',
482
+ value: [new Date(`${year}-01-01`), new Date(`${year}-12-31`)],
483
+ },
484
+ },
485
+ groupBy: ['EXTRACT(MONTH FROM created_at)'],
486
+ orderBy: [{ field: 'month', direction: 'ASC' }],
487
+ })
488
+ }
489
+ ```
490
+
491
+ ### 4. Data Export
492
+
493
+ ```typescript
494
+ const exportUsers = async (filters: UserSearchParams) => {
495
+ const where: Conditions<User> = {}
496
+
497
+ if (filters.status) {
498
+ where.status = { operator: '=', value: filters.status }
499
+ }
500
+
501
+ if (filters.createdAfter) {
502
+ where.created_at = { operator: '>=', value: filters.createdAfter }
503
+ }
504
+
505
+ return findMany({
506
+ tableName: 'users',
507
+ dbClient,
508
+ select: ['id', 'name', 'email', 'status', 'created_at'],
509
+ where,
510
+ orderBy: [{ field: 'created_at', direction: 'DESC' }],
511
+ })
512
+ }
513
+ ```
514
+
515
+ ## Performance Considerations
516
+
517
+ ### 1. Indexing Strategy
518
+
519
+ ```sql
520
+ -- Composite indexes for common query patterns
521
+ CREATE INDEX idx_users_status_created_at ON users(status, created_at);
522
+ CREATE INDEX idx_users_age_status ON users(age, status);
523
+
524
+ -- Partial indexes for specific conditions
525
+ CREATE INDEX idx_users_active_created_at ON users(created_at)
526
+ WHERE status = 'active';
527
+ ```
528
+
529
+ ### 2. Query Optimization
530
+
531
+ ```typescript
532
+ // Good: Use indexed fields in WHERE clause
533
+ const users = await findMany({
534
+ tableName: 'users',
535
+ dbClient,
536
+ where: {
537
+ status: { operator: '=', value: 'active' }, // Indexed field
538
+ created_at: { operator: '>=', value: new Date('2023-01-01') }, // Indexed field
539
+ },
540
+ })
541
+
542
+ // Avoid: Using non-indexed fields in WHERE clause
543
+ const users = await findMany({
544
+ tableName: 'users',
545
+ dbClient,
546
+ where: {
547
+ bio: { operator: 'LIKE', value: '%developer%' }, // Non-indexed field
548
+ },
549
+ })
550
+ ```
551
+
552
+ ### 3. Memory Management
553
+
554
+ ```typescript
555
+ // For large datasets, process in batches
556
+ const processUsersInBatches = async (batchSize: number = 1000) => {
557
+ let offset = 0
558
+ let hasMore = true
559
+
560
+ while (hasMore) {
561
+ const users = await findMany({
562
+ tableName: 'users',
563
+ dbClient,
564
+ limit: batchSize,
565
+ offset,
566
+ orderBy: [{ field: 'id', direction: 'ASC' }],
567
+ })
568
+
569
+ if (users.length === 0) {
570
+ hasMore = false
571
+ } else {
572
+ // Process batch
573
+ await processBatch(users)
574
+ offset += batchSize
575
+ }
576
+ }
577
+ }
578
+ ```
579
+
580
+ ## Error Messages
581
+
582
+ Common error messages you might encounter:
583
+
584
+ - `Table name is required` - The `tableName` parameter is missing
585
+ - `DB client is required` - The `dbClient` parameter is missing
586
+ - Database-specific errors from the underlying database driver
587
+ - Memory errors when trying to load too many records at once