firestore-batch-updater 1.5.0 → 1.6.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/README.ko.md +61 -0
- package/README.md +50 -0
- package/dist/index.d.mts +49 -1
- package/dist/index.d.ts +49 -1
- package/dist/index.js +107 -0
- package/dist/index.mjs +107 -0
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
- 전체 문서 조회 - `getAll()`로 매칭되는 모든 문서 데이터 조회
|
|
21
21
|
- 집계 쿼리 - `aggregate()`로 서버 사이드 `sum`, `average`, `count` 연산
|
|
22
22
|
- 커서 페이지네이션 - `paginate()`로 메모리 효율적인 페이지 단위 조회
|
|
23
|
+
- ID 직접 조회 - `getOne()`으로 문서 ID로 빠른 조회
|
|
24
|
+
- 벌크 업데이트 - `bulkUpdate()`로 여러 문서에 각기 다른 데이터 업데이트
|
|
23
25
|
- FieldValue 지원 - `increment()`, `arrayUnion()`, `delete()`, `serverTimestamp()` 등 사용 가능
|
|
24
26
|
- 서브컬렉션 & 컬렉션 그룹 - 서브컬렉션 쿼리 또는 동일 이름의 모든 컬렉션 쿼리
|
|
25
27
|
- Dry Run 모드 - 실제 변경 없이 작업 시뮬레이션
|
|
@@ -93,6 +95,7 @@ console.log(`${result.successCount}개 문서 업데이트 완료`);
|
|
|
93
95
|
| `count()` | 매칭되는 문서 개수 조회 | `CountResult` |
|
|
94
96
|
| `exists()` | 매칭되는 문서 존재 여부 확인 | `boolean` |
|
|
95
97
|
| `findOne()` | 첫 번째 매칭 문서 조회 | `{ id, data } \| null` |
|
|
98
|
+
| `getOne(id)` | ID로 문서 직접 조회 | `{ id, data } \| null` |
|
|
96
99
|
| `getAll()` | 모든 매칭 문서 조회 | `{ id, data }[]` |
|
|
97
100
|
| `preview(data)` | 업데이트 전 미리보기 | `PreviewResult` |
|
|
98
101
|
| `update(data, options?)` | 매칭되는 문서 업데이트 | `UpdateResult` |
|
|
@@ -104,6 +107,7 @@ console.log(`${result.successCount}개 문서 업데이트 완료`);
|
|
|
104
107
|
| `deleteOne()` | 첫 번째 매칭 문서 삭제 | `{ success, id }` |
|
|
105
108
|
| `aggregate(spec)` | sum/average/count 집계 쿼리 | `AggregateResult` |
|
|
106
109
|
| `paginate(options)` | 커서 기반 페이지네이션 | `PaginateResult` |
|
|
110
|
+
| `bulkUpdate(updates, options?)` | 여러 문서에 각기 다른 데이터 업데이트 | `BulkUpdateResult` |
|
|
107
111
|
| `getFields(field)` | 특정 필드 값 조회 | `FieldValueResult[]` |
|
|
108
112
|
|
|
109
113
|
### 옵션
|
|
@@ -153,6 +157,7 @@ console.log(`${result.successCount}개 문서 업데이트 완료`);
|
|
|
153
157
|
| `DeleteResult` | `successCount`, `failureCount`, `totalCount`, `deletedIds[]`, `failedDocIds?`, `logFilePath?` |
|
|
154
158
|
| `AggregateResult` | `{ [alias]: number \| null }` |
|
|
155
159
|
| `PaginateResult` | `docs[]`, `nextCursor`, `hasMore` |
|
|
160
|
+
| `BulkUpdateResult` | `successCount`, `failureCount`, `totalCount`, `failedDocIds?`, `logFilePath?` |
|
|
156
161
|
| `FieldValueResult` | `id`, `value` |
|
|
157
162
|
|
|
158
163
|
## 사용 예시
|
|
@@ -511,6 +516,62 @@ const page = await updater
|
|
|
511
516
|
.paginate({ pageSize: 50 });
|
|
512
517
|
```
|
|
513
518
|
|
|
519
|
+
### ID로 문서 조회
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
// 문서 ID로 직접 조회 (쿼리 필터 없이 가장 빠름)
|
|
523
|
+
const user = await updater.collection("users").getOne("user-123");
|
|
524
|
+
|
|
525
|
+
if (user) {
|
|
526
|
+
console.log(`찾음: ${user.data.name} (${user.data.email})`);
|
|
527
|
+
} else {
|
|
528
|
+
console.log("사용자를 찾을 수 없음");
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// select와 함께 사용하여 특정 필드만 가져오기
|
|
532
|
+
const userBasic = await updater
|
|
533
|
+
.collection("users")
|
|
534
|
+
.select("name", "email")
|
|
535
|
+
.getOne("user-123");
|
|
536
|
+
|
|
537
|
+
// 서브컬렉션에서 문서 조회
|
|
538
|
+
const order = await updater
|
|
539
|
+
.collection("users/user-123/orders")
|
|
540
|
+
.getOne("order-456");
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### 벌크 업데이트
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
// 여러 문서를 각각 다른 데이터로 업데이트
|
|
547
|
+
const result = await updater.collection("users").bulkUpdate([
|
|
548
|
+
{ id: "user-1", data: { name: "Alice", age: 30 } },
|
|
549
|
+
{ id: "user-2", data: { name: "Bob", status: "active" } },
|
|
550
|
+
{ id: "user-3", data: { email: "charlie@example.com" } },
|
|
551
|
+
]);
|
|
552
|
+
|
|
553
|
+
console.log(`성공: ${result.successCount}, 실패: ${result.failureCount}`);
|
|
554
|
+
|
|
555
|
+
// 진행 상황 콜백과 함께 사용
|
|
556
|
+
const result = await updater.collection("products").bulkUpdate(
|
|
557
|
+
[
|
|
558
|
+
{ id: "prod-1", data: { price: 29.99, stock: 100 } },
|
|
559
|
+
{ id: "prod-2", data: { price: 49.99, stock: 50 } },
|
|
560
|
+
// ... 더 많은 업데이트
|
|
561
|
+
],
|
|
562
|
+
{
|
|
563
|
+
onProgress: (progress) => {
|
|
564
|
+
console.log(`${progress.processedCount}/${progress.totalCount} 처리됨`);
|
|
565
|
+
},
|
|
566
|
+
}
|
|
567
|
+
);
|
|
568
|
+
|
|
569
|
+
// 실패 처리
|
|
570
|
+
if (result.failureCount > 0) {
|
|
571
|
+
console.log("실패한 문서 ID:", result.failedDocIds);
|
|
572
|
+
}
|
|
573
|
+
```
|
|
574
|
+
|
|
514
575
|
### Dry Run 모드
|
|
515
576
|
|
|
516
577
|
```typescript
|
package/README.md
CHANGED
|
@@ -20,6 +20,8 @@ English | [한국어](./README.ko.md)
|
|
|
20
20
|
- Get all documents - Use `getAll()` to retrieve all matching documents with data
|
|
21
21
|
- Aggregation - Use `aggregate()` for server-side `sum`, `average`, and `count` operations
|
|
22
22
|
- Cursor pagination - Use `paginate()` for memory-efficient page-by-page iteration
|
|
23
|
+
- Direct ID lookup - Use `getOne()` for fast document retrieval by ID
|
|
24
|
+
- Bulk updates - Use `bulkUpdate()` to update multiple documents with different data each
|
|
23
25
|
- FieldValue support - Use `increment()`, `arrayUnion()`, `delete()`, `serverTimestamp()`, etc.
|
|
24
26
|
- Subcollection & Collection Group - Query subcollections or all collections with the same name
|
|
25
27
|
- Dry run mode - Simulate operations without making changes
|
|
@@ -93,6 +95,7 @@ console.log(`Updated ${result.successCount} documents`);
|
|
|
93
95
|
| `count()` | Count matching documents | `CountResult` |
|
|
94
96
|
| `exists()` | Check if matching documents exist | `boolean` |
|
|
95
97
|
| `findOne()` | Find first matching document | `{ id, data } \| null` |
|
|
98
|
+
| `getOne(id)` | Get document by ID directly | `{ id, data } \| null` |
|
|
96
99
|
| `getAll()` | Get all matching documents | `{ id, data }[]` |
|
|
97
100
|
| `preview(data)` | Preview changes before update | `PreviewResult` |
|
|
98
101
|
| `update(data, options?)` | Update matching documents | `UpdateResult` |
|
|
@@ -104,6 +107,7 @@ console.log(`Updated ${result.successCount} documents`);
|
|
|
104
107
|
| `deleteOne()` | Delete first matching document | `{ success, id }` |
|
|
105
108
|
| `aggregate(spec)` | Run sum/average/count queries | `AggregateResult` |
|
|
106
109
|
| `paginate(options)` | Cursor-based pagination | `PaginateResult` |
|
|
110
|
+
| `bulkUpdate(updates, options?)` | Update multiple docs with different data | `BulkUpdateResult` |
|
|
107
111
|
| `getFields(field)` | Get specific field values | `FieldValueResult[]` |
|
|
108
112
|
|
|
109
113
|
### Options
|
|
@@ -153,6 +157,7 @@ All write operations support an optional `options` parameter:
|
|
|
153
157
|
| `DeleteResult` | `successCount`, `failureCount`, `totalCount`, `deletedIds[]`, `failedDocIds?`, `logFilePath?` |
|
|
154
158
|
| `AggregateResult` | `{ [alias]: number \| null }` |
|
|
155
159
|
| `PaginateResult` | `docs[]`, `nextCursor`, `hasMore` |
|
|
160
|
+
| `BulkUpdateResult` | `successCount`, `failureCount`, `totalCount`, `failedDocIds?`, `logFilePath?` |
|
|
156
161
|
| `FieldValueResult` | `id`, `value` |
|
|
157
162
|
|
|
158
163
|
## Usage Examples
|
|
@@ -510,6 +515,51 @@ const page = await updater
|
|
|
510
515
|
.paginate({ pageSize: 50 });
|
|
511
516
|
```
|
|
512
517
|
|
|
518
|
+
### Get Document by ID
|
|
519
|
+
|
|
520
|
+
```typescript
|
|
521
|
+
// Fast lookup when you know the document ID
|
|
522
|
+
const user = await updater.collection("users").getOne("user-123");
|
|
523
|
+
|
|
524
|
+
if (user) {
|
|
525
|
+
console.log(`Found: ${user.data.name}`);
|
|
526
|
+
} else {
|
|
527
|
+
console.log("User not found");
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Works with select for field filtering
|
|
531
|
+
const profile = await updater
|
|
532
|
+
.collection("users")
|
|
533
|
+
.select("name", "avatar")
|
|
534
|
+
.getOne("user-123");
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
### Bulk Update with Different Data
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
// Update multiple documents with different data for each
|
|
541
|
+
const result = await updater.collection("users").bulkUpdate([
|
|
542
|
+
{ id: "user-1", data: { score: 100, rank: 1 } },
|
|
543
|
+
{ id: "user-2", data: { score: 85, rank: 2 } },
|
|
544
|
+
{ id: "user-3", data: { score: 70, rank: 3 } },
|
|
545
|
+
]);
|
|
546
|
+
|
|
547
|
+
console.log(`Updated ${result.successCount} documents`);
|
|
548
|
+
|
|
549
|
+
// With progress tracking
|
|
550
|
+
const result2 = await updater.collection("products").bulkUpdate(
|
|
551
|
+
[
|
|
552
|
+
{ id: "prod-1", data: { price: 29.99, stock: 100 } },
|
|
553
|
+
{ id: "prod-2", data: { price: 49.99, stock: 50 } },
|
|
554
|
+
],
|
|
555
|
+
{
|
|
556
|
+
onProgress: (progress) => {
|
|
557
|
+
console.log(`${progress.percentage}% complete`);
|
|
558
|
+
},
|
|
559
|
+
}
|
|
560
|
+
);
|
|
561
|
+
```
|
|
562
|
+
|
|
513
563
|
### Dry Run Mode
|
|
514
564
|
|
|
515
565
|
```typescript
|
package/dist/index.d.mts
CHANGED
|
@@ -302,6 +302,36 @@ interface PaginateResult {
|
|
|
302
302
|
nextCursor: unknown | null;
|
|
303
303
|
hasMore: boolean;
|
|
304
304
|
}
|
|
305
|
+
/**
|
|
306
|
+
* Input for bulk update operation
|
|
307
|
+
*/
|
|
308
|
+
interface BulkUpdateInput {
|
|
309
|
+
id: string;
|
|
310
|
+
data: Record<string, any>;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Options for bulk update operation
|
|
314
|
+
*/
|
|
315
|
+
interface BulkUpdateOptions {
|
|
316
|
+
/**
|
|
317
|
+
* Callback function for progress updates
|
|
318
|
+
* @param progress - Current progress information
|
|
319
|
+
*/
|
|
320
|
+
onProgress?: (progress: ProgressInfo) => void;
|
|
321
|
+
/**
|
|
322
|
+
* Log file generation options
|
|
323
|
+
*/
|
|
324
|
+
log?: LogOptions;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Result of bulk update operation
|
|
328
|
+
*/
|
|
329
|
+
interface BulkUpdateResult {
|
|
330
|
+
successCount: number;
|
|
331
|
+
failureCount: number;
|
|
332
|
+
totalCount: number;
|
|
333
|
+
failedDocIds?: string[];
|
|
334
|
+
}
|
|
305
335
|
|
|
306
336
|
/**
|
|
307
337
|
* BatchUpdater - Core class for batch operations on Firestore
|
|
@@ -389,6 +419,15 @@ declare class BatchUpdater {
|
|
|
389
419
|
id: string;
|
|
390
420
|
data: Record<string, any>;
|
|
391
421
|
}[]>;
|
|
422
|
+
/**
|
|
423
|
+
* Get a document by its ID directly (faster than findOne with where)
|
|
424
|
+
* @param id - Document ID
|
|
425
|
+
* @returns Document with id and data, or null if not found
|
|
426
|
+
*/
|
|
427
|
+
getOne(id: string): Promise<{
|
|
428
|
+
id: string;
|
|
429
|
+
data: Record<string, any>;
|
|
430
|
+
} | null>;
|
|
392
431
|
/**
|
|
393
432
|
* Update the first document matching the query conditions
|
|
394
433
|
* @param updateData - Data to update
|
|
@@ -459,6 +498,15 @@ declare class BatchUpdater {
|
|
|
459
498
|
create(documents: CreateDocumentInput[], options?: CreateOptions): Promise<CreateResult & {
|
|
460
499
|
logFilePath?: string;
|
|
461
500
|
}>;
|
|
501
|
+
/**
|
|
502
|
+
* Update multiple documents with different data for each
|
|
503
|
+
* @param updates - Array of { id, data } objects specifying updates for each document
|
|
504
|
+
* @param options - Bulk update options (e.g., progress callback, log options)
|
|
505
|
+
* @returns Bulk update result with success/failure counts and optional log file path
|
|
506
|
+
*/
|
|
507
|
+
bulkUpdate(updates: BulkUpdateInput[], options?: BulkUpdateOptions): Promise<BulkUpdateResult & {
|
|
508
|
+
logFilePath?: string;
|
|
509
|
+
}>;
|
|
462
510
|
/**
|
|
463
511
|
* Upsert documents matching query conditions
|
|
464
512
|
* Updates existing documents or creates them if they don't exist
|
|
@@ -554,4 +602,4 @@ declare function isValidUpdateData(value: any): value is Record<string, any>;
|
|
|
554
602
|
*/
|
|
555
603
|
declare function formatError(error: unknown, context?: string): string;
|
|
556
604
|
|
|
557
|
-
export { type AggregateResult, type AggregateSpec, BatchUpdater, type CountResult, type CreateDocumentInput, type CreateOneResult, type CreateOptions, type CreateResult, type DeleteOptions, type DeleteResult, type DocumentSnapshot, type DryRunResult, type FieldValueResult, type LogEntry, type LogOptions, type OperationLog, type OrderByCondition, type PaginateOptions, type PaginateResult, type PreviewResult, type ProgressInfo, type UpdateOptions, type UpdateResult, type UpsertOptions, type UpsertResult, type WhereCondition, calculateProgress, createLogCollector, formatError, formatOperationLog, getAffectedFields, isValidUpdateData, mergeUpdateData, writeOperationLog };
|
|
605
|
+
export { type AggregateResult, type AggregateSpec, BatchUpdater, type BulkUpdateInput, type BulkUpdateOptions, type BulkUpdateResult, type CountResult, type CreateDocumentInput, type CreateOneResult, type CreateOptions, type CreateResult, type DeleteOptions, type DeleteResult, type DocumentSnapshot, type DryRunResult, type FieldValueResult, type LogEntry, type LogOptions, type OperationLog, type OrderByCondition, type PaginateOptions, type PaginateResult, type PreviewResult, type ProgressInfo, type UpdateOptions, type UpdateResult, type UpsertOptions, type UpsertResult, type WhereCondition, calculateProgress, createLogCollector, formatError, formatOperationLog, getAffectedFields, isValidUpdateData, mergeUpdateData, writeOperationLog };
|
package/dist/index.d.ts
CHANGED
|
@@ -302,6 +302,36 @@ interface PaginateResult {
|
|
|
302
302
|
nextCursor: unknown | null;
|
|
303
303
|
hasMore: boolean;
|
|
304
304
|
}
|
|
305
|
+
/**
|
|
306
|
+
* Input for bulk update operation
|
|
307
|
+
*/
|
|
308
|
+
interface BulkUpdateInput {
|
|
309
|
+
id: string;
|
|
310
|
+
data: Record<string, any>;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Options for bulk update operation
|
|
314
|
+
*/
|
|
315
|
+
interface BulkUpdateOptions {
|
|
316
|
+
/**
|
|
317
|
+
* Callback function for progress updates
|
|
318
|
+
* @param progress - Current progress information
|
|
319
|
+
*/
|
|
320
|
+
onProgress?: (progress: ProgressInfo) => void;
|
|
321
|
+
/**
|
|
322
|
+
* Log file generation options
|
|
323
|
+
*/
|
|
324
|
+
log?: LogOptions;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Result of bulk update operation
|
|
328
|
+
*/
|
|
329
|
+
interface BulkUpdateResult {
|
|
330
|
+
successCount: number;
|
|
331
|
+
failureCount: number;
|
|
332
|
+
totalCount: number;
|
|
333
|
+
failedDocIds?: string[];
|
|
334
|
+
}
|
|
305
335
|
|
|
306
336
|
/**
|
|
307
337
|
* BatchUpdater - Core class for batch operations on Firestore
|
|
@@ -389,6 +419,15 @@ declare class BatchUpdater {
|
|
|
389
419
|
id: string;
|
|
390
420
|
data: Record<string, any>;
|
|
391
421
|
}[]>;
|
|
422
|
+
/**
|
|
423
|
+
* Get a document by its ID directly (faster than findOne with where)
|
|
424
|
+
* @param id - Document ID
|
|
425
|
+
* @returns Document with id and data, or null if not found
|
|
426
|
+
*/
|
|
427
|
+
getOne(id: string): Promise<{
|
|
428
|
+
id: string;
|
|
429
|
+
data: Record<string, any>;
|
|
430
|
+
} | null>;
|
|
392
431
|
/**
|
|
393
432
|
* Update the first document matching the query conditions
|
|
394
433
|
* @param updateData - Data to update
|
|
@@ -459,6 +498,15 @@ declare class BatchUpdater {
|
|
|
459
498
|
create(documents: CreateDocumentInput[], options?: CreateOptions): Promise<CreateResult & {
|
|
460
499
|
logFilePath?: string;
|
|
461
500
|
}>;
|
|
501
|
+
/**
|
|
502
|
+
* Update multiple documents with different data for each
|
|
503
|
+
* @param updates - Array of { id, data } objects specifying updates for each document
|
|
504
|
+
* @param options - Bulk update options (e.g., progress callback, log options)
|
|
505
|
+
* @returns Bulk update result with success/failure counts and optional log file path
|
|
506
|
+
*/
|
|
507
|
+
bulkUpdate(updates: BulkUpdateInput[], options?: BulkUpdateOptions): Promise<BulkUpdateResult & {
|
|
508
|
+
logFilePath?: string;
|
|
509
|
+
}>;
|
|
462
510
|
/**
|
|
463
511
|
* Upsert documents matching query conditions
|
|
464
512
|
* Updates existing documents or creates them if they don't exist
|
|
@@ -554,4 +602,4 @@ declare function isValidUpdateData(value: any): value is Record<string, any>;
|
|
|
554
602
|
*/
|
|
555
603
|
declare function formatError(error: unknown, context?: string): string;
|
|
556
604
|
|
|
557
|
-
export { type AggregateResult, type AggregateSpec, BatchUpdater, type CountResult, type CreateDocumentInput, type CreateOneResult, type CreateOptions, type CreateResult, type DeleteOptions, type DeleteResult, type DocumentSnapshot, type DryRunResult, type FieldValueResult, type LogEntry, type LogOptions, type OperationLog, type OrderByCondition, type PaginateOptions, type PaginateResult, type PreviewResult, type ProgressInfo, type UpdateOptions, type UpdateResult, type UpsertOptions, type UpsertResult, type WhereCondition, calculateProgress, createLogCollector, formatError, formatOperationLog, getAffectedFields, isValidUpdateData, mergeUpdateData, writeOperationLog };
|
|
605
|
+
export { type AggregateResult, type AggregateSpec, BatchUpdater, type BulkUpdateInput, type BulkUpdateOptions, type BulkUpdateResult, type CountResult, type CreateDocumentInput, type CreateOneResult, type CreateOptions, type CreateResult, type DeleteOptions, type DeleteResult, type DocumentSnapshot, type DryRunResult, type FieldValueResult, type LogEntry, type LogOptions, type OperationLog, type OrderByCondition, type PaginateOptions, type PaginateResult, type PreviewResult, type ProgressInfo, type UpdateOptions, type UpdateResult, type UpsertOptions, type UpsertResult, type WhereCondition, calculateProgress, createLogCollector, formatError, formatOperationLog, getAffectedFields, isValidUpdateData, mergeUpdateData, writeOperationLog };
|
package/dist/index.js
CHANGED
|
@@ -332,6 +332,38 @@ var BatchUpdater = class {
|
|
|
332
332
|
data: doc.data()
|
|
333
333
|
}));
|
|
334
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* Get a document by its ID directly (faster than findOne with where)
|
|
337
|
+
* @param id - Document ID
|
|
338
|
+
* @returns Document with id and data, or null if not found
|
|
339
|
+
*/
|
|
340
|
+
async getOne(id) {
|
|
341
|
+
this.validateSetup();
|
|
342
|
+
if (this.isCollectionGroup) {
|
|
343
|
+
throw new Error(
|
|
344
|
+
"getOne() cannot be used with collectionGroup(). Use findOne() with where conditions instead."
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
const docRef = this.firestore.collection(this.collectionPath).doc(id);
|
|
348
|
+
let docSnapshot;
|
|
349
|
+
if (this.selectedFields && this.selectedFields.length > 0) {
|
|
350
|
+
const query = this.firestore.collection(this.collectionPath).where("__name__", "==", docRef).select(...this.selectedFields);
|
|
351
|
+
const snapshot = await query.get();
|
|
352
|
+
if (snapshot.empty) {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
docSnapshot = snapshot.docs[0];
|
|
356
|
+
} else {
|
|
357
|
+
docSnapshot = await docRef.get();
|
|
358
|
+
if (!docSnapshot.exists) {
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return {
|
|
363
|
+
id: docSnapshot.id,
|
|
364
|
+
data: docSnapshot.data()
|
|
365
|
+
};
|
|
366
|
+
}
|
|
335
367
|
/**
|
|
336
368
|
* Update the first document matching the query conditions
|
|
337
369
|
* @param updateData - Data to update
|
|
@@ -721,6 +753,81 @@ var BatchUpdater = class {
|
|
|
721
753
|
}
|
|
722
754
|
return result;
|
|
723
755
|
}
|
|
756
|
+
/**
|
|
757
|
+
* Update multiple documents with different data for each
|
|
758
|
+
* @param updates - Array of { id, data } objects specifying updates for each document
|
|
759
|
+
* @param options - Bulk update options (e.g., progress callback, log options)
|
|
760
|
+
* @returns Bulk update result with success/failure counts and optional log file path
|
|
761
|
+
*/
|
|
762
|
+
async bulkUpdate(updates, options = {}) {
|
|
763
|
+
this.validateSetup();
|
|
764
|
+
if (this.isCollectionGroup) {
|
|
765
|
+
throw new Error(
|
|
766
|
+
"bulkUpdate() cannot be used with collectionGroup(). Use collection() with a specific path instead."
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
if (!Array.isArray(updates) || updates.length === 0) {
|
|
770
|
+
throw new Error("Updates array must be non-empty");
|
|
771
|
+
}
|
|
772
|
+
for (const update of updates) {
|
|
773
|
+
if (!update.id || typeof update.id !== "string") {
|
|
774
|
+
throw new Error("Each update must have a valid id");
|
|
775
|
+
}
|
|
776
|
+
if (!isValidUpdateData(update.data)) {
|
|
777
|
+
throw new Error("Each update must have valid data");
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
const totalCount = updates.length;
|
|
781
|
+
let successCount = 0;
|
|
782
|
+
let failureCount = 0;
|
|
783
|
+
const failedDocIds = [];
|
|
784
|
+
const logCollector = options.log?.enabled ? createLogCollector("update", this.collectionPath) : null;
|
|
785
|
+
const bulkWriter = this.firestore.bulkWriter();
|
|
786
|
+
const collection = this.firestore.collection(this.collectionPath);
|
|
787
|
+
let processedCount = 0;
|
|
788
|
+
const docIdMap = /* @__PURE__ */ new Map();
|
|
789
|
+
for (const update of updates) {
|
|
790
|
+
const docRef = collection.doc(update.id);
|
|
791
|
+
docIdMap.set(docRef.path, update.id);
|
|
792
|
+
}
|
|
793
|
+
bulkWriter.onWriteResult((ref) => {
|
|
794
|
+
successCount++;
|
|
795
|
+
processedCount++;
|
|
796
|
+
const docId = docIdMap.get(ref.path) || ref.id;
|
|
797
|
+
logCollector?.addEntry(docId, "success");
|
|
798
|
+
if (options.onProgress) {
|
|
799
|
+
const progress = calculateProgress(processedCount, totalCount);
|
|
800
|
+
options.onProgress(progress);
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
bulkWriter.onWriteError((error) => {
|
|
804
|
+
failureCount++;
|
|
805
|
+
processedCount++;
|
|
806
|
+
const docId = error.documentRef?.id || "unknown";
|
|
807
|
+
failedDocIds.push(docId);
|
|
808
|
+
logCollector?.addEntry(docId, "failure", error.message);
|
|
809
|
+
if (options.onProgress) {
|
|
810
|
+
const progress = calculateProgress(processedCount, totalCount);
|
|
811
|
+
options.onProgress(progress);
|
|
812
|
+
}
|
|
813
|
+
return false;
|
|
814
|
+
});
|
|
815
|
+
for (const update of updates) {
|
|
816
|
+
const docRef = collection.doc(update.id);
|
|
817
|
+
bulkWriter.update(docRef, update.data);
|
|
818
|
+
}
|
|
819
|
+
await bulkWriter.close();
|
|
820
|
+
const result = {
|
|
821
|
+
successCount,
|
|
822
|
+
failureCount,
|
|
823
|
+
totalCount,
|
|
824
|
+
failedDocIds: failedDocIds.length > 0 ? failedDocIds : void 0
|
|
825
|
+
};
|
|
826
|
+
if (logCollector && options.log) {
|
|
827
|
+
result.logFilePath = logCollector.finalize(options.log);
|
|
828
|
+
}
|
|
829
|
+
return result;
|
|
830
|
+
}
|
|
724
831
|
/**
|
|
725
832
|
* Upsert documents matching query conditions
|
|
726
833
|
* Updates existing documents or creates them if they don't exist
|
package/dist/index.mjs
CHANGED
|
@@ -287,6 +287,38 @@ var BatchUpdater = class {
|
|
|
287
287
|
data: doc.data()
|
|
288
288
|
}));
|
|
289
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* Get a document by its ID directly (faster than findOne with where)
|
|
292
|
+
* @param id - Document ID
|
|
293
|
+
* @returns Document with id and data, or null if not found
|
|
294
|
+
*/
|
|
295
|
+
async getOne(id) {
|
|
296
|
+
this.validateSetup();
|
|
297
|
+
if (this.isCollectionGroup) {
|
|
298
|
+
throw new Error(
|
|
299
|
+
"getOne() cannot be used with collectionGroup(). Use findOne() with where conditions instead."
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
const docRef = this.firestore.collection(this.collectionPath).doc(id);
|
|
303
|
+
let docSnapshot;
|
|
304
|
+
if (this.selectedFields && this.selectedFields.length > 0) {
|
|
305
|
+
const query = this.firestore.collection(this.collectionPath).where("__name__", "==", docRef).select(...this.selectedFields);
|
|
306
|
+
const snapshot = await query.get();
|
|
307
|
+
if (snapshot.empty) {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
docSnapshot = snapshot.docs[0];
|
|
311
|
+
} else {
|
|
312
|
+
docSnapshot = await docRef.get();
|
|
313
|
+
if (!docSnapshot.exists) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
id: docSnapshot.id,
|
|
319
|
+
data: docSnapshot.data()
|
|
320
|
+
};
|
|
321
|
+
}
|
|
290
322
|
/**
|
|
291
323
|
* Update the first document matching the query conditions
|
|
292
324
|
* @param updateData - Data to update
|
|
@@ -676,6 +708,81 @@ var BatchUpdater = class {
|
|
|
676
708
|
}
|
|
677
709
|
return result;
|
|
678
710
|
}
|
|
711
|
+
/**
|
|
712
|
+
* Update multiple documents with different data for each
|
|
713
|
+
* @param updates - Array of { id, data } objects specifying updates for each document
|
|
714
|
+
* @param options - Bulk update options (e.g., progress callback, log options)
|
|
715
|
+
* @returns Bulk update result with success/failure counts and optional log file path
|
|
716
|
+
*/
|
|
717
|
+
async bulkUpdate(updates, options = {}) {
|
|
718
|
+
this.validateSetup();
|
|
719
|
+
if (this.isCollectionGroup) {
|
|
720
|
+
throw new Error(
|
|
721
|
+
"bulkUpdate() cannot be used with collectionGroup(). Use collection() with a specific path instead."
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
if (!Array.isArray(updates) || updates.length === 0) {
|
|
725
|
+
throw new Error("Updates array must be non-empty");
|
|
726
|
+
}
|
|
727
|
+
for (const update of updates) {
|
|
728
|
+
if (!update.id || typeof update.id !== "string") {
|
|
729
|
+
throw new Error("Each update must have a valid id");
|
|
730
|
+
}
|
|
731
|
+
if (!isValidUpdateData(update.data)) {
|
|
732
|
+
throw new Error("Each update must have valid data");
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
const totalCount = updates.length;
|
|
736
|
+
let successCount = 0;
|
|
737
|
+
let failureCount = 0;
|
|
738
|
+
const failedDocIds = [];
|
|
739
|
+
const logCollector = options.log?.enabled ? createLogCollector("update", this.collectionPath) : null;
|
|
740
|
+
const bulkWriter = this.firestore.bulkWriter();
|
|
741
|
+
const collection = this.firestore.collection(this.collectionPath);
|
|
742
|
+
let processedCount = 0;
|
|
743
|
+
const docIdMap = /* @__PURE__ */ new Map();
|
|
744
|
+
for (const update of updates) {
|
|
745
|
+
const docRef = collection.doc(update.id);
|
|
746
|
+
docIdMap.set(docRef.path, update.id);
|
|
747
|
+
}
|
|
748
|
+
bulkWriter.onWriteResult((ref) => {
|
|
749
|
+
successCount++;
|
|
750
|
+
processedCount++;
|
|
751
|
+
const docId = docIdMap.get(ref.path) || ref.id;
|
|
752
|
+
logCollector?.addEntry(docId, "success");
|
|
753
|
+
if (options.onProgress) {
|
|
754
|
+
const progress = calculateProgress(processedCount, totalCount);
|
|
755
|
+
options.onProgress(progress);
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
bulkWriter.onWriteError((error) => {
|
|
759
|
+
failureCount++;
|
|
760
|
+
processedCount++;
|
|
761
|
+
const docId = error.documentRef?.id || "unknown";
|
|
762
|
+
failedDocIds.push(docId);
|
|
763
|
+
logCollector?.addEntry(docId, "failure", error.message);
|
|
764
|
+
if (options.onProgress) {
|
|
765
|
+
const progress = calculateProgress(processedCount, totalCount);
|
|
766
|
+
options.onProgress(progress);
|
|
767
|
+
}
|
|
768
|
+
return false;
|
|
769
|
+
});
|
|
770
|
+
for (const update of updates) {
|
|
771
|
+
const docRef = collection.doc(update.id);
|
|
772
|
+
bulkWriter.update(docRef, update.data);
|
|
773
|
+
}
|
|
774
|
+
await bulkWriter.close();
|
|
775
|
+
const result = {
|
|
776
|
+
successCount,
|
|
777
|
+
failureCount,
|
|
778
|
+
totalCount,
|
|
779
|
+
failedDocIds: failedDocIds.length > 0 ? failedDocIds : void 0
|
|
780
|
+
};
|
|
781
|
+
if (logCollector && options.log) {
|
|
782
|
+
result.logFilePath = logCollector.finalize(options.log);
|
|
783
|
+
}
|
|
784
|
+
return result;
|
|
785
|
+
}
|
|
679
786
|
/**
|
|
680
787
|
* Upsert documents matching query conditions
|
|
681
788
|
* Updates existing documents or creates them if they don't exist
|