@starbemtech/star-db-query-builder 1.0.38 → 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 (85) 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 +1309 -129
  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/block-navigation.js +1 -1
  10. package/coverage/lcov-report/index.html +41 -11
  11. package/coverage/lcov-report/mysqlClient.ts.html +685 -0
  12. package/coverage/lcov-report/pgClient.ts.html +823 -0
  13. package/coverage/lcov-report/sorter.js +21 -7
  14. package/coverage/lcov.info +533 -0
  15. package/coverage/mysqlClient.ts.html +685 -0
  16. package/coverage/pgClient.ts.html +823 -0
  17. package/coverage/prettify.css +1 -0
  18. package/coverage/prettify.js +2 -0
  19. package/coverage/sort-arrow-sprite.png +0 -0
  20. package/coverage/sorter.js +210 -0
  21. package/dist/index.d.ts +5 -4
  22. package/dist/index.js +1 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/src/core/repository.d.ts +374 -0
  25. package/dist/src/core/repository.js +677 -0
  26. package/dist/src/core/repository.js.map +1 -0
  27. package/dist/src/{default → core}/types.d.ts +8 -0
  28. package/dist/src/{default → core}/types.js.map +1 -1
  29. package/dist/src/core/utils.d.ts +133 -0
  30. package/dist/src/{default → core}/utils.js +167 -0
  31. package/dist/src/core/utils.js.map +1 -0
  32. package/dist/src/db/IDatabaseClient.d.ts +10 -1
  33. package/dist/src/db/initDb.d.ts +120 -1
  34. package/dist/src/db/initDb.js +119 -0
  35. package/dist/src/db/initDb.js.map +1 -1
  36. package/dist/src/db/mysqlClient.d.ts +23 -1
  37. package/dist/src/db/mysqlClient.js +115 -0
  38. package/dist/src/db/mysqlClient.js.map +1 -1
  39. package/dist/src/db/pgClient.d.ts +22 -1
  40. package/dist/src/db/pgClient.js +127 -0
  41. package/dist/src/db/pgClient.js.map +1 -1
  42. package/dist/src/monitor/monitor.d.ts +6 -1
  43. package/dist/src/monitor/monitor.js +5 -0
  44. package/dist/src/monitor/monitor.js.map +1 -1
  45. package/dist/src/setupTests.d.ts +26 -0
  46. package/dist/src/setupTests.js +43 -0
  47. package/dist/src/setupTests.js.map +1 -0
  48. package/docs/INDEX.md +145 -0
  49. package/docs/methods/findFirst.md +394 -0
  50. package/docs/methods/findMany.md +587 -0
  51. package/docs/methods/insert.md +536 -0
  52. package/docs/methods/insertMany.md +627 -0
  53. package/docs/methods/joins.md +781 -0
  54. package/docs/methods/rawQuery.md +284 -0
  55. package/docs/methods/transactions.md +737 -0
  56. package/eslint.config.mjs +77 -0
  57. package/index.ts +5 -4
  58. package/jest.config.ts +23 -28
  59. package/package.json +53 -30
  60. package/scripts/release.sh +123 -0
  61. package/src/core/repository.ts +865 -0
  62. package/src/{default → core}/types.ts +10 -1
  63. package/src/{default → core}/utils.ts +168 -0
  64. package/src/db/IDatabaseClient.ts +11 -1
  65. package/src/db/__tests__/mysqlClient.test.ts +262 -0
  66. package/src/db/__tests__/pgClient.test.ts +260 -0
  67. package/src/db/initDb.ts +120 -1
  68. package/src/db/mysqlClient.ts +119 -3
  69. package/src/db/pgClient.ts +131 -3
  70. package/src/monitor/monitor.ts +5 -0
  71. package/src/setupTests.ts +45 -0
  72. package/tsconfig.test.json +21 -0
  73. package/.eslintignore +0 -4
  74. package/.eslintrc.json +0 -32
  75. package/coverage/clover.xml +0 -6
  76. package/coverage/coverage-final.json +0 -1
  77. package/dist/.eslintrc.json +0 -32
  78. package/dist/src/default/genericRepository.d.ts +0 -20
  79. package/dist/src/default/genericRepository.js +0 -192
  80. package/dist/src/default/genericRepository.js.map +0 -1
  81. package/dist/src/default/utils.d.ts +0 -9
  82. package/dist/src/default/utils.js.map +0 -1
  83. package/index.d.ts +0 -60
  84. package/src/default/genericRepository.ts +0 -320
  85. /package/dist/src/{default → core}/types.js +0 -0
@@ -0,0 +1,627 @@
1
+ # insertMany
2
+
3
+ Inserts multiple records into a database table in a single operation and returns the inserted records.
4
+
5
+ ## Signature
6
+
7
+ ```typescript
8
+ insertMany<P, R>({
9
+ tableName: string,
10
+ dbClient: IDatabaseClient,
11
+ data: P[],
12
+ returning?: string[]
13
+ }): Promise<R[]>
14
+ ```
15
+
16
+ ## Parameters
17
+
18
+ | Parameter | Type | Required | Description |
19
+ | ----------- | ----------------- | -------- | ---------------------------------------------- |
20
+ | `tableName` | `string` | ✅ | Name of the database table |
21
+ | `dbClient` | `IDatabaseClient` | ✅ | Database client instance |
22
+ | `data` | `P[]` | ✅ | Array of objects containing the data to insert |
23
+ | `returning` | `string[]` | ❌ | Array of field names to return after insertion |
24
+
25
+ ## Return Value
26
+
27
+ - **Type**: `Promise<R[]>`
28
+ - **Description**: Returns an array of inserted records with all fields (including auto-generated ones)
29
+
30
+ ## Auto-Generated Fields
31
+
32
+ The `insertMany` method automatically adds the following fields to every record:
33
+
34
+ - **`id`**: A UUID v4 string for each record
35
+ - **`updated_at`**: Current timestamp for each record
36
+
37
+ ## Examples
38
+
39
+ ### Basic Usage
40
+
41
+ ```typescript
42
+ import { insertMany } from '@starbemtech/star-db-query-builder'
43
+
44
+ // Insert multiple users
45
+ const users = await insertMany({
46
+ tableName: 'users',
47
+ dbClient,
48
+ data: [
49
+ { name: 'John Doe', email: 'john@example.com', age: 30 },
50
+ { name: 'Jane Smith', email: 'jane@example.com', age: 25 },
51
+ { name: 'Bob Johnson', email: 'bob@example.com', age: 35 },
52
+ ],
53
+ })
54
+
55
+ console.log(users)
56
+ // [
57
+ // {
58
+ // id: '550e8400-e29b-41d4-a716-446655440000',
59
+ // name: 'John Doe',
60
+ // email: 'john@example.com',
61
+ // age: 30,
62
+ // created_at: '2023-12-01T10:00:00.000Z',
63
+ // updated_at: '2023-12-01T10:00:00.000Z'
64
+ // },
65
+ // {
66
+ // id: '550e8400-e29b-41d4-a716-446655440001',
67
+ // name: 'Jane Smith',
68
+ // email: 'jane@example.com',
69
+ // age: 25,
70
+ // created_at: '2023-12-01T10:00:00.000Z',
71
+ // updated_at: '2023-12-01T10:00:00.000Z'
72
+ // },
73
+ // // ... more users
74
+ // ]
75
+ ```
76
+
77
+ ### With Specific Returning Fields
78
+
79
+ ```typescript
80
+ // Return only specific fields after insertion
81
+ const users = await insertMany({
82
+ tableName: 'users',
83
+ dbClient,
84
+ data: [
85
+ { name: 'John Doe', email: 'john@example.com' },
86
+ { name: 'Jane Smith', email: 'jane@example.com' },
87
+ ],
88
+ returning: ['id', 'name', 'email', 'created_at'],
89
+ })
90
+
91
+ console.log(users)
92
+ // [
93
+ // {
94
+ // id: '550e8400-e29b-41d4-a716-446655440000',
95
+ // name: 'John Doe',
96
+ // email: 'john@example.com',
97
+ // created_at: '2023-12-01T10:00:00.000Z'
98
+ // },
99
+ // // ... more users
100
+ // ]
101
+ ```
102
+
103
+ ### TypeScript Usage
104
+
105
+ ```typescript
106
+ interface UserData {
107
+ name: string
108
+ email: string
109
+ age: number
110
+ bio?: string
111
+ }
112
+
113
+ interface User {
114
+ id: string
115
+ name: string
116
+ email: string
117
+ age: number
118
+ bio?: string
119
+ created_at: Date
120
+ updated_at: Date
121
+ }
122
+
123
+ // Typed usage
124
+ const usersData: UserData[] = [
125
+ { name: 'John Doe', email: 'john@example.com', age: 30 },
126
+ { name: 'Jane Smith', email: 'jane@example.com', age: 25 },
127
+ ]
128
+
129
+ const users: User[] = await insertMany<UserData, User>({
130
+ tableName: 'users',
131
+ dbClient,
132
+ data: usersData,
133
+ })
134
+
135
+ console.log(`Created ${users.length} users`)
136
+ ```
137
+
138
+ ### Inserting with Mixed Data Types
139
+
140
+ ```typescript
141
+ // Insert with various data types
142
+ const users = await insertMany({
143
+ tableName: 'users',
144
+ dbClient,
145
+ data: [
146
+ {
147
+ name: 'John Doe',
148
+ email: 'john@example.com',
149
+ age: 30,
150
+ is_active: true,
151
+ preferences: { theme: 'dark', language: 'en' },
152
+ birth_date: new Date('1990-01-01'),
153
+ },
154
+ {
155
+ name: 'Jane Smith',
156
+ email: 'jane@example.com',
157
+ age: 25,
158
+ is_active: false,
159
+ preferences: { theme: 'light', language: 'es' },
160
+ birth_date: new Date('1995-05-15'),
161
+ },
162
+ ],
163
+ })
164
+ ```
165
+
166
+ ### Error Handling
167
+
168
+ ```typescript
169
+ try {
170
+ const users = await insertMany({
171
+ tableName: 'users',
172
+ dbClient,
173
+ data: [
174
+ { name: 'John Doe', email: 'john@example.com' },
175
+ { name: 'Jane Smith', email: 'jane@example.com' },
176
+ ],
177
+ })
178
+
179
+ console.log(`Successfully created ${users.length} users`)
180
+ } catch (error) {
181
+ if (error.message.includes('duplicate key')) {
182
+ console.error('One or more users already exist')
183
+ } else {
184
+ console.error('Failed to create users:', error.message)
185
+ }
186
+ }
187
+ ```
188
+
189
+ ### Batch Processing Large Datasets
190
+
191
+ ```typescript
192
+ const insertUsersInBatches = async (
193
+ allUsersData: any[],
194
+ batchSize: number = 100
195
+ ) => {
196
+ const results = []
197
+
198
+ for (let i = 0; i < allUsersData.length; i += batchSize) {
199
+ const batch = allUsersData.slice(i, i + batchSize)
200
+
201
+ try {
202
+ const batchResults = await insertMany({
203
+ tableName: 'users',
204
+ dbClient,
205
+ data: batch,
206
+ })
207
+
208
+ results.push(...batchResults)
209
+ console.log(`Processed batch ${Math.floor(i / batchSize) + 1}`)
210
+ } catch (error) {
211
+ console.error(
212
+ `Failed to process batch starting at index ${i}:`,
213
+ error.message
214
+ )
215
+ // Continue with next batch or handle as needed
216
+ }
217
+ }
218
+
219
+ return results
220
+ }
221
+
222
+ // Usage
223
+ const allUsers = await insertUsersInBatches(largeUserDataset, 50)
224
+ ```
225
+
226
+ ## Generated SQL Examples
227
+
228
+ ### PostgreSQL
229
+
230
+ ```sql
231
+ INSERT INTO users (id, name, email, age, updated_at)
232
+ VALUES
233
+ ($1, $2, $3, $4, $5),
234
+ ($6, $7, $8, $9, $10),
235
+ ($11, $12, $13, $14, $15)
236
+ RETURNING *
237
+ ```
238
+
239
+ ### MySQL
240
+
241
+ ```sql
242
+ INSERT INTO users (id, name, email, age, updated_at)
243
+ VALUES
244
+ (?, ?, ?, ?, ?),
245
+ (?, ?, ?, ?, ?),
246
+ (?, ?, ?, ?, ?)
247
+
248
+ SELECT * FROM users
249
+ WHERE id IN (?, ?, ?)
250
+ ORDER BY FIELD(id, ?, ?, ?)
251
+ ```
252
+
253
+ ### With Specific Returning Fields (PostgreSQL)
254
+
255
+ ```sql
256
+ INSERT INTO users (id, name, email, age, updated_at)
257
+ VALUES
258
+ ($1, $2, $3, $4, $5),
259
+ ($6, $7, $8, $9, $10)
260
+ RETURNING id, name, email, created_at
261
+ ```
262
+
263
+ ## Best Practices
264
+
265
+ ### 1. Use Appropriate Batch Sizes
266
+
267
+ ```typescript
268
+ // Good: Use reasonable batch sizes (50-1000 records)
269
+ const users = await insertMany({
270
+ tableName: 'users',
271
+ dbClient,
272
+ data: userData.slice(0, 100), // Limit to 100 records
273
+ })
274
+
275
+ // Avoid: Inserting too many records at once
276
+ const users = await insertMany({
277
+ tableName: 'users',
278
+ dbClient,
279
+ data: hugeUserArray, // Could cause memory issues
280
+ })
281
+ ```
282
+
283
+ ### 2. Validate Data Before Insertion
284
+
285
+ ```typescript
286
+ const validateUserData = (userData: any) => {
287
+ if (!userData.name || !userData.email) {
288
+ throw new Error('Name and email are required')
289
+ }
290
+
291
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
292
+ if (!emailRegex.test(userData.email)) {
293
+ throw new Error(`Invalid email format: ${userData.email}`)
294
+ }
295
+
296
+ return userData
297
+ }
298
+
299
+ const createUsers = async (usersData: any[]) => {
300
+ // Validate all data before insertion
301
+ const validatedData = usersData.map(validateUserData)
302
+
303
+ return insertMany({
304
+ tableName: 'users',
305
+ dbClient,
306
+ data: validatedData,
307
+ })
308
+ }
309
+ ```
310
+
311
+ ### 3. Handle Partial Failures
312
+
313
+ ```typescript
314
+ const createUsersWithErrorHandling = async (usersData: any[]) => {
315
+ const successfulInserts = []
316
+ const failedInserts = []
317
+
318
+ // Process in smaller batches to minimize impact of failures
319
+ const batchSize = 50
320
+
321
+ for (let i = 0; i < usersData.length; i += batchSize) {
322
+ const batch = usersData.slice(i, i + batchSize)
323
+
324
+ try {
325
+ const results = await insertMany({
326
+ tableName: 'users',
327
+ dbClient,
328
+ data: batch,
329
+ })
330
+
331
+ successfulInserts.push(...results)
332
+ } catch (error) {
333
+ // Log the error and continue with individual inserts
334
+ console.error(`Batch failed:`, error.message)
335
+
336
+ for (const userData of batch) {
337
+ try {
338
+ const result = await insert({
339
+ tableName: 'users',
340
+ dbClient,
341
+ data: userData,
342
+ })
343
+ successfulInserts.push(result)
344
+ } catch (individualError) {
345
+ failedInserts.push({ data: userData, error: individualError.message })
346
+ }
347
+ }
348
+ }
349
+ }
350
+
351
+ return {
352
+ successful: successfulInserts,
353
+ failed: failedInserts,
354
+ }
355
+ }
356
+ ```
357
+
358
+ ### 4. Use TypeScript for Type Safety
359
+
360
+ ```typescript
361
+ interface BulkUserData {
362
+ name: string
363
+ email: string
364
+ age: number
365
+ department?: string
366
+ }
367
+
368
+ interface BulkUserResult {
369
+ id: string
370
+ name: string
371
+ email: string
372
+ age: number
373
+ department?: string
374
+ created_at: Date
375
+ updated_at: Date
376
+ }
377
+
378
+ const createBulkUsers = async (
379
+ usersData: BulkUserData[]
380
+ ): Promise<BulkUserResult[]> => {
381
+ return insertMany<BulkUserData, BulkUserResult>({
382
+ tableName: 'users',
383
+ dbClient,
384
+ data: usersData,
385
+ })
386
+ }
387
+ ```
388
+
389
+ ### 5. Optimize for Performance
390
+
391
+ ```typescript
392
+ // Good: Use insertMany for bulk operations
393
+ const importUsers = async (csvData: any[]) => {
394
+ const batchSize = 1000
395
+ const results = []
396
+
397
+ for (let i = 0; i < csvData.length; i += batchSize) {
398
+ const batch = csvData.slice(i, i + batchSize)
399
+
400
+ const batchResults = await insertMany({
401
+ tableName: 'users',
402
+ dbClient,
403
+ data: batch,
404
+ })
405
+
406
+ results.push(...batchResults)
407
+ }
408
+
409
+ return results
410
+ }
411
+
412
+ // Avoid: Multiple individual inserts
413
+ const importUsersSlow = async (csvData: any[]) => {
414
+ const results = []
415
+
416
+ for (const userData of csvData) {
417
+ const result = await insert({
418
+ tableName: 'users',
419
+ dbClient,
420
+ data: userData,
421
+ })
422
+ results.push(result)
423
+ }
424
+
425
+ return results
426
+ }
427
+ ```
428
+
429
+ ## Common Use Cases
430
+
431
+ ### 1. Data Import/CSV Processing
432
+
433
+ ```typescript
434
+ const importUsersFromCSV = async (csvFilePath: string) => {
435
+ const csvData = await parseCSV(csvFilePath)
436
+
437
+ // Transform CSV data to match database schema
438
+ const usersData = csvData.map((row) => ({
439
+ name: row.name,
440
+ email: row.email,
441
+ age: parseInt(row.age),
442
+ department: row.department,
443
+ }))
444
+
445
+ // Insert in batches
446
+ const batchSize = 500
447
+ const results = []
448
+
449
+ for (let i = 0; i < usersData.length; i += batchSize) {
450
+ const batch = usersData.slice(i, i + batchSize)
451
+
452
+ const batchResults = await insertMany({
453
+ tableName: 'users',
454
+ dbClient,
455
+ data: batch,
456
+ returning: ['id', 'name', 'email'],
457
+ })
458
+
459
+ results.push(...batchResults)
460
+ }
461
+
462
+ return results
463
+ }
464
+ ```
465
+
466
+ ### 2. Bulk User Creation
467
+
468
+ ```typescript
469
+ const createUsersFromTemplate = async (template: any, count: number) => {
470
+ const usersData = Array.from({ length: count }, (_, index) => ({
471
+ ...template,
472
+ name: `${template.name} ${index + 1}`,
473
+ email: `${template.emailPrefix}${index + 1}@example.com`,
474
+ }))
475
+
476
+ return insertMany({
477
+ tableName: 'users',
478
+ dbClient,
479
+ data: usersData,
480
+ })
481
+ }
482
+
483
+ // Usage
484
+ const testUsers = await createUsersFromTemplate(
485
+ { name: 'Test User', emailPrefix: 'testuser', age: 25 },
486
+ 100
487
+ )
488
+ ```
489
+
490
+ ### 3. Data Migration
491
+
492
+ ```typescript
493
+ const migrateUsers = async (oldUsers: any[]) => {
494
+ // Transform old data format to new format
495
+ const newUsersData = oldUsers.map((oldUser) => ({
496
+ name: oldUser.full_name,
497
+ email: oldUser.email_address,
498
+ age: oldUser.user_age,
499
+ status: oldUser.is_active ? 'active' : 'inactive',
500
+ migrated_at: new Date(),
501
+ }))
502
+
503
+ return insertMany({
504
+ tableName: 'users',
505
+ dbClient,
506
+ data: newUsersData,
507
+ returning: ['id', 'name', 'email'],
508
+ })
509
+ }
510
+ ```
511
+
512
+ ### 4. Seed Data
513
+
514
+ ```typescript
515
+ const seedInitialData = async () => {
516
+ // Seed users
517
+ const users = await insertMany({
518
+ tableName: 'users',
519
+ dbClient,
520
+ data: [
521
+ { name: 'Admin User', email: 'admin@example.com', role: 'admin' },
522
+ { name: 'Regular User', email: 'user@example.com', role: 'user' },
523
+ ],
524
+ })
525
+
526
+ // Seed categories
527
+ const categories = await insertMany({
528
+ tableName: 'categories',
529
+ dbClient,
530
+ data: [
531
+ { name: 'Technology', description: 'Tech-related content' },
532
+ { name: 'Business', description: 'Business-related content' },
533
+ ],
534
+ })
535
+
536
+ return { users, categories }
537
+ }
538
+ ```
539
+
540
+ ## Performance Considerations
541
+
542
+ ### 1. Batch Size Optimization
543
+
544
+ ```typescript
545
+ // Test different batch sizes to find optimal performance
546
+ const findOptimalBatchSize = async (testData: any[]) => {
547
+ const batchSizes = [50, 100, 500, 1000, 2000]
548
+ const results = []
549
+
550
+ for (const batchSize of batchSizes) {
551
+ const startTime = Date.now()
552
+
553
+ try {
554
+ await insertMany({
555
+ tableName: 'users',
556
+ dbClient,
557
+ data: testData.slice(0, batchSize),
558
+ })
559
+
560
+ const endTime = Date.now()
561
+ results.push({
562
+ batchSize,
563
+ time: endTime - startTime,
564
+ recordsPerSecond: (batchSize / (endTime - startTime)) * 1000,
565
+ })
566
+ } catch (error) {
567
+ results.push({ batchSize, error: error.message })
568
+ }
569
+ }
570
+
571
+ return results
572
+ }
573
+ ```
574
+
575
+ ### 2. Memory Management
576
+
577
+ ```typescript
578
+ // Process large datasets without loading everything into memory
579
+ const processLargeDataset = async (dataStream: any[]) => {
580
+ const batchSize = 1000
581
+ let processedCount = 0
582
+
583
+ for (let i = 0; i < dataStream.length; i += batchSize) {
584
+ const batch = dataStream.slice(i, i + batchSize)
585
+
586
+ await insertMany({
587
+ tableName: 'users',
588
+ dbClient,
589
+ data: batch,
590
+ })
591
+
592
+ processedCount += batch.length
593
+ console.log(`Processed ${processedCount} records`)
594
+
595
+ // Optional: Add delay to prevent overwhelming the database
596
+ if (i + batchSize < dataStream.length) {
597
+ await new Promise((resolve) => setTimeout(resolve, 100))
598
+ }
599
+ }
600
+ }
601
+ ```
602
+
603
+ ### 3. Index Considerations
604
+
605
+ ```sql
606
+ -- Ensure proper indexes exist for bulk operations
607
+ CREATE INDEX idx_users_email ON users(email);
608
+ CREATE INDEX idx_users_created_at ON users(created_at);
609
+
610
+ -- Consider temporarily disabling indexes during large bulk inserts
611
+ -- (PostgreSQL example)
612
+ -- DROP INDEX idx_users_email;
613
+ -- INSERT INTO users ... (bulk insert)
614
+ -- CREATE INDEX idx_users_email ON users(email);
615
+ ```
616
+
617
+ ## Error Messages
618
+
619
+ Common error messages you might encounter:
620
+
621
+ - `Table name is required` - The `tableName` parameter is missing
622
+ - `DB client is required` - The `dbClient` parameter is missing
623
+ - `Data array is required and cannot be empty` - The `data` parameter is missing or empty
624
+ - `duplicate key value violates unique constraint` - One or more records have duplicate unique values
625
+ - `column "field_name" of relation "table_name" does not exist` - Invalid field name
626
+ - `null value in column "field_name" violates not-null constraint` - Required field is missing
627
+ - Memory errors when trying to insert too many records at once