@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.
- package/.github/workflows/publish.yml +118 -0
- package/ARCHITECTURE.md +313 -0
- package/CHANGELOG.md +67 -0
- package/README.md +1309 -129
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +131 -0
- package/coverage/lcov-report/block-navigation.js +1 -1
- package/coverage/lcov-report/index.html +41 -11
- package/coverage/lcov-report/mysqlClient.ts.html +685 -0
- package/coverage/lcov-report/pgClient.ts.html +823 -0
- package/coverage/lcov-report/sorter.js +21 -7
- package/coverage/lcov.info +533 -0
- package/coverage/mysqlClient.ts.html +685 -0
- package/coverage/pgClient.ts.html +823 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/src/core/repository.d.ts +374 -0
- package/dist/src/core/repository.js +677 -0
- package/dist/src/core/repository.js.map +1 -0
- package/dist/src/{default → core}/types.d.ts +8 -0
- package/dist/src/{default → core}/types.js.map +1 -1
- package/dist/src/core/utils.d.ts +133 -0
- package/dist/src/{default → core}/utils.js +167 -0
- package/dist/src/core/utils.js.map +1 -0
- package/dist/src/db/IDatabaseClient.d.ts +10 -1
- package/dist/src/db/initDb.d.ts +120 -1
- package/dist/src/db/initDb.js +119 -0
- package/dist/src/db/initDb.js.map +1 -1
- package/dist/src/db/mysqlClient.d.ts +23 -1
- package/dist/src/db/mysqlClient.js +115 -0
- package/dist/src/db/mysqlClient.js.map +1 -1
- package/dist/src/db/pgClient.d.ts +22 -1
- package/dist/src/db/pgClient.js +127 -0
- package/dist/src/db/pgClient.js.map +1 -1
- package/dist/src/monitor/monitor.d.ts +6 -1
- package/dist/src/monitor/monitor.js +5 -0
- package/dist/src/monitor/monitor.js.map +1 -1
- package/dist/src/setupTests.d.ts +26 -0
- package/dist/src/setupTests.js +43 -0
- package/dist/src/setupTests.js.map +1 -0
- package/docs/INDEX.md +145 -0
- package/docs/methods/findFirst.md +394 -0
- package/docs/methods/findMany.md +587 -0
- package/docs/methods/insert.md +536 -0
- package/docs/methods/insertMany.md +627 -0
- package/docs/methods/joins.md +781 -0
- package/docs/methods/rawQuery.md +284 -0
- package/docs/methods/transactions.md +737 -0
- package/eslint.config.mjs +77 -0
- package/index.ts +5 -4
- package/jest.config.ts +23 -28
- package/package.json +53 -30
- package/scripts/release.sh +123 -0
- package/src/core/repository.ts +865 -0
- package/src/{default → core}/types.ts +10 -1
- package/src/{default → core}/utils.ts +168 -0
- package/src/db/IDatabaseClient.ts +11 -1
- package/src/db/__tests__/mysqlClient.test.ts +262 -0
- package/src/db/__tests__/pgClient.test.ts +260 -0
- package/src/db/initDb.ts +120 -1
- package/src/db/mysqlClient.ts +119 -3
- package/src/db/pgClient.ts +131 -3
- package/src/monitor/monitor.ts +5 -0
- package/src/setupTests.ts +45 -0
- package/tsconfig.test.json +21 -0
- package/.eslintignore +0 -4
- package/.eslintrc.json +0 -32
- package/coverage/clover.xml +0 -6
- package/coverage/coverage-final.json +0 -1
- package/dist/.eslintrc.json +0 -32
- package/dist/src/default/genericRepository.d.ts +0 -20
- package/dist/src/default/genericRepository.js +0 -192
- package/dist/src/default/genericRepository.js.map +0 -1
- package/dist/src/default/utils.d.ts +0 -9
- package/dist/src/default/utils.js.map +0 -1
- package/index.d.ts +0 -60
- package/src/default/genericRepository.ts +0 -320
- /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
|