firestore-batch-updater 1.7.0 → 1.8.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 +58 -0
- package/README.md +58 -0
- package/dist/index.d.mts +88 -1
- package/dist/index.d.ts +88 -1
- package/dist/index.js +225 -0
- package/dist/index.mjs +225 -0
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
- 커서 페이지네이션 - `paginate()`로 메모리 효율적인 페이지 단위 조회
|
|
23
23
|
- ID 직접 조회 - `getOne()`으로 문서 ID로 빠른 조회
|
|
24
24
|
- 벌크 작업 - `bulkCreate()`, `bulkUpdate()`, `bulkDelete()`로 여러 문서에 각기 다른 데이터로 효율적 처리
|
|
25
|
+
- 문서 변환 - `transform()`으로 각 문서에 커스텀 로직 적용 (가격 인상, 데이터 마이그레이션 등)
|
|
26
|
+
- 복사 & 이동 - `copyTo()`로 컬렉션 간 문서 복사/이동 (데이터 변환 옵션 포함)
|
|
25
27
|
- FieldValue 지원 - `increment()`, `arrayUnion()`, `delete()`, `serverTimestamp()` 등 사용 가능
|
|
26
28
|
- 서브컬렉션 & 컬렉션 그룹 - 서브컬렉션 쿼리 또는 동일 이름의 모든 컬렉션 쿼리
|
|
27
29
|
- Dry Run 모드 - 실제 변경 없이 작업 시뮬레이션
|
|
@@ -110,6 +112,8 @@ console.log(`${result.successCount}개 문서 업데이트 완료`);
|
|
|
110
112
|
| `bulkCreate(docs, options?)` | 여러 문서를 각기 다른 데이터로 생성 | `BulkCreateResult` |
|
|
111
113
|
| `bulkUpdate(updates, options?)` | 여러 문서에 각기 다른 데이터 업데이트 | `BulkUpdateResult` |
|
|
112
114
|
| `bulkDelete(ids, options?)` | ID 배열로 여러 문서 삭제 | `BulkDeleteResult` |
|
|
115
|
+
| `transform(fn, options?)` | 커스텀 함수로 문서 변환 | `TransformResult` |
|
|
116
|
+
| `copyTo(target, options?)` | 다른 컬렉션으로 문서 복사/이동 | `CopyToResult` |
|
|
113
117
|
| `getFields(field)` | 특정 필드 값 조회 | `FieldValueResult[]` |
|
|
114
118
|
|
|
115
119
|
### 옵션
|
|
@@ -162,6 +166,8 @@ console.log(`${result.successCount}개 문서 업데이트 완료`);
|
|
|
162
166
|
| `BulkCreateResult` | `successCount`, `failureCount`, `totalCount`, `createdIds[]`, `failedDocIds?`, `logFilePath?` |
|
|
163
167
|
| `BulkUpdateResult` | `successCount`, `failureCount`, `totalCount`, `failedDocIds?`, `logFilePath?` |
|
|
164
168
|
| `BulkDeleteResult` | `successCount`, `failureCount`, `totalCount`, `deletedIds[]`, `failedDocIds?`, `logFilePath?` |
|
|
169
|
+
| `TransformResult` | `successCount`, `failureCount`, `skippedCount`, `totalCount`, `failedDocIds?`, `logFilePath?` |
|
|
170
|
+
| `CopyToResult` | `successCount`, `failureCount`, `totalCount`, `copiedIds[]`, `failedDocIds?`, `logFilePath?` |
|
|
165
171
|
| `FieldValueResult` | `id`, `value` |
|
|
166
172
|
|
|
167
173
|
## 사용 예시
|
|
@@ -609,6 +615,58 @@ const result2 = await updater.collection("logs").bulkDelete(expiredLogIds, {
|
|
|
609
615
|
});
|
|
610
616
|
```
|
|
611
617
|
|
|
618
|
+
### 문서 변환
|
|
619
|
+
|
|
620
|
+
```typescript
|
|
621
|
+
// 각 문서에 커스텀 로직 적용
|
|
622
|
+
const result = await updater
|
|
623
|
+
.collection("products")
|
|
624
|
+
.where("category", "==", "electronics")
|
|
625
|
+
.transform((doc) => ({
|
|
626
|
+
price: doc.data.price * 1.1, // 10% 가격 인상
|
|
627
|
+
name: doc.data.name.toUpperCase(),
|
|
628
|
+
}));
|
|
629
|
+
|
|
630
|
+
console.log(`변환: ${result.successCount}, 건너뜀: ${result.skippedCount}`);
|
|
631
|
+
|
|
632
|
+
// 조건부 건너뛰기 (null 반환 시 스킵)
|
|
633
|
+
const result2 = await updater
|
|
634
|
+
.collection("users")
|
|
635
|
+
.transform((doc) => {
|
|
636
|
+
if (doc.data.score < 50) return null; // 낮은 점수 건너뛰기
|
|
637
|
+
return { tier: "premium" };
|
|
638
|
+
});
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
### 문서 복사 & 이동
|
|
642
|
+
|
|
643
|
+
```typescript
|
|
644
|
+
// 다른 컬렉션으로 문서 복사
|
|
645
|
+
const result = await updater
|
|
646
|
+
.collection("users")
|
|
647
|
+
.where("status", "==", "inactive")
|
|
648
|
+
.copyTo("archived_users");
|
|
649
|
+
|
|
650
|
+
console.log(`${result.successCount}개 문서 복사 완료`);
|
|
651
|
+
|
|
652
|
+
// 데이터 변환하며 복사 (민감 정보 제거 등)
|
|
653
|
+
await updater
|
|
654
|
+
.collection("users")
|
|
655
|
+
.copyTo("public_profiles", {
|
|
656
|
+
transform: (doc) => ({
|
|
657
|
+
name: doc.data.name,
|
|
658
|
+
avatar: doc.data.avatar,
|
|
659
|
+
// password, email 제외
|
|
660
|
+
}),
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
// 문서 이동 (복사 + 원본 삭제)
|
|
664
|
+
await updater
|
|
665
|
+
.collection("orders")
|
|
666
|
+
.where("status", "==", "completed")
|
|
667
|
+
.copyTo("order_archive", { deleteSource: true });
|
|
668
|
+
```
|
|
669
|
+
|
|
612
670
|
### Dry Run 모드
|
|
613
671
|
|
|
614
672
|
```typescript
|
package/README.md
CHANGED
|
@@ -22,6 +22,8 @@ English | [한국어](./README.ko.md)
|
|
|
22
22
|
- Cursor pagination - Use `paginate()` for memory-efficient page-by-page iteration
|
|
23
23
|
- Direct ID lookup - Use `getOne()` for fast document retrieval by ID
|
|
24
24
|
- Bulk operations - Use `bulkCreate()`, `bulkUpdate()`, `bulkDelete()` for efficient multi-document operations with different data each
|
|
25
|
+
- Transform - Use `transform()` to apply custom logic to each document (e.g., price increase, data migration)
|
|
26
|
+
- Copy & Move - Use `copyTo()` to copy/move documents between collections with optional data transformation
|
|
25
27
|
- FieldValue support - Use `increment()`, `arrayUnion()`, `delete()`, `serverTimestamp()`, etc.
|
|
26
28
|
- Subcollection & Collection Group - Query subcollections or all collections with the same name
|
|
27
29
|
- Dry run mode - Simulate operations without making changes
|
|
@@ -110,6 +112,8 @@ console.log(`Updated ${result.successCount} documents`);
|
|
|
110
112
|
| `bulkCreate(docs, options?)` | Create multiple docs with different data | `BulkCreateResult` |
|
|
111
113
|
| `bulkUpdate(updates, options?)` | Update multiple docs with different data | `BulkUpdateResult` |
|
|
112
114
|
| `bulkDelete(ids, options?)` | Delete multiple docs by ID | `BulkDeleteResult` |
|
|
115
|
+
| `transform(fn, options?)` | Transform docs with custom function | `TransformResult` |
|
|
116
|
+
| `copyTo(target, options?)` | Copy/move docs to another collection | `CopyToResult` |
|
|
113
117
|
| `getFields(field)` | Get specific field values | `FieldValueResult[]` |
|
|
114
118
|
|
|
115
119
|
### Options
|
|
@@ -162,6 +166,8 @@ All write operations support an optional `options` parameter:
|
|
|
162
166
|
| `BulkCreateResult` | `successCount`, `failureCount`, `totalCount`, `createdIds[]`, `failedDocIds?`, `logFilePath?` |
|
|
163
167
|
| `BulkUpdateResult` | `successCount`, `failureCount`, `totalCount`, `failedDocIds?`, `logFilePath?` |
|
|
164
168
|
| `BulkDeleteResult` | `successCount`, `failureCount`, `totalCount`, `deletedIds[]`, `failedDocIds?`, `logFilePath?` |
|
|
169
|
+
| `TransformResult` | `successCount`, `failureCount`, `skippedCount`, `totalCount`, `failedDocIds?`, `logFilePath?` |
|
|
170
|
+
| `CopyToResult` | `successCount`, `failureCount`, `totalCount`, `copiedIds[]`, `failedDocIds?`, `logFilePath?` |
|
|
165
171
|
| `FieldValueResult` | `id`, `value` |
|
|
166
172
|
|
|
167
173
|
## Usage Examples
|
|
@@ -597,6 +603,58 @@ const result2 = await updater.collection("logs").bulkDelete(expiredLogIds, {
|
|
|
597
603
|
});
|
|
598
604
|
```
|
|
599
605
|
|
|
606
|
+
### Transform Documents
|
|
607
|
+
|
|
608
|
+
```typescript
|
|
609
|
+
// Apply custom logic to each document
|
|
610
|
+
const result = await updater
|
|
611
|
+
.collection("products")
|
|
612
|
+
.where("category", "==", "electronics")
|
|
613
|
+
.transform((doc) => ({
|
|
614
|
+
price: doc.data.price * 1.1, // 10% price increase
|
|
615
|
+
name: doc.data.name.toUpperCase(),
|
|
616
|
+
}));
|
|
617
|
+
|
|
618
|
+
console.log(`Transformed ${result.successCount}, skipped ${result.skippedCount}`);
|
|
619
|
+
|
|
620
|
+
// Skip documents conditionally (return null)
|
|
621
|
+
const result2 = await updater
|
|
622
|
+
.collection("users")
|
|
623
|
+
.transform((doc) => {
|
|
624
|
+
if (doc.data.score < 50) return null; // Skip low scores
|
|
625
|
+
return { tier: "premium" };
|
|
626
|
+
});
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
### Copy & Move Documents
|
|
630
|
+
|
|
631
|
+
```typescript
|
|
632
|
+
// Copy documents to another collection
|
|
633
|
+
const result = await updater
|
|
634
|
+
.collection("users")
|
|
635
|
+
.where("status", "==", "inactive")
|
|
636
|
+
.copyTo("archived_users");
|
|
637
|
+
|
|
638
|
+
console.log(`Copied ${result.successCount} documents`);
|
|
639
|
+
|
|
640
|
+
// Copy with data transformation (e.g., remove sensitive fields)
|
|
641
|
+
await updater
|
|
642
|
+
.collection("users")
|
|
643
|
+
.copyTo("public_profiles", {
|
|
644
|
+
transform: (doc) => ({
|
|
645
|
+
name: doc.data.name,
|
|
646
|
+
avatar: doc.data.avatar,
|
|
647
|
+
// password, email omitted
|
|
648
|
+
}),
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
// Move documents (copy + delete source)
|
|
652
|
+
await updater
|
|
653
|
+
.collection("orders")
|
|
654
|
+
.where("status", "==", "completed")
|
|
655
|
+
.copyTo("order_archive", { deleteSource: true });
|
|
656
|
+
```
|
|
657
|
+
|
|
600
658
|
### Dry Run Mode
|
|
601
659
|
|
|
602
660
|
```typescript
|
package/dist/index.d.mts
CHANGED
|
@@ -387,6 +387,74 @@ interface BulkUpdateResult {
|
|
|
387
387
|
totalCount: number;
|
|
388
388
|
failedDocIds?: string[];
|
|
389
389
|
}
|
|
390
|
+
/**
|
|
391
|
+
* Transform function that receives document data and returns the update data
|
|
392
|
+
*/
|
|
393
|
+
type TransformFn = (doc: {
|
|
394
|
+
id: string;
|
|
395
|
+
data: Record<string, any>;
|
|
396
|
+
}) => Record<string, any> | null;
|
|
397
|
+
/**
|
|
398
|
+
* Options for transform operation
|
|
399
|
+
*/
|
|
400
|
+
interface TransformOptions {
|
|
401
|
+
/**
|
|
402
|
+
* Callback function for progress updates
|
|
403
|
+
* @param progress - Current progress information
|
|
404
|
+
*/
|
|
405
|
+
onProgress?: (progress: ProgressInfo) => void;
|
|
406
|
+
/**
|
|
407
|
+
* Log file generation options
|
|
408
|
+
*/
|
|
409
|
+
log?: LogOptions;
|
|
410
|
+
/**
|
|
411
|
+
* Batch size for pagination (optional)
|
|
412
|
+
*/
|
|
413
|
+
batchSize?: number;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Result of transform operation
|
|
417
|
+
*/
|
|
418
|
+
interface TransformResult {
|
|
419
|
+
successCount: number;
|
|
420
|
+
failureCount: number;
|
|
421
|
+
skippedCount: number;
|
|
422
|
+
totalCount: number;
|
|
423
|
+
failedDocIds?: string[];
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Options for copyTo operation
|
|
427
|
+
*/
|
|
428
|
+
interface CopyToOptions {
|
|
429
|
+
/**
|
|
430
|
+
* Callback function for progress updates
|
|
431
|
+
* @param progress - Current progress information
|
|
432
|
+
*/
|
|
433
|
+
onProgress?: (progress: ProgressInfo) => void;
|
|
434
|
+
/**
|
|
435
|
+
* Log file generation options
|
|
436
|
+
*/
|
|
437
|
+
log?: LogOptions;
|
|
438
|
+
/**
|
|
439
|
+
* Optional transform function to modify data before copying
|
|
440
|
+
*/
|
|
441
|
+
transform?: TransformFn;
|
|
442
|
+
/**
|
|
443
|
+
* Whether to delete source documents after copying (move operation)
|
|
444
|
+
* @default false
|
|
445
|
+
*/
|
|
446
|
+
deleteSource?: boolean;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Result of copyTo operation
|
|
450
|
+
*/
|
|
451
|
+
interface CopyToResult {
|
|
452
|
+
successCount: number;
|
|
453
|
+
failureCount: number;
|
|
454
|
+
totalCount: number;
|
|
455
|
+
copiedIds: string[];
|
|
456
|
+
failedDocIds?: string[];
|
|
457
|
+
}
|
|
390
458
|
|
|
391
459
|
/**
|
|
392
460
|
* BatchUpdater - Core class for batch operations on Firestore
|
|
@@ -580,6 +648,25 @@ declare class BatchUpdater {
|
|
|
580
648
|
bulkDelete(ids: string[], options?: BulkDeleteOptions): Promise<BulkDeleteResult & {
|
|
581
649
|
logFilePath?: string;
|
|
582
650
|
}>;
|
|
651
|
+
/**
|
|
652
|
+
* Transform matching documents using a custom function
|
|
653
|
+
* Reads each document, applies the transform function, and updates with the result
|
|
654
|
+
* @param transformFn - Function that receives { id, data } and returns update data, or null to skip
|
|
655
|
+
* @param options - Transform options (e.g., progress callback, log options, batchSize)
|
|
656
|
+
* @returns Transform result with success/failure/skipped counts and optional log file path
|
|
657
|
+
*/
|
|
658
|
+
transform(transformFn: TransformFn, options?: TransformOptions): Promise<TransformResult & {
|
|
659
|
+
logFilePath?: string;
|
|
660
|
+
}>;
|
|
661
|
+
/**
|
|
662
|
+
* Copy matching documents to another collection
|
|
663
|
+
* @param targetCollection - Target collection path to copy documents to
|
|
664
|
+
* @param options - Copy options (transform, deleteSource for move, progress callback)
|
|
665
|
+
* @returns Copy result with success/failure counts, copied IDs, and optional log file path
|
|
666
|
+
*/
|
|
667
|
+
copyTo(targetCollection: string, options?: CopyToOptions): Promise<CopyToResult & {
|
|
668
|
+
logFilePath?: string;
|
|
669
|
+
}>;
|
|
583
670
|
/**
|
|
584
671
|
* Upsert documents matching query conditions
|
|
585
672
|
* Updates existing documents or creates them if they don't exist
|
|
@@ -675,4 +762,4 @@ declare function isValidUpdateData(value: any): value is Record<string, any>;
|
|
|
675
762
|
*/
|
|
676
763
|
declare function formatError(error: unknown, context?: string): string;
|
|
677
764
|
|
|
678
|
-
export { type AggregateResult, type AggregateSpec, BatchUpdater, type BulkCreateInput, type BulkCreateOptions, type BulkCreateResult, type BulkDeleteOptions, type BulkDeleteResult, 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 };
|
|
765
|
+
export { type AggregateResult, type AggregateSpec, BatchUpdater, type BulkCreateInput, type BulkCreateOptions, type BulkCreateResult, type BulkDeleteOptions, type BulkDeleteResult, type BulkUpdateInput, type BulkUpdateOptions, type BulkUpdateResult, type CopyToOptions, type CopyToResult, 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 TransformFn, type TransformOptions, type TransformResult, type UpdateOptions, type UpdateResult, type UpsertOptions, type UpsertResult, type WhereCondition, calculateProgress, createLogCollector, formatError, formatOperationLog, getAffectedFields, isValidUpdateData, mergeUpdateData, writeOperationLog };
|
package/dist/index.d.ts
CHANGED
|
@@ -387,6 +387,74 @@ interface BulkUpdateResult {
|
|
|
387
387
|
totalCount: number;
|
|
388
388
|
failedDocIds?: string[];
|
|
389
389
|
}
|
|
390
|
+
/**
|
|
391
|
+
* Transform function that receives document data and returns the update data
|
|
392
|
+
*/
|
|
393
|
+
type TransformFn = (doc: {
|
|
394
|
+
id: string;
|
|
395
|
+
data: Record<string, any>;
|
|
396
|
+
}) => Record<string, any> | null;
|
|
397
|
+
/**
|
|
398
|
+
* Options for transform operation
|
|
399
|
+
*/
|
|
400
|
+
interface TransformOptions {
|
|
401
|
+
/**
|
|
402
|
+
* Callback function for progress updates
|
|
403
|
+
* @param progress - Current progress information
|
|
404
|
+
*/
|
|
405
|
+
onProgress?: (progress: ProgressInfo) => void;
|
|
406
|
+
/**
|
|
407
|
+
* Log file generation options
|
|
408
|
+
*/
|
|
409
|
+
log?: LogOptions;
|
|
410
|
+
/**
|
|
411
|
+
* Batch size for pagination (optional)
|
|
412
|
+
*/
|
|
413
|
+
batchSize?: number;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Result of transform operation
|
|
417
|
+
*/
|
|
418
|
+
interface TransformResult {
|
|
419
|
+
successCount: number;
|
|
420
|
+
failureCount: number;
|
|
421
|
+
skippedCount: number;
|
|
422
|
+
totalCount: number;
|
|
423
|
+
failedDocIds?: string[];
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Options for copyTo operation
|
|
427
|
+
*/
|
|
428
|
+
interface CopyToOptions {
|
|
429
|
+
/**
|
|
430
|
+
* Callback function for progress updates
|
|
431
|
+
* @param progress - Current progress information
|
|
432
|
+
*/
|
|
433
|
+
onProgress?: (progress: ProgressInfo) => void;
|
|
434
|
+
/**
|
|
435
|
+
* Log file generation options
|
|
436
|
+
*/
|
|
437
|
+
log?: LogOptions;
|
|
438
|
+
/**
|
|
439
|
+
* Optional transform function to modify data before copying
|
|
440
|
+
*/
|
|
441
|
+
transform?: TransformFn;
|
|
442
|
+
/**
|
|
443
|
+
* Whether to delete source documents after copying (move operation)
|
|
444
|
+
* @default false
|
|
445
|
+
*/
|
|
446
|
+
deleteSource?: boolean;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Result of copyTo operation
|
|
450
|
+
*/
|
|
451
|
+
interface CopyToResult {
|
|
452
|
+
successCount: number;
|
|
453
|
+
failureCount: number;
|
|
454
|
+
totalCount: number;
|
|
455
|
+
copiedIds: string[];
|
|
456
|
+
failedDocIds?: string[];
|
|
457
|
+
}
|
|
390
458
|
|
|
391
459
|
/**
|
|
392
460
|
* BatchUpdater - Core class for batch operations on Firestore
|
|
@@ -580,6 +648,25 @@ declare class BatchUpdater {
|
|
|
580
648
|
bulkDelete(ids: string[], options?: BulkDeleteOptions): Promise<BulkDeleteResult & {
|
|
581
649
|
logFilePath?: string;
|
|
582
650
|
}>;
|
|
651
|
+
/**
|
|
652
|
+
* Transform matching documents using a custom function
|
|
653
|
+
* Reads each document, applies the transform function, and updates with the result
|
|
654
|
+
* @param transformFn - Function that receives { id, data } and returns update data, or null to skip
|
|
655
|
+
* @param options - Transform options (e.g., progress callback, log options, batchSize)
|
|
656
|
+
* @returns Transform result with success/failure/skipped counts and optional log file path
|
|
657
|
+
*/
|
|
658
|
+
transform(transformFn: TransformFn, options?: TransformOptions): Promise<TransformResult & {
|
|
659
|
+
logFilePath?: string;
|
|
660
|
+
}>;
|
|
661
|
+
/**
|
|
662
|
+
* Copy matching documents to another collection
|
|
663
|
+
* @param targetCollection - Target collection path to copy documents to
|
|
664
|
+
* @param options - Copy options (transform, deleteSource for move, progress callback)
|
|
665
|
+
* @returns Copy result with success/failure counts, copied IDs, and optional log file path
|
|
666
|
+
*/
|
|
667
|
+
copyTo(targetCollection: string, options?: CopyToOptions): Promise<CopyToResult & {
|
|
668
|
+
logFilePath?: string;
|
|
669
|
+
}>;
|
|
583
670
|
/**
|
|
584
671
|
* Upsert documents matching query conditions
|
|
585
672
|
* Updates existing documents or creates them if they don't exist
|
|
@@ -675,4 +762,4 @@ declare function isValidUpdateData(value: any): value is Record<string, any>;
|
|
|
675
762
|
*/
|
|
676
763
|
declare function formatError(error: unknown, context?: string): string;
|
|
677
764
|
|
|
678
|
-
export { type AggregateResult, type AggregateSpec, BatchUpdater, type BulkCreateInput, type BulkCreateOptions, type BulkCreateResult, type BulkDeleteOptions, type BulkDeleteResult, 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 };
|
|
765
|
+
export { type AggregateResult, type AggregateSpec, BatchUpdater, type BulkCreateInput, type BulkCreateOptions, type BulkCreateResult, type BulkDeleteOptions, type BulkDeleteResult, type BulkUpdateInput, type BulkUpdateOptions, type BulkUpdateResult, type CopyToOptions, type CopyToResult, 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 TransformFn, type TransformOptions, type TransformResult, type UpdateOptions, type UpdateResult, type UpsertOptions, type UpsertResult, type WhereCondition, calculateProgress, createLogCollector, formatError, formatOperationLog, getAffectedFields, isValidUpdateData, mergeUpdateData, writeOperationLog };
|
package/dist/index.js
CHANGED
|
@@ -972,6 +972,231 @@ var BatchUpdater = class {
|
|
|
972
972
|
}
|
|
973
973
|
return result;
|
|
974
974
|
}
|
|
975
|
+
/**
|
|
976
|
+
* Transform matching documents using a custom function
|
|
977
|
+
* Reads each document, applies the transform function, and updates with the result
|
|
978
|
+
* @param transformFn - Function that receives { id, data } and returns update data, or null to skip
|
|
979
|
+
* @param options - Transform options (e.g., progress callback, log options, batchSize)
|
|
980
|
+
* @returns Transform result with success/failure/skipped counts and optional log file path
|
|
981
|
+
*/
|
|
982
|
+
async transform(transformFn, options = {}) {
|
|
983
|
+
this.validateSetup();
|
|
984
|
+
if (typeof transformFn !== "function") {
|
|
985
|
+
throw new Error("Transform function is required");
|
|
986
|
+
}
|
|
987
|
+
const logCollector = options.log?.enabled ? createLogCollector("update", this.collectionPath, this.conditions) : null;
|
|
988
|
+
let successCount = 0;
|
|
989
|
+
let failureCount = 0;
|
|
990
|
+
let skippedCount = 0;
|
|
991
|
+
let totalCount = 0;
|
|
992
|
+
const failedDocIds = [];
|
|
993
|
+
const processDocuments = async (docs, processedSoFar, grandTotal) => {
|
|
994
|
+
const bulkWriter = this.firestore.bulkWriter();
|
|
995
|
+
let processedCount = processedSoFar;
|
|
996
|
+
const docIdMap = /* @__PURE__ */ new Map();
|
|
997
|
+
bulkWriter.onWriteResult((ref) => {
|
|
998
|
+
successCount++;
|
|
999
|
+
processedCount++;
|
|
1000
|
+
const docId = docIdMap.get(ref.path) || ref.id;
|
|
1001
|
+
logCollector?.addEntry(docId, "success");
|
|
1002
|
+
if (options.onProgress) {
|
|
1003
|
+
const progress = calculateProgress(processedCount, grandTotal);
|
|
1004
|
+
options.onProgress(progress);
|
|
1005
|
+
}
|
|
1006
|
+
});
|
|
1007
|
+
bulkWriter.onWriteError((error) => {
|
|
1008
|
+
failureCount++;
|
|
1009
|
+
processedCount++;
|
|
1010
|
+
const docId = error.documentRef?.id || "unknown";
|
|
1011
|
+
failedDocIds.push(docId);
|
|
1012
|
+
logCollector?.addEntry(docId, "failure", error.message);
|
|
1013
|
+
if (options.onProgress) {
|
|
1014
|
+
const progress = calculateProgress(processedCount, grandTotal);
|
|
1015
|
+
options.onProgress(progress);
|
|
1016
|
+
}
|
|
1017
|
+
return false;
|
|
1018
|
+
});
|
|
1019
|
+
for (const doc of docs) {
|
|
1020
|
+
const updateData = transformFn({ id: doc.id, data: doc.data() });
|
|
1021
|
+
if (updateData === null) {
|
|
1022
|
+
skippedCount++;
|
|
1023
|
+
processedCount++;
|
|
1024
|
+
if (options.onProgress) {
|
|
1025
|
+
const progress = calculateProgress(processedCount, grandTotal);
|
|
1026
|
+
options.onProgress(progress);
|
|
1027
|
+
}
|
|
1028
|
+
continue;
|
|
1029
|
+
}
|
|
1030
|
+
docIdMap.set(doc.ref.path, doc.id);
|
|
1031
|
+
bulkWriter.update(doc.ref, updateData);
|
|
1032
|
+
}
|
|
1033
|
+
await bulkWriter.close();
|
|
1034
|
+
return processedCount;
|
|
1035
|
+
};
|
|
1036
|
+
if (options.batchSize && options.batchSize > 0) {
|
|
1037
|
+
const countSnapshot = await this.buildQuery().count().get();
|
|
1038
|
+
totalCount = countSnapshot.data().count;
|
|
1039
|
+
if (totalCount === 0) {
|
|
1040
|
+
const result2 = {
|
|
1041
|
+
successCount: 0,
|
|
1042
|
+
failureCount: 0,
|
|
1043
|
+
skippedCount: 0,
|
|
1044
|
+
totalCount: 0
|
|
1045
|
+
};
|
|
1046
|
+
if (logCollector && options.log) {
|
|
1047
|
+
result2.logFilePath = logCollector.finalize(options.log);
|
|
1048
|
+
}
|
|
1049
|
+
return result2;
|
|
1050
|
+
}
|
|
1051
|
+
let processedCount = 0;
|
|
1052
|
+
let lastDoc = null;
|
|
1053
|
+
while (true) {
|
|
1054
|
+
let paginatedQuery = this.buildQuery().limit(options.batchSize);
|
|
1055
|
+
if (lastDoc) {
|
|
1056
|
+
paginatedQuery = paginatedQuery.startAfter(lastDoc);
|
|
1057
|
+
}
|
|
1058
|
+
const snapshot = await paginatedQuery.get();
|
|
1059
|
+
if (snapshot.empty) break;
|
|
1060
|
+
processedCount = await processDocuments(
|
|
1061
|
+
snapshot.docs,
|
|
1062
|
+
processedCount,
|
|
1063
|
+
totalCount
|
|
1064
|
+
);
|
|
1065
|
+
lastDoc = snapshot.docs[snapshot.docs.length - 1];
|
|
1066
|
+
if (snapshot.docs.length < options.batchSize) break;
|
|
1067
|
+
}
|
|
1068
|
+
} else {
|
|
1069
|
+
const query = this.buildQuery();
|
|
1070
|
+
const snapshot = await query.get();
|
|
1071
|
+
totalCount = snapshot.size;
|
|
1072
|
+
if (totalCount === 0) {
|
|
1073
|
+
const result2 = {
|
|
1074
|
+
successCount: 0,
|
|
1075
|
+
failureCount: 0,
|
|
1076
|
+
skippedCount: 0,
|
|
1077
|
+
totalCount: 0
|
|
1078
|
+
};
|
|
1079
|
+
if (logCollector && options.log) {
|
|
1080
|
+
result2.logFilePath = logCollector.finalize(options.log);
|
|
1081
|
+
}
|
|
1082
|
+
return result2;
|
|
1083
|
+
}
|
|
1084
|
+
await processDocuments(snapshot.docs, 0, totalCount);
|
|
1085
|
+
}
|
|
1086
|
+
const result = {
|
|
1087
|
+
successCount,
|
|
1088
|
+
failureCount,
|
|
1089
|
+
skippedCount,
|
|
1090
|
+
totalCount,
|
|
1091
|
+
failedDocIds: failedDocIds.length > 0 ? failedDocIds : void 0
|
|
1092
|
+
};
|
|
1093
|
+
if (logCollector && options.log) {
|
|
1094
|
+
result.logFilePath = logCollector.finalize(options.log);
|
|
1095
|
+
}
|
|
1096
|
+
return result;
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Copy matching documents to another collection
|
|
1100
|
+
* @param targetCollection - Target collection path to copy documents to
|
|
1101
|
+
* @param options - Copy options (transform, deleteSource for move, progress callback)
|
|
1102
|
+
* @returns Copy result with success/failure counts, copied IDs, and optional log file path
|
|
1103
|
+
*/
|
|
1104
|
+
async copyTo(targetCollection, options = {}) {
|
|
1105
|
+
this.validateSetup();
|
|
1106
|
+
if (!targetCollection || typeof targetCollection !== "string") {
|
|
1107
|
+
throw new Error("Target collection path is required");
|
|
1108
|
+
}
|
|
1109
|
+
if (this.isCollectionGroup) {
|
|
1110
|
+
throw new Error(
|
|
1111
|
+
"copyTo() cannot be used with collectionGroup(). Use collection() with a specific path instead."
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1114
|
+
const logCollector = options.log?.enabled ? createLogCollector("create", targetCollection) : null;
|
|
1115
|
+
const query = this.buildQuery();
|
|
1116
|
+
const snapshot = await query.get();
|
|
1117
|
+
const totalCount = snapshot.size;
|
|
1118
|
+
if (totalCount === 0) {
|
|
1119
|
+
const result2 = {
|
|
1120
|
+
successCount: 0,
|
|
1121
|
+
failureCount: 0,
|
|
1122
|
+
totalCount: 0,
|
|
1123
|
+
copiedIds: []
|
|
1124
|
+
};
|
|
1125
|
+
if (logCollector && options.log) {
|
|
1126
|
+
result2.logFilePath = logCollector.finalize(options.log);
|
|
1127
|
+
}
|
|
1128
|
+
return result2;
|
|
1129
|
+
}
|
|
1130
|
+
let successCount = 0;
|
|
1131
|
+
let failureCount = 0;
|
|
1132
|
+
const copiedIds = [];
|
|
1133
|
+
const failedDocIds = [];
|
|
1134
|
+
const bulkWriter = this.firestore.bulkWriter();
|
|
1135
|
+
const targetCol = this.firestore.collection(targetCollection);
|
|
1136
|
+
let processedCount = 0;
|
|
1137
|
+
const docIdMap = /* @__PURE__ */ new Map();
|
|
1138
|
+
bulkWriter.onWriteResult((ref) => {
|
|
1139
|
+
successCount++;
|
|
1140
|
+
processedCount++;
|
|
1141
|
+
const docId = docIdMap.get(ref.path) || ref.id;
|
|
1142
|
+
copiedIds.push(docId);
|
|
1143
|
+
logCollector?.addEntry(docId, "success");
|
|
1144
|
+
if (options.onProgress) {
|
|
1145
|
+
const progress = calculateProgress(processedCount, totalCount);
|
|
1146
|
+
options.onProgress(progress);
|
|
1147
|
+
}
|
|
1148
|
+
});
|
|
1149
|
+
bulkWriter.onWriteError((error) => {
|
|
1150
|
+
failureCount++;
|
|
1151
|
+
processedCount++;
|
|
1152
|
+
const docId = error.documentRef?.id || "unknown";
|
|
1153
|
+
failedDocIds.push(docId);
|
|
1154
|
+
logCollector?.addEntry(docId, "failure", error.message);
|
|
1155
|
+
if (options.onProgress) {
|
|
1156
|
+
const progress = calculateProgress(processedCount, totalCount);
|
|
1157
|
+
options.onProgress(progress);
|
|
1158
|
+
}
|
|
1159
|
+
return false;
|
|
1160
|
+
});
|
|
1161
|
+
for (const doc of snapshot.docs) {
|
|
1162
|
+
let data = doc.data();
|
|
1163
|
+
if (options.transform) {
|
|
1164
|
+
const transformed = options.transform({ id: doc.id, data });
|
|
1165
|
+
if (transformed === null) {
|
|
1166
|
+
processedCount++;
|
|
1167
|
+
if (options.onProgress) {
|
|
1168
|
+
const progress = calculateProgress(processedCount, totalCount);
|
|
1169
|
+
options.onProgress(progress);
|
|
1170
|
+
}
|
|
1171
|
+
continue;
|
|
1172
|
+
}
|
|
1173
|
+
data = transformed;
|
|
1174
|
+
}
|
|
1175
|
+
const targetRef = targetCol.doc(doc.id);
|
|
1176
|
+
docIdMap.set(targetRef.path, doc.id);
|
|
1177
|
+
bulkWriter.set(targetRef, data);
|
|
1178
|
+
}
|
|
1179
|
+
await bulkWriter.close();
|
|
1180
|
+
if (options.deleteSource && copiedIds.length > 0) {
|
|
1181
|
+
const deleteBulkWriter = this.firestore.bulkWriter();
|
|
1182
|
+
const sourceCol = this.firestore.collection(this.collectionPath);
|
|
1183
|
+
for (const id of copiedIds) {
|
|
1184
|
+
deleteBulkWriter.delete(sourceCol.doc(id));
|
|
1185
|
+
}
|
|
1186
|
+
await deleteBulkWriter.close();
|
|
1187
|
+
}
|
|
1188
|
+
const result = {
|
|
1189
|
+
successCount,
|
|
1190
|
+
failureCount,
|
|
1191
|
+
totalCount,
|
|
1192
|
+
copiedIds,
|
|
1193
|
+
failedDocIds: failedDocIds.length > 0 ? failedDocIds : void 0
|
|
1194
|
+
};
|
|
1195
|
+
if (logCollector && options.log) {
|
|
1196
|
+
result.logFilePath = logCollector.finalize(options.log);
|
|
1197
|
+
}
|
|
1198
|
+
return result;
|
|
1199
|
+
}
|
|
975
1200
|
/**
|
|
976
1201
|
* Upsert documents matching query conditions
|
|
977
1202
|
* Updates existing documents or creates them if they don't exist
|
package/dist/index.mjs
CHANGED
|
@@ -927,6 +927,231 @@ var BatchUpdater = class {
|
|
|
927
927
|
}
|
|
928
928
|
return result;
|
|
929
929
|
}
|
|
930
|
+
/**
|
|
931
|
+
* Transform matching documents using a custom function
|
|
932
|
+
* Reads each document, applies the transform function, and updates with the result
|
|
933
|
+
* @param transformFn - Function that receives { id, data } and returns update data, or null to skip
|
|
934
|
+
* @param options - Transform options (e.g., progress callback, log options, batchSize)
|
|
935
|
+
* @returns Transform result with success/failure/skipped counts and optional log file path
|
|
936
|
+
*/
|
|
937
|
+
async transform(transformFn, options = {}) {
|
|
938
|
+
this.validateSetup();
|
|
939
|
+
if (typeof transformFn !== "function") {
|
|
940
|
+
throw new Error("Transform function is required");
|
|
941
|
+
}
|
|
942
|
+
const logCollector = options.log?.enabled ? createLogCollector("update", this.collectionPath, this.conditions) : null;
|
|
943
|
+
let successCount = 0;
|
|
944
|
+
let failureCount = 0;
|
|
945
|
+
let skippedCount = 0;
|
|
946
|
+
let totalCount = 0;
|
|
947
|
+
const failedDocIds = [];
|
|
948
|
+
const processDocuments = async (docs, processedSoFar, grandTotal) => {
|
|
949
|
+
const bulkWriter = this.firestore.bulkWriter();
|
|
950
|
+
let processedCount = processedSoFar;
|
|
951
|
+
const docIdMap = /* @__PURE__ */ new Map();
|
|
952
|
+
bulkWriter.onWriteResult((ref) => {
|
|
953
|
+
successCount++;
|
|
954
|
+
processedCount++;
|
|
955
|
+
const docId = docIdMap.get(ref.path) || ref.id;
|
|
956
|
+
logCollector?.addEntry(docId, "success");
|
|
957
|
+
if (options.onProgress) {
|
|
958
|
+
const progress = calculateProgress(processedCount, grandTotal);
|
|
959
|
+
options.onProgress(progress);
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
bulkWriter.onWriteError((error) => {
|
|
963
|
+
failureCount++;
|
|
964
|
+
processedCount++;
|
|
965
|
+
const docId = error.documentRef?.id || "unknown";
|
|
966
|
+
failedDocIds.push(docId);
|
|
967
|
+
logCollector?.addEntry(docId, "failure", error.message);
|
|
968
|
+
if (options.onProgress) {
|
|
969
|
+
const progress = calculateProgress(processedCount, grandTotal);
|
|
970
|
+
options.onProgress(progress);
|
|
971
|
+
}
|
|
972
|
+
return false;
|
|
973
|
+
});
|
|
974
|
+
for (const doc of docs) {
|
|
975
|
+
const updateData = transformFn({ id: doc.id, data: doc.data() });
|
|
976
|
+
if (updateData === null) {
|
|
977
|
+
skippedCount++;
|
|
978
|
+
processedCount++;
|
|
979
|
+
if (options.onProgress) {
|
|
980
|
+
const progress = calculateProgress(processedCount, grandTotal);
|
|
981
|
+
options.onProgress(progress);
|
|
982
|
+
}
|
|
983
|
+
continue;
|
|
984
|
+
}
|
|
985
|
+
docIdMap.set(doc.ref.path, doc.id);
|
|
986
|
+
bulkWriter.update(doc.ref, updateData);
|
|
987
|
+
}
|
|
988
|
+
await bulkWriter.close();
|
|
989
|
+
return processedCount;
|
|
990
|
+
};
|
|
991
|
+
if (options.batchSize && options.batchSize > 0) {
|
|
992
|
+
const countSnapshot = await this.buildQuery().count().get();
|
|
993
|
+
totalCount = countSnapshot.data().count;
|
|
994
|
+
if (totalCount === 0) {
|
|
995
|
+
const result2 = {
|
|
996
|
+
successCount: 0,
|
|
997
|
+
failureCount: 0,
|
|
998
|
+
skippedCount: 0,
|
|
999
|
+
totalCount: 0
|
|
1000
|
+
};
|
|
1001
|
+
if (logCollector && options.log) {
|
|
1002
|
+
result2.logFilePath = logCollector.finalize(options.log);
|
|
1003
|
+
}
|
|
1004
|
+
return result2;
|
|
1005
|
+
}
|
|
1006
|
+
let processedCount = 0;
|
|
1007
|
+
let lastDoc = null;
|
|
1008
|
+
while (true) {
|
|
1009
|
+
let paginatedQuery = this.buildQuery().limit(options.batchSize);
|
|
1010
|
+
if (lastDoc) {
|
|
1011
|
+
paginatedQuery = paginatedQuery.startAfter(lastDoc);
|
|
1012
|
+
}
|
|
1013
|
+
const snapshot = await paginatedQuery.get();
|
|
1014
|
+
if (snapshot.empty) break;
|
|
1015
|
+
processedCount = await processDocuments(
|
|
1016
|
+
snapshot.docs,
|
|
1017
|
+
processedCount,
|
|
1018
|
+
totalCount
|
|
1019
|
+
);
|
|
1020
|
+
lastDoc = snapshot.docs[snapshot.docs.length - 1];
|
|
1021
|
+
if (snapshot.docs.length < options.batchSize) break;
|
|
1022
|
+
}
|
|
1023
|
+
} else {
|
|
1024
|
+
const query = this.buildQuery();
|
|
1025
|
+
const snapshot = await query.get();
|
|
1026
|
+
totalCount = snapshot.size;
|
|
1027
|
+
if (totalCount === 0) {
|
|
1028
|
+
const result2 = {
|
|
1029
|
+
successCount: 0,
|
|
1030
|
+
failureCount: 0,
|
|
1031
|
+
skippedCount: 0,
|
|
1032
|
+
totalCount: 0
|
|
1033
|
+
};
|
|
1034
|
+
if (logCollector && options.log) {
|
|
1035
|
+
result2.logFilePath = logCollector.finalize(options.log);
|
|
1036
|
+
}
|
|
1037
|
+
return result2;
|
|
1038
|
+
}
|
|
1039
|
+
await processDocuments(snapshot.docs, 0, totalCount);
|
|
1040
|
+
}
|
|
1041
|
+
const result = {
|
|
1042
|
+
successCount,
|
|
1043
|
+
failureCount,
|
|
1044
|
+
skippedCount,
|
|
1045
|
+
totalCount,
|
|
1046
|
+
failedDocIds: failedDocIds.length > 0 ? failedDocIds : void 0
|
|
1047
|
+
};
|
|
1048
|
+
if (logCollector && options.log) {
|
|
1049
|
+
result.logFilePath = logCollector.finalize(options.log);
|
|
1050
|
+
}
|
|
1051
|
+
return result;
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Copy matching documents to another collection
|
|
1055
|
+
* @param targetCollection - Target collection path to copy documents to
|
|
1056
|
+
* @param options - Copy options (transform, deleteSource for move, progress callback)
|
|
1057
|
+
* @returns Copy result with success/failure counts, copied IDs, and optional log file path
|
|
1058
|
+
*/
|
|
1059
|
+
async copyTo(targetCollection, options = {}) {
|
|
1060
|
+
this.validateSetup();
|
|
1061
|
+
if (!targetCollection || typeof targetCollection !== "string") {
|
|
1062
|
+
throw new Error("Target collection path is required");
|
|
1063
|
+
}
|
|
1064
|
+
if (this.isCollectionGroup) {
|
|
1065
|
+
throw new Error(
|
|
1066
|
+
"copyTo() cannot be used with collectionGroup(). Use collection() with a specific path instead."
|
|
1067
|
+
);
|
|
1068
|
+
}
|
|
1069
|
+
const logCollector = options.log?.enabled ? createLogCollector("create", targetCollection) : null;
|
|
1070
|
+
const query = this.buildQuery();
|
|
1071
|
+
const snapshot = await query.get();
|
|
1072
|
+
const totalCount = snapshot.size;
|
|
1073
|
+
if (totalCount === 0) {
|
|
1074
|
+
const result2 = {
|
|
1075
|
+
successCount: 0,
|
|
1076
|
+
failureCount: 0,
|
|
1077
|
+
totalCount: 0,
|
|
1078
|
+
copiedIds: []
|
|
1079
|
+
};
|
|
1080
|
+
if (logCollector && options.log) {
|
|
1081
|
+
result2.logFilePath = logCollector.finalize(options.log);
|
|
1082
|
+
}
|
|
1083
|
+
return result2;
|
|
1084
|
+
}
|
|
1085
|
+
let successCount = 0;
|
|
1086
|
+
let failureCount = 0;
|
|
1087
|
+
const copiedIds = [];
|
|
1088
|
+
const failedDocIds = [];
|
|
1089
|
+
const bulkWriter = this.firestore.bulkWriter();
|
|
1090
|
+
const targetCol = this.firestore.collection(targetCollection);
|
|
1091
|
+
let processedCount = 0;
|
|
1092
|
+
const docIdMap = /* @__PURE__ */ new Map();
|
|
1093
|
+
bulkWriter.onWriteResult((ref) => {
|
|
1094
|
+
successCount++;
|
|
1095
|
+
processedCount++;
|
|
1096
|
+
const docId = docIdMap.get(ref.path) || ref.id;
|
|
1097
|
+
copiedIds.push(docId);
|
|
1098
|
+
logCollector?.addEntry(docId, "success");
|
|
1099
|
+
if (options.onProgress) {
|
|
1100
|
+
const progress = calculateProgress(processedCount, totalCount);
|
|
1101
|
+
options.onProgress(progress);
|
|
1102
|
+
}
|
|
1103
|
+
});
|
|
1104
|
+
bulkWriter.onWriteError((error) => {
|
|
1105
|
+
failureCount++;
|
|
1106
|
+
processedCount++;
|
|
1107
|
+
const docId = error.documentRef?.id || "unknown";
|
|
1108
|
+
failedDocIds.push(docId);
|
|
1109
|
+
logCollector?.addEntry(docId, "failure", error.message);
|
|
1110
|
+
if (options.onProgress) {
|
|
1111
|
+
const progress = calculateProgress(processedCount, totalCount);
|
|
1112
|
+
options.onProgress(progress);
|
|
1113
|
+
}
|
|
1114
|
+
return false;
|
|
1115
|
+
});
|
|
1116
|
+
for (const doc of snapshot.docs) {
|
|
1117
|
+
let data = doc.data();
|
|
1118
|
+
if (options.transform) {
|
|
1119
|
+
const transformed = options.transform({ id: doc.id, data });
|
|
1120
|
+
if (transformed === null) {
|
|
1121
|
+
processedCount++;
|
|
1122
|
+
if (options.onProgress) {
|
|
1123
|
+
const progress = calculateProgress(processedCount, totalCount);
|
|
1124
|
+
options.onProgress(progress);
|
|
1125
|
+
}
|
|
1126
|
+
continue;
|
|
1127
|
+
}
|
|
1128
|
+
data = transformed;
|
|
1129
|
+
}
|
|
1130
|
+
const targetRef = targetCol.doc(doc.id);
|
|
1131
|
+
docIdMap.set(targetRef.path, doc.id);
|
|
1132
|
+
bulkWriter.set(targetRef, data);
|
|
1133
|
+
}
|
|
1134
|
+
await bulkWriter.close();
|
|
1135
|
+
if (options.deleteSource && copiedIds.length > 0) {
|
|
1136
|
+
const deleteBulkWriter = this.firestore.bulkWriter();
|
|
1137
|
+
const sourceCol = this.firestore.collection(this.collectionPath);
|
|
1138
|
+
for (const id of copiedIds) {
|
|
1139
|
+
deleteBulkWriter.delete(sourceCol.doc(id));
|
|
1140
|
+
}
|
|
1141
|
+
await deleteBulkWriter.close();
|
|
1142
|
+
}
|
|
1143
|
+
const result = {
|
|
1144
|
+
successCount,
|
|
1145
|
+
failureCount,
|
|
1146
|
+
totalCount,
|
|
1147
|
+
copiedIds,
|
|
1148
|
+
failedDocIds: failedDocIds.length > 0 ? failedDocIds : void 0
|
|
1149
|
+
};
|
|
1150
|
+
if (logCollector && options.log) {
|
|
1151
|
+
result.logFilePath = logCollector.finalize(options.log);
|
|
1152
|
+
}
|
|
1153
|
+
return result;
|
|
1154
|
+
}
|
|
930
1155
|
/**
|
|
931
1156
|
* Upsert documents matching query conditions
|
|
932
1157
|
* Updates existing documents or creates them if they don't exist
|