firestore-batch-updater 1.1.0 → 1.2.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 +87 -2
- package/README.md +87 -2
- package/dist/index.d.mts +56 -13
- package/dist/index.d.ts +56 -13
- package/dist/index.js +69 -9
- package/dist/index.mjs +69 -9
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
- 진행 상황 추적 - 실시간 진행률 콜백
|
|
15
15
|
- 일괄 생성/Upsert/삭제 - 여러 문서를 한 번에 생성, upsert 또는 삭제
|
|
16
16
|
- 정렬 및 제한 - `orderBy()`와 `limit()`으로 정밀한 제어
|
|
17
|
-
- FieldValue 지원 - `increment()`, `arrayUnion()`, `serverTimestamp()` 등 사용 가능
|
|
17
|
+
- FieldValue 지원 - `increment()`, `arrayUnion()`, `delete()`, `serverTimestamp()` 등 사용 가능
|
|
18
|
+
- 서브컬렉션 & 컬렉션 그룹 - 서브컬렉션 쿼리 또는 동일 이름의 모든 컬렉션 쿼리
|
|
19
|
+
- Dry Run 모드 - 실제 변경 없이 작업 시뮬레이션
|
|
20
|
+
- 문서 개수 조회 - 문서를 로드하지 않고 빠르게 개수 확인
|
|
18
21
|
- 로그 파일 생성 - 감사를 위한 상세 작업 로그 (선택사항)
|
|
19
22
|
|
|
20
23
|
## 설치
|
|
@@ -75,10 +78,12 @@ console.log(`${result.successCount}개 문서 업데이트 완료`);
|
|
|
75
78
|
|
|
76
79
|
| 메서드 | 설명 | 반환값 |
|
|
77
80
|
|--------|------|--------|
|
|
78
|
-
| `collection(path)` | 작업할 컬렉션 선택 | `this` |
|
|
81
|
+
| `collection(path)` | 작업할 컬렉션 선택 (서브컬렉션 경로 지원) | `this` |
|
|
82
|
+
| `collectionGroup(id)` | 동일 ID의 모든 컬렉션 쿼리 | `this` |
|
|
79
83
|
| `where(field, op, value)` | 필터 조건 추가 (체이닝 가능) | `this` |
|
|
80
84
|
| `orderBy(field, direction?)` | 정렬 추가 (체이닝 가능) | `this` |
|
|
81
85
|
| `limit(count)` | 문서 수 제한 (체이닝 가능) | `this` |
|
|
86
|
+
| `count()` | 매칭되는 문서 개수 조회 | `CountResult` |
|
|
82
87
|
| `preview(data)` | 업데이트 전 미리보기 | `PreviewResult` |
|
|
83
88
|
| `update(data, options?)` | 매칭되는 문서 업데이트 | `UpdateResult` |
|
|
84
89
|
| `create(docs, options?)` | 새 문서 생성 | `CreateResult` |
|
|
@@ -95,6 +100,7 @@ console.log(`${result.successCount}개 문서 업데이트 완료`);
|
|
|
95
100
|
onProgress?: (progress: ProgressInfo) => void;
|
|
96
101
|
log?: LogOptions;
|
|
97
102
|
batchSize?: number; // update/upsert/delete 전용
|
|
103
|
+
dryRun?: boolean; // update/upsert/delete 전용 - 실제 쓰기 없이 시뮬레이션
|
|
98
104
|
}
|
|
99
105
|
|
|
100
106
|
// ProgressInfo
|
|
@@ -116,10 +122,15 @@ console.log(`${result.successCount}개 문서 업데이트 완료`);
|
|
|
116
122
|
- 미설정: 모든 문서를 메모리에 한 번에 로드 (소규모 컬렉션에 적합)
|
|
117
123
|
- 설정 시 (예: `batchSize: 1000`): 커서 페이지네이션을 사용하여 배치 단위로 처리 (대규모 컬렉션의 메모리 문제 방지)
|
|
118
124
|
|
|
125
|
+
**dryRun 옵션:**
|
|
126
|
+
- `true` 설정 시: 실제 변경 없이 `DryRunResult` 반환 (`wouldAffect` 개수와 `sampleIds` 포함)
|
|
127
|
+
|
|
119
128
|
### 반환 타입
|
|
120
129
|
|
|
121
130
|
| 타입 | 필드 |
|
|
122
131
|
|------|------|
|
|
132
|
+
| `CountResult` | `count` |
|
|
133
|
+
| `DryRunResult` | `wouldAffect`, `sampleIds[]`, `operation` |
|
|
123
134
|
| `PreviewResult` | `affectedCount`, `samples[]`, `affectedFields[]` |
|
|
124
135
|
| `UpdateResult` | `successCount`, `failureCount`, `totalCount`, `failedDocIds?`, `logFilePath?` |
|
|
125
136
|
| `CreateResult` | `successCount`, `failureCount`, `totalCount`, `createdIds[]`, `failedDocIds?`, `logFilePath?` |
|
|
@@ -285,6 +296,80 @@ await updater
|
|
|
285
296
|
.collection("users")
|
|
286
297
|
.where("status", "==", "active")
|
|
287
298
|
.update({ updatedAt: FieldValue.serverTimestamp() });
|
|
299
|
+
|
|
300
|
+
// 필드 삭제
|
|
301
|
+
await updater
|
|
302
|
+
.collection("users")
|
|
303
|
+
.where("status", "==", "inactive")
|
|
304
|
+
.update({ temporaryData: FieldValue.delete() });
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### 문서 개수 조회
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
// 문서를 로드하지 않고 빠르게 개수 조회
|
|
311
|
+
const result = await updater
|
|
312
|
+
.collection("users")
|
|
313
|
+
.where("status", "==", "inactive")
|
|
314
|
+
.count();
|
|
315
|
+
|
|
316
|
+
console.log(`${result.count}명의 비활성 사용자 발견`);
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Dry Run 모드
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
// 실제 변경 없이 작업 시뮬레이션
|
|
323
|
+
const simulation = await updater
|
|
324
|
+
.collection("users")
|
|
325
|
+
.where("status", "==", "inactive")
|
|
326
|
+
.update(
|
|
327
|
+
{ status: "archived" },
|
|
328
|
+
{ dryRun: true }
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
console.log(`${simulation.wouldAffect}개 문서가 영향을 받을 예정`);
|
|
332
|
+
console.log("샘플 ID:", simulation.sampleIds);
|
|
333
|
+
|
|
334
|
+
// 삭제에도 사용 가능
|
|
335
|
+
const deleteSimulation = await updater
|
|
336
|
+
.collection("logs")
|
|
337
|
+
.where("createdAt", "<", thirtyDaysAgo)
|
|
338
|
+
.delete({ dryRun: true });
|
|
339
|
+
|
|
340
|
+
console.log(`${deleteSimulation.wouldAffect}개 문서가 삭제될 예정`);
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### 서브컬렉션
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
// 특정 서브컬렉션 경로 쿼리
|
|
347
|
+
const result = await updater
|
|
348
|
+
.collection("users/user-123/orders")
|
|
349
|
+
.where("status", "==", "pending")
|
|
350
|
+
.update({ status: "cancelled" });
|
|
351
|
+
|
|
352
|
+
// 동적 경로 사용
|
|
353
|
+
const userId = "user-123";
|
|
354
|
+
await updater
|
|
355
|
+
.collection(`users/${userId}/notifications`)
|
|
356
|
+
.where("read", "==", false)
|
|
357
|
+
.delete();
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### 컬렉션 그룹 쿼리
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
// 모든 사용자의 "orders" 서브컬렉션을 한 번에 쿼리
|
|
364
|
+
const result = await updater
|
|
365
|
+
.collectionGroup("orders")
|
|
366
|
+
.where("status", "==", "pending")
|
|
367
|
+
.where("createdAt", "<", thirtyDaysAgo)
|
|
368
|
+
.update({ status: "expired" });
|
|
369
|
+
|
|
370
|
+
console.log(`${result.successCount}개 주문 업데이트 완료`);
|
|
371
|
+
|
|
372
|
+
// 참고: collectionGroup은 쿼리 필드에 대한 Firestore 인덱스가 필요합니다
|
|
288
373
|
```
|
|
289
374
|
|
|
290
375
|
> **참고:** 서로 다른 필드에 여러 `where()` 조건을 사용하거나, `where()`와 `orderBy()`를 다른 필드에 사용할 경우, Firestore에서 [복합 인덱스](https://firebase.google.com/docs/firestore/query-data/indexing)가 필요할 수 있습니다. `FAILED_PRECONDITION` 오류가 발생하면 오류 메시지의 링크를 통해 필요한 인덱스를 생성하세요.
|
package/README.md
CHANGED
|
@@ -14,7 +14,10 @@ English | [한국어](./README.ko.md)
|
|
|
14
14
|
- Progress tracking - Real-time progress callbacks
|
|
15
15
|
- Batch create/upsert/delete - Create, upsert, or delete multiple documents at once
|
|
16
16
|
- Sorting and limiting - Use `orderBy()` and `limit()` for precise control
|
|
17
|
-
- FieldValue support - Use `increment()`, `arrayUnion()`, `serverTimestamp()`, etc.
|
|
17
|
+
- FieldValue support - Use `increment()`, `arrayUnion()`, `delete()`, `serverTimestamp()`, etc.
|
|
18
|
+
- Subcollection & Collection Group - Query subcollections or all collections with the same name
|
|
19
|
+
- Dry run mode - Simulate operations without making changes
|
|
20
|
+
- Count documents - Quickly count matching documents without loading them
|
|
18
21
|
- Log file generation - Optional detailed operation logs for auditing
|
|
19
22
|
|
|
20
23
|
## Installation
|
|
@@ -75,10 +78,12 @@ console.log(`Updated ${result.successCount} documents`);
|
|
|
75
78
|
|
|
76
79
|
| Method | Description | Returns |
|
|
77
80
|
|--------|-------------|---------|
|
|
78
|
-
| `collection(path)` | Select collection to operate on | `this` |
|
|
81
|
+
| `collection(path)` | Select collection to operate on (supports subcollection paths) | `this` |
|
|
82
|
+
| `collectionGroup(id)` | Query all collections with the same ID | `this` |
|
|
79
83
|
| `where(field, op, value)` | Add filter condition (chainable) | `this` |
|
|
80
84
|
| `orderBy(field, direction?)` | Add sorting (chainable) | `this` |
|
|
81
85
|
| `limit(count)` | Limit number of documents (chainable) | `this` |
|
|
86
|
+
| `count()` | Count matching documents | `CountResult` |
|
|
82
87
|
| `preview(data)` | Preview changes before update | `PreviewResult` |
|
|
83
88
|
| `update(data, options?)` | Update matching documents | `UpdateResult` |
|
|
84
89
|
| `create(docs, options?)` | Create new documents | `CreateResult` |
|
|
@@ -95,6 +100,7 @@ All write operations support an optional `options` parameter:
|
|
|
95
100
|
onProgress?: (progress: ProgressInfo) => void;
|
|
96
101
|
log?: LogOptions;
|
|
97
102
|
batchSize?: number; // For update/upsert/delete
|
|
103
|
+
dryRun?: boolean; // For update/upsert/delete - simulate without writing
|
|
98
104
|
}
|
|
99
105
|
|
|
100
106
|
// ProgressInfo
|
|
@@ -116,10 +122,15 @@ All write operations support an optional `options` parameter:
|
|
|
116
122
|
- When not set: All documents are loaded into memory at once (suitable for small collections)
|
|
117
123
|
- When set (e.g., `batchSize: 1000`): Documents are processed in batches using cursor pagination (suitable for large collections to prevent memory issues)
|
|
118
124
|
|
|
125
|
+
**dryRun option:**
|
|
126
|
+
- When `true`: Returns `DryRunResult` with `wouldAffect` count and `sampleIds` without making any changes
|
|
127
|
+
|
|
119
128
|
### Return Types
|
|
120
129
|
|
|
121
130
|
| Type | Fields |
|
|
122
131
|
|------|--------|
|
|
132
|
+
| `CountResult` | `count` |
|
|
133
|
+
| `DryRunResult` | `wouldAffect`, `sampleIds[]`, `operation` |
|
|
123
134
|
| `PreviewResult` | `affectedCount`, `samples[]`, `affectedFields[]` |
|
|
124
135
|
| `UpdateResult` | `successCount`, `failureCount`, `totalCount`, `failedDocIds?`, `logFilePath?` |
|
|
125
136
|
| `CreateResult` | `successCount`, `failureCount`, `totalCount`, `createdIds[]`, `failedDocIds?`, `logFilePath?` |
|
|
@@ -284,6 +295,80 @@ await updater
|
|
|
284
295
|
.collection("users")
|
|
285
296
|
.where("status", "==", "active")
|
|
286
297
|
.update({ lastSeen: FieldValue.serverTimestamp() });
|
|
298
|
+
|
|
299
|
+
// Delete a field
|
|
300
|
+
await updater
|
|
301
|
+
.collection("users")
|
|
302
|
+
.where("status", "==", "inactive")
|
|
303
|
+
.update({ temporaryData: FieldValue.delete() });
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Count Documents
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
// Quickly count matching documents without loading them
|
|
310
|
+
const result = await updater
|
|
311
|
+
.collection("users")
|
|
312
|
+
.where("status", "==", "inactive")
|
|
313
|
+
.count();
|
|
314
|
+
|
|
315
|
+
console.log(`Found ${result.count} inactive users`);
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Dry Run Mode
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
// Simulate an operation without making any changes
|
|
322
|
+
const simulation = await updater
|
|
323
|
+
.collection("users")
|
|
324
|
+
.where("status", "==", "inactive")
|
|
325
|
+
.update(
|
|
326
|
+
{ status: "archived" },
|
|
327
|
+
{ dryRun: true }
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
console.log(`Would affect ${simulation.wouldAffect} documents`);
|
|
331
|
+
console.log("Sample IDs:", simulation.sampleIds);
|
|
332
|
+
|
|
333
|
+
// Also works with delete
|
|
334
|
+
const deleteSimulation = await updater
|
|
335
|
+
.collection("logs")
|
|
336
|
+
.where("createdAt", "<", thirtyDaysAgo)
|
|
337
|
+
.delete({ dryRun: true });
|
|
338
|
+
|
|
339
|
+
console.log(`Would delete ${deleteSimulation.wouldAffect} documents`);
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Subcollections
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
// Query a specific subcollection path
|
|
346
|
+
const result = await updater
|
|
347
|
+
.collection("users/user-123/orders")
|
|
348
|
+
.where("status", "==", "pending")
|
|
349
|
+
.update({ status: "cancelled" });
|
|
350
|
+
|
|
351
|
+
// Or use dynamic paths
|
|
352
|
+
const userId = "user-123";
|
|
353
|
+
await updater
|
|
354
|
+
.collection(`users/${userId}/notifications`)
|
|
355
|
+
.where("read", "==", false)
|
|
356
|
+
.delete();
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Collection Group Queries
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
// Query ALL "orders" subcollections across all users
|
|
363
|
+
const result = await updater
|
|
364
|
+
.collectionGroup("orders")
|
|
365
|
+
.where("status", "==", "pending")
|
|
366
|
+
.where("createdAt", "<", thirtyDaysAgo)
|
|
367
|
+
.update({ status: "expired" });
|
|
368
|
+
|
|
369
|
+
console.log(`Updated ${result.successCount} orders across all users`);
|
|
370
|
+
|
|
371
|
+
// Note: collectionGroup requires a Firestore index on the queried fields
|
|
287
372
|
```
|
|
288
373
|
|
|
289
374
|
### Error Handling
|
package/dist/index.d.mts
CHANGED
|
@@ -32,6 +32,11 @@ interface UpdateOptions {
|
|
|
32
32
|
* When not set, all documents are loaded at once
|
|
33
33
|
*/
|
|
34
34
|
batchSize?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Dry run mode - simulate the operation without actually writing
|
|
37
|
+
* Returns what would happen without making any changes
|
|
38
|
+
*/
|
|
39
|
+
dryRun?: boolean;
|
|
35
40
|
}
|
|
36
41
|
/**
|
|
37
42
|
* Result of batch update operation
|
|
@@ -130,6 +135,11 @@ interface UpsertOptions {
|
|
|
130
135
|
* When not set, all documents are loaded at once
|
|
131
136
|
*/
|
|
132
137
|
batchSize?: number;
|
|
138
|
+
/**
|
|
139
|
+
* Dry run mode - simulate the operation without actually writing
|
|
140
|
+
* Returns what would happen without making any changes
|
|
141
|
+
*/
|
|
142
|
+
dryRun?: boolean;
|
|
133
143
|
}
|
|
134
144
|
/**
|
|
135
145
|
* Result of batch upsert operation
|
|
@@ -159,6 +169,11 @@ interface DeleteOptions {
|
|
|
159
169
|
* When not set, all documents are loaded at once
|
|
160
170
|
*/
|
|
161
171
|
batchSize?: number;
|
|
172
|
+
/**
|
|
173
|
+
* Dry run mode - simulate the operation without actually writing
|
|
174
|
+
* Returns what would happen without making any changes
|
|
175
|
+
*/
|
|
176
|
+
dryRun?: boolean;
|
|
162
177
|
}
|
|
163
178
|
/**
|
|
164
179
|
* Result of batch delete operation
|
|
@@ -170,6 +185,20 @@ interface DeleteResult {
|
|
|
170
185
|
deletedIds: string[];
|
|
171
186
|
failedDocIds?: string[];
|
|
172
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Result of count operation
|
|
190
|
+
*/
|
|
191
|
+
interface CountResult {
|
|
192
|
+
count: number;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Result of dry run operation
|
|
196
|
+
*/
|
|
197
|
+
interface DryRunResult {
|
|
198
|
+
wouldAffect: number;
|
|
199
|
+
sampleIds: string[];
|
|
200
|
+
operation: "update" | "upsert" | "delete";
|
|
201
|
+
}
|
|
173
202
|
/**
|
|
174
203
|
* Log options for batch operations
|
|
175
204
|
*/
|
|
@@ -215,6 +244,7 @@ interface OperationLog {
|
|
|
215
244
|
declare class BatchUpdater {
|
|
216
245
|
private firestore;
|
|
217
246
|
private collectionPath?;
|
|
247
|
+
private isCollectionGroup;
|
|
218
248
|
private conditions;
|
|
219
249
|
private orderByConditions;
|
|
220
250
|
private limitCount?;
|
|
@@ -225,10 +255,17 @@ declare class BatchUpdater {
|
|
|
225
255
|
constructor(firestore: Firestore);
|
|
226
256
|
/**
|
|
227
257
|
* Select a collection to operate on
|
|
258
|
+
* Supports subcollection paths like "users/userId/orders"
|
|
228
259
|
* @param path - Collection path
|
|
229
260
|
* @returns This instance for chaining
|
|
230
261
|
*/
|
|
231
262
|
collection(path: string): this;
|
|
263
|
+
/**
|
|
264
|
+
* Select a collection group to operate on (queries across all subcollections with the same name)
|
|
265
|
+
* @param collectionId - Collection ID (not a path, just the collection name)
|
|
266
|
+
* @returns This instance for chaining
|
|
267
|
+
*/
|
|
268
|
+
collectionGroup(collectionId: string): this;
|
|
232
269
|
/**
|
|
233
270
|
* Add a where condition to filter documents
|
|
234
271
|
* @param field - Field path
|
|
@@ -250,6 +287,11 @@ declare class BatchUpdater {
|
|
|
250
287
|
* @returns This instance for chaining
|
|
251
288
|
*/
|
|
252
289
|
limit(count: number): this;
|
|
290
|
+
/**
|
|
291
|
+
* Count documents matching the query conditions
|
|
292
|
+
* @returns Count result with number of matching documents
|
|
293
|
+
*/
|
|
294
|
+
count(): Promise<CountResult>;
|
|
253
295
|
/**
|
|
254
296
|
* Preview changes before executing update
|
|
255
297
|
* @param updateData - Data to update
|
|
@@ -259,12 +301,12 @@ declare class BatchUpdater {
|
|
|
259
301
|
/**
|
|
260
302
|
* Execute batch update operation
|
|
261
303
|
* @param updateData - Data to update
|
|
262
|
-
* @param options - Update options (e.g., progress callback, log options, batchSize for pagination)
|
|
263
|
-
* @returns Update result with success/failure counts and optional log file path
|
|
304
|
+
* @param options - Update options (e.g., progress callback, log options, batchSize for pagination, dryRun)
|
|
305
|
+
* @returns Update result with success/failure counts and optional log file path, or DryRunResult if dryRun is true
|
|
264
306
|
*/
|
|
265
|
-
update(updateData: Record<string, any>, options?: UpdateOptions): Promise<UpdateResult & {
|
|
307
|
+
update(updateData: Record<string, any>, options?: UpdateOptions): Promise<(UpdateResult & {
|
|
266
308
|
logFilePath?: string;
|
|
267
|
-
}>;
|
|
309
|
+
}) | DryRunResult>;
|
|
268
310
|
/**
|
|
269
311
|
* Get specific field values from matching documents
|
|
270
312
|
* @param fieldPath - Field path to retrieve
|
|
@@ -273,6 +315,7 @@ declare class BatchUpdater {
|
|
|
273
315
|
getFields(fieldPath: string): Promise<FieldValueResult[]>;
|
|
274
316
|
/**
|
|
275
317
|
* Create multiple documents in batch
|
|
318
|
+
* Note: This method does not work with collectionGroup()
|
|
276
319
|
* @param documents - Array of documents to create
|
|
277
320
|
* @param options - Create options (e.g., progress callback, log options)
|
|
278
321
|
* @returns Create result with success/failure counts, created IDs, and optional log file path
|
|
@@ -284,20 +327,20 @@ declare class BatchUpdater {
|
|
|
284
327
|
* Upsert documents matching query conditions
|
|
285
328
|
* Updates existing documents or creates them if they don't exist
|
|
286
329
|
* @param updateData - Data to set/merge
|
|
287
|
-
* @param options - Upsert options (e.g., progress callback, log options, batchSize for pagination)
|
|
288
|
-
* @returns Upsert result with success/failure counts and optional log file path
|
|
330
|
+
* @param options - Upsert options (e.g., progress callback, log options, batchSize for pagination, dryRun)
|
|
331
|
+
* @returns Upsert result with success/failure counts and optional log file path, or DryRunResult if dryRun is true
|
|
289
332
|
*/
|
|
290
|
-
upsert(updateData: Record<string, any>, options?: UpsertOptions): Promise<UpsertResult & {
|
|
333
|
+
upsert(updateData: Record<string, any>, options?: UpsertOptions): Promise<(UpsertResult & {
|
|
291
334
|
logFilePath?: string;
|
|
292
|
-
}>;
|
|
335
|
+
}) | DryRunResult>;
|
|
293
336
|
/**
|
|
294
337
|
* Delete documents matching query conditions
|
|
295
|
-
* @param options - Delete options (e.g., progress callback, log options, batchSize for pagination)
|
|
296
|
-
* @returns Delete result with success/failure counts, deleted IDs, and optional log file path
|
|
338
|
+
* @param options - Delete options (e.g., progress callback, log options, batchSize for pagination, dryRun)
|
|
339
|
+
* @returns Delete result with success/failure counts, deleted IDs, and optional log file path, or DryRunResult if dryRun is true
|
|
297
340
|
*/
|
|
298
|
-
delete(options?: DeleteOptions): Promise<DeleteResult & {
|
|
341
|
+
delete(options?: DeleteOptions): Promise<(DeleteResult & {
|
|
299
342
|
logFilePath?: string;
|
|
300
|
-
}>;
|
|
343
|
+
}) | DryRunResult>;
|
|
301
344
|
/**
|
|
302
345
|
* Validate that collection is set
|
|
303
346
|
* @private
|
|
@@ -374,4 +417,4 @@ declare function isValidUpdateData(value: any): value is Record<string, any>;
|
|
|
374
417
|
*/
|
|
375
418
|
declare function formatError(error: unknown, context?: string): string;
|
|
376
419
|
|
|
377
|
-
export { BatchUpdater, type CreateDocumentInput, type CreateOptions, type CreateResult, type DeleteOptions, type DeleteResult, type DocumentSnapshot, type FieldValueResult, type LogEntry, type LogOptions, type OperationLog, type OrderByCondition, type PreviewResult, type ProgressInfo, type UpdateOptions, type UpdateResult, type UpsertOptions, type UpsertResult, type WhereCondition, calculateProgress, createLogCollector, formatError, formatOperationLog, getAffectedFields, isValidUpdateData, mergeUpdateData, writeOperationLog };
|
|
420
|
+
export { BatchUpdater, type CountResult, type CreateDocumentInput, type CreateOptions, type CreateResult, type DeleteOptions, type DeleteResult, type DocumentSnapshot, type DryRunResult, type FieldValueResult, type LogEntry, type LogOptions, type OperationLog, type OrderByCondition, 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
|
@@ -32,6 +32,11 @@ interface UpdateOptions {
|
|
|
32
32
|
* When not set, all documents are loaded at once
|
|
33
33
|
*/
|
|
34
34
|
batchSize?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Dry run mode - simulate the operation without actually writing
|
|
37
|
+
* Returns what would happen without making any changes
|
|
38
|
+
*/
|
|
39
|
+
dryRun?: boolean;
|
|
35
40
|
}
|
|
36
41
|
/**
|
|
37
42
|
* Result of batch update operation
|
|
@@ -130,6 +135,11 @@ interface UpsertOptions {
|
|
|
130
135
|
* When not set, all documents are loaded at once
|
|
131
136
|
*/
|
|
132
137
|
batchSize?: number;
|
|
138
|
+
/**
|
|
139
|
+
* Dry run mode - simulate the operation without actually writing
|
|
140
|
+
* Returns what would happen without making any changes
|
|
141
|
+
*/
|
|
142
|
+
dryRun?: boolean;
|
|
133
143
|
}
|
|
134
144
|
/**
|
|
135
145
|
* Result of batch upsert operation
|
|
@@ -159,6 +169,11 @@ interface DeleteOptions {
|
|
|
159
169
|
* When not set, all documents are loaded at once
|
|
160
170
|
*/
|
|
161
171
|
batchSize?: number;
|
|
172
|
+
/**
|
|
173
|
+
* Dry run mode - simulate the operation without actually writing
|
|
174
|
+
* Returns what would happen without making any changes
|
|
175
|
+
*/
|
|
176
|
+
dryRun?: boolean;
|
|
162
177
|
}
|
|
163
178
|
/**
|
|
164
179
|
* Result of batch delete operation
|
|
@@ -170,6 +185,20 @@ interface DeleteResult {
|
|
|
170
185
|
deletedIds: string[];
|
|
171
186
|
failedDocIds?: string[];
|
|
172
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Result of count operation
|
|
190
|
+
*/
|
|
191
|
+
interface CountResult {
|
|
192
|
+
count: number;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Result of dry run operation
|
|
196
|
+
*/
|
|
197
|
+
interface DryRunResult {
|
|
198
|
+
wouldAffect: number;
|
|
199
|
+
sampleIds: string[];
|
|
200
|
+
operation: "update" | "upsert" | "delete";
|
|
201
|
+
}
|
|
173
202
|
/**
|
|
174
203
|
* Log options for batch operations
|
|
175
204
|
*/
|
|
@@ -215,6 +244,7 @@ interface OperationLog {
|
|
|
215
244
|
declare class BatchUpdater {
|
|
216
245
|
private firestore;
|
|
217
246
|
private collectionPath?;
|
|
247
|
+
private isCollectionGroup;
|
|
218
248
|
private conditions;
|
|
219
249
|
private orderByConditions;
|
|
220
250
|
private limitCount?;
|
|
@@ -225,10 +255,17 @@ declare class BatchUpdater {
|
|
|
225
255
|
constructor(firestore: Firestore);
|
|
226
256
|
/**
|
|
227
257
|
* Select a collection to operate on
|
|
258
|
+
* Supports subcollection paths like "users/userId/orders"
|
|
228
259
|
* @param path - Collection path
|
|
229
260
|
* @returns This instance for chaining
|
|
230
261
|
*/
|
|
231
262
|
collection(path: string): this;
|
|
263
|
+
/**
|
|
264
|
+
* Select a collection group to operate on (queries across all subcollections with the same name)
|
|
265
|
+
* @param collectionId - Collection ID (not a path, just the collection name)
|
|
266
|
+
* @returns This instance for chaining
|
|
267
|
+
*/
|
|
268
|
+
collectionGroup(collectionId: string): this;
|
|
232
269
|
/**
|
|
233
270
|
* Add a where condition to filter documents
|
|
234
271
|
* @param field - Field path
|
|
@@ -250,6 +287,11 @@ declare class BatchUpdater {
|
|
|
250
287
|
* @returns This instance for chaining
|
|
251
288
|
*/
|
|
252
289
|
limit(count: number): this;
|
|
290
|
+
/**
|
|
291
|
+
* Count documents matching the query conditions
|
|
292
|
+
* @returns Count result with number of matching documents
|
|
293
|
+
*/
|
|
294
|
+
count(): Promise<CountResult>;
|
|
253
295
|
/**
|
|
254
296
|
* Preview changes before executing update
|
|
255
297
|
* @param updateData - Data to update
|
|
@@ -259,12 +301,12 @@ declare class BatchUpdater {
|
|
|
259
301
|
/**
|
|
260
302
|
* Execute batch update operation
|
|
261
303
|
* @param updateData - Data to update
|
|
262
|
-
* @param options - Update options (e.g., progress callback, log options, batchSize for pagination)
|
|
263
|
-
* @returns Update result with success/failure counts and optional log file path
|
|
304
|
+
* @param options - Update options (e.g., progress callback, log options, batchSize for pagination, dryRun)
|
|
305
|
+
* @returns Update result with success/failure counts and optional log file path, or DryRunResult if dryRun is true
|
|
264
306
|
*/
|
|
265
|
-
update(updateData: Record<string, any>, options?: UpdateOptions): Promise<UpdateResult & {
|
|
307
|
+
update(updateData: Record<string, any>, options?: UpdateOptions): Promise<(UpdateResult & {
|
|
266
308
|
logFilePath?: string;
|
|
267
|
-
}>;
|
|
309
|
+
}) | DryRunResult>;
|
|
268
310
|
/**
|
|
269
311
|
* Get specific field values from matching documents
|
|
270
312
|
* @param fieldPath - Field path to retrieve
|
|
@@ -273,6 +315,7 @@ declare class BatchUpdater {
|
|
|
273
315
|
getFields(fieldPath: string): Promise<FieldValueResult[]>;
|
|
274
316
|
/**
|
|
275
317
|
* Create multiple documents in batch
|
|
318
|
+
* Note: This method does not work with collectionGroup()
|
|
276
319
|
* @param documents - Array of documents to create
|
|
277
320
|
* @param options - Create options (e.g., progress callback, log options)
|
|
278
321
|
* @returns Create result with success/failure counts, created IDs, and optional log file path
|
|
@@ -284,20 +327,20 @@ declare class BatchUpdater {
|
|
|
284
327
|
* Upsert documents matching query conditions
|
|
285
328
|
* Updates existing documents or creates them if they don't exist
|
|
286
329
|
* @param updateData - Data to set/merge
|
|
287
|
-
* @param options - Upsert options (e.g., progress callback, log options, batchSize for pagination)
|
|
288
|
-
* @returns Upsert result with success/failure counts and optional log file path
|
|
330
|
+
* @param options - Upsert options (e.g., progress callback, log options, batchSize for pagination, dryRun)
|
|
331
|
+
* @returns Upsert result with success/failure counts and optional log file path, or DryRunResult if dryRun is true
|
|
289
332
|
*/
|
|
290
|
-
upsert(updateData: Record<string, any>, options?: UpsertOptions): Promise<UpsertResult & {
|
|
333
|
+
upsert(updateData: Record<string, any>, options?: UpsertOptions): Promise<(UpsertResult & {
|
|
291
334
|
logFilePath?: string;
|
|
292
|
-
}>;
|
|
335
|
+
}) | DryRunResult>;
|
|
293
336
|
/**
|
|
294
337
|
* Delete documents matching query conditions
|
|
295
|
-
* @param options - Delete options (e.g., progress callback, log options, batchSize for pagination)
|
|
296
|
-
* @returns Delete result with success/failure counts, deleted IDs, and optional log file path
|
|
338
|
+
* @param options - Delete options (e.g., progress callback, log options, batchSize for pagination, dryRun)
|
|
339
|
+
* @returns Delete result with success/failure counts, deleted IDs, and optional log file path, or DryRunResult if dryRun is true
|
|
297
340
|
*/
|
|
298
|
-
delete(options?: DeleteOptions): Promise<DeleteResult & {
|
|
341
|
+
delete(options?: DeleteOptions): Promise<(DeleteResult & {
|
|
299
342
|
logFilePath?: string;
|
|
300
|
-
}>;
|
|
343
|
+
}) | DryRunResult>;
|
|
301
344
|
/**
|
|
302
345
|
* Validate that collection is set
|
|
303
346
|
* @private
|
|
@@ -374,4 +417,4 @@ declare function isValidUpdateData(value: any): value is Record<string, any>;
|
|
|
374
417
|
*/
|
|
375
418
|
declare function formatError(error: unknown, context?: string): string;
|
|
376
419
|
|
|
377
|
-
export { BatchUpdater, type CreateDocumentInput, type CreateOptions, type CreateResult, type DeleteOptions, type DeleteResult, type DocumentSnapshot, type FieldValueResult, type LogEntry, type LogOptions, type OperationLog, type OrderByCondition, type PreviewResult, type ProgressInfo, type UpdateOptions, type UpdateResult, type UpsertOptions, type UpsertResult, type WhereCondition, calculateProgress, createLogCollector, formatError, formatOperationLog, getAffectedFields, isValidUpdateData, mergeUpdateData, writeOperationLog };
|
|
420
|
+
export { BatchUpdater, type CountResult, type CreateDocumentInput, type CreateOptions, type CreateResult, type DeleteOptions, type DeleteResult, type DocumentSnapshot, type DryRunResult, type FieldValueResult, type LogEntry, type LogOptions, type OperationLog, type OrderByCondition, 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
|
@@ -196,17 +196,33 @@ var BatchUpdater = class {
|
|
|
196
196
|
* @param firestore - Initialized Firestore instance from firebase-admin
|
|
197
197
|
*/
|
|
198
198
|
constructor(firestore) {
|
|
199
|
+
this.isCollectionGroup = false;
|
|
199
200
|
this.conditions = [];
|
|
200
201
|
this.orderByConditions = [];
|
|
201
202
|
this.firestore = firestore;
|
|
202
203
|
}
|
|
203
204
|
/**
|
|
204
205
|
* Select a collection to operate on
|
|
206
|
+
* Supports subcollection paths like "users/userId/orders"
|
|
205
207
|
* @param path - Collection path
|
|
206
208
|
* @returns This instance for chaining
|
|
207
209
|
*/
|
|
208
210
|
collection(path2) {
|
|
209
211
|
this.collectionPath = path2;
|
|
212
|
+
this.isCollectionGroup = false;
|
|
213
|
+
this.conditions = [];
|
|
214
|
+
this.orderByConditions = [];
|
|
215
|
+
this.limitCount = void 0;
|
|
216
|
+
return this;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Select a collection group to operate on (queries across all subcollections with the same name)
|
|
220
|
+
* @param collectionId - Collection ID (not a path, just the collection name)
|
|
221
|
+
* @returns This instance for chaining
|
|
222
|
+
*/
|
|
223
|
+
collectionGroup(collectionId) {
|
|
224
|
+
this.collectionPath = collectionId;
|
|
225
|
+
this.isCollectionGroup = true;
|
|
210
226
|
this.conditions = [];
|
|
211
227
|
this.orderByConditions = [];
|
|
212
228
|
this.limitCount = void 0;
|
|
@@ -242,6 +258,18 @@ var BatchUpdater = class {
|
|
|
242
258
|
this.limitCount = count;
|
|
243
259
|
return this;
|
|
244
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* Count documents matching the query conditions
|
|
263
|
+
* @returns Count result with number of matching documents
|
|
264
|
+
*/
|
|
265
|
+
async count() {
|
|
266
|
+
this.validateSetup();
|
|
267
|
+
const query = this.buildQuery();
|
|
268
|
+
const snapshot = await query.count().get();
|
|
269
|
+
return {
|
|
270
|
+
count: snapshot.data().count
|
|
271
|
+
};
|
|
272
|
+
}
|
|
245
273
|
/**
|
|
246
274
|
* Preview changes before executing update
|
|
247
275
|
* @param updateData - Data to update
|
|
@@ -276,14 +304,24 @@ var BatchUpdater = class {
|
|
|
276
304
|
/**
|
|
277
305
|
* Execute batch update operation
|
|
278
306
|
* @param updateData - Data to update
|
|
279
|
-
* @param options - Update options (e.g., progress callback, log options, batchSize for pagination)
|
|
280
|
-
* @returns Update result with success/failure counts and optional log file path
|
|
307
|
+
* @param options - Update options (e.g., progress callback, log options, batchSize for pagination, dryRun)
|
|
308
|
+
* @returns Update result with success/failure counts and optional log file path, or DryRunResult if dryRun is true
|
|
281
309
|
*/
|
|
282
310
|
async update(updateData, options = {}) {
|
|
283
311
|
this.validateSetup();
|
|
284
312
|
if (!isValidUpdateData(updateData)) {
|
|
285
313
|
throw new Error("Update data must be a non-empty object");
|
|
286
314
|
}
|
|
315
|
+
if (options.dryRun) {
|
|
316
|
+
const query = this.buildQuery();
|
|
317
|
+
const snapshot = await query.limit(10).get();
|
|
318
|
+
const countSnapshot = await this.buildQuery().count().get();
|
|
319
|
+
return {
|
|
320
|
+
wouldAffect: countSnapshot.data().count,
|
|
321
|
+
sampleIds: snapshot.docs.map((doc) => doc.id),
|
|
322
|
+
operation: "update"
|
|
323
|
+
};
|
|
324
|
+
}
|
|
287
325
|
const logCollector = options.log?.enabled ? createLogCollector("update", this.collectionPath, this.conditions, updateData) : null;
|
|
288
326
|
let successCount = 0;
|
|
289
327
|
let failureCount = 0;
|
|
@@ -432,12 +470,16 @@ var BatchUpdater = class {
|
|
|
432
470
|
}
|
|
433
471
|
/**
|
|
434
472
|
* Create multiple documents in batch
|
|
473
|
+
* Note: This method does not work with collectionGroup()
|
|
435
474
|
* @param documents - Array of documents to create
|
|
436
475
|
* @param options - Create options (e.g., progress callback, log options)
|
|
437
476
|
* @returns Create result with success/failure counts, created IDs, and optional log file path
|
|
438
477
|
*/
|
|
439
478
|
async create(documents, options = {}) {
|
|
440
479
|
this.validateSetup();
|
|
480
|
+
if (this.isCollectionGroup) {
|
|
481
|
+
throw new Error("create() cannot be used with collectionGroup(). Use collection() with a specific path instead.");
|
|
482
|
+
}
|
|
441
483
|
if (!Array.isArray(documents) || documents.length === 0) {
|
|
442
484
|
throw new Error("Documents array must be non-empty");
|
|
443
485
|
}
|
|
@@ -498,14 +540,24 @@ var BatchUpdater = class {
|
|
|
498
540
|
* Upsert documents matching query conditions
|
|
499
541
|
* Updates existing documents or creates them if they don't exist
|
|
500
542
|
* @param updateData - Data to set/merge
|
|
501
|
-
* @param options - Upsert options (e.g., progress callback, log options, batchSize for pagination)
|
|
502
|
-
* @returns Upsert result with success/failure counts and optional log file path
|
|
543
|
+
* @param options - Upsert options (e.g., progress callback, log options, batchSize for pagination, dryRun)
|
|
544
|
+
* @returns Upsert result with success/failure counts and optional log file path, or DryRunResult if dryRun is true
|
|
503
545
|
*/
|
|
504
546
|
async upsert(updateData, options = {}) {
|
|
505
547
|
this.validateSetup();
|
|
506
548
|
if (!isValidUpdateData(updateData)) {
|
|
507
549
|
throw new Error("Update data must be a non-empty object");
|
|
508
550
|
}
|
|
551
|
+
if (options.dryRun) {
|
|
552
|
+
const query = this.buildQuery();
|
|
553
|
+
const snapshot = await query.limit(10).get();
|
|
554
|
+
const countSnapshot = await this.buildQuery().count().get();
|
|
555
|
+
return {
|
|
556
|
+
wouldAffect: countSnapshot.data().count,
|
|
557
|
+
sampleIds: snapshot.docs.map((doc) => doc.id),
|
|
558
|
+
operation: "upsert"
|
|
559
|
+
};
|
|
560
|
+
}
|
|
509
561
|
const logCollector = options.log?.enabled ? createLogCollector("upsert", this.collectionPath, this.conditions, updateData) : null;
|
|
510
562
|
let successCount = 0;
|
|
511
563
|
let failureCount = 0;
|
|
@@ -634,11 +686,21 @@ var BatchUpdater = class {
|
|
|
634
686
|
}
|
|
635
687
|
/**
|
|
636
688
|
* Delete documents matching query conditions
|
|
637
|
-
* @param options - Delete options (e.g., progress callback, log options, batchSize for pagination)
|
|
638
|
-
* @returns Delete result with success/failure counts, deleted IDs, and optional log file path
|
|
689
|
+
* @param options - Delete options (e.g., progress callback, log options, batchSize for pagination, dryRun)
|
|
690
|
+
* @returns Delete result with success/failure counts, deleted IDs, and optional log file path, or DryRunResult if dryRun is true
|
|
639
691
|
*/
|
|
640
692
|
async delete(options = {}) {
|
|
641
693
|
this.validateSetup();
|
|
694
|
+
if (options.dryRun) {
|
|
695
|
+
const query = this.buildQuery();
|
|
696
|
+
const snapshot = await query.limit(10).get();
|
|
697
|
+
const countSnapshot = await this.buildQuery().count().get();
|
|
698
|
+
return {
|
|
699
|
+
wouldAffect: countSnapshot.data().count,
|
|
700
|
+
sampleIds: snapshot.docs.map((doc) => doc.id),
|
|
701
|
+
operation: "delete"
|
|
702
|
+
};
|
|
703
|
+
}
|
|
642
704
|
const logCollector = options.log?.enabled ? createLogCollector("delete", this.collectionPath, this.conditions) : null;
|
|
643
705
|
let successCount = 0;
|
|
644
706
|
let failureCount = 0;
|
|
@@ -785,9 +847,7 @@ var BatchUpdater = class {
|
|
|
785
847
|
* @private
|
|
786
848
|
*/
|
|
787
849
|
buildQuery() {
|
|
788
|
-
let query = this.firestore.collection(
|
|
789
|
-
this.collectionPath
|
|
790
|
-
);
|
|
850
|
+
let query = this.isCollectionGroup ? this.firestore.collectionGroup(this.collectionPath) : this.firestore.collection(this.collectionPath);
|
|
791
851
|
for (const condition of this.conditions) {
|
|
792
852
|
query = query.where(condition.field, condition.operator, condition.value);
|
|
793
853
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -151,17 +151,33 @@ var BatchUpdater = class {
|
|
|
151
151
|
* @param firestore - Initialized Firestore instance from firebase-admin
|
|
152
152
|
*/
|
|
153
153
|
constructor(firestore) {
|
|
154
|
+
this.isCollectionGroup = false;
|
|
154
155
|
this.conditions = [];
|
|
155
156
|
this.orderByConditions = [];
|
|
156
157
|
this.firestore = firestore;
|
|
157
158
|
}
|
|
158
159
|
/**
|
|
159
160
|
* Select a collection to operate on
|
|
161
|
+
* Supports subcollection paths like "users/userId/orders"
|
|
160
162
|
* @param path - Collection path
|
|
161
163
|
* @returns This instance for chaining
|
|
162
164
|
*/
|
|
163
165
|
collection(path2) {
|
|
164
166
|
this.collectionPath = path2;
|
|
167
|
+
this.isCollectionGroup = false;
|
|
168
|
+
this.conditions = [];
|
|
169
|
+
this.orderByConditions = [];
|
|
170
|
+
this.limitCount = void 0;
|
|
171
|
+
return this;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Select a collection group to operate on (queries across all subcollections with the same name)
|
|
175
|
+
* @param collectionId - Collection ID (not a path, just the collection name)
|
|
176
|
+
* @returns This instance for chaining
|
|
177
|
+
*/
|
|
178
|
+
collectionGroup(collectionId) {
|
|
179
|
+
this.collectionPath = collectionId;
|
|
180
|
+
this.isCollectionGroup = true;
|
|
165
181
|
this.conditions = [];
|
|
166
182
|
this.orderByConditions = [];
|
|
167
183
|
this.limitCount = void 0;
|
|
@@ -197,6 +213,18 @@ var BatchUpdater = class {
|
|
|
197
213
|
this.limitCount = count;
|
|
198
214
|
return this;
|
|
199
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Count documents matching the query conditions
|
|
218
|
+
* @returns Count result with number of matching documents
|
|
219
|
+
*/
|
|
220
|
+
async count() {
|
|
221
|
+
this.validateSetup();
|
|
222
|
+
const query = this.buildQuery();
|
|
223
|
+
const snapshot = await query.count().get();
|
|
224
|
+
return {
|
|
225
|
+
count: snapshot.data().count
|
|
226
|
+
};
|
|
227
|
+
}
|
|
200
228
|
/**
|
|
201
229
|
* Preview changes before executing update
|
|
202
230
|
* @param updateData - Data to update
|
|
@@ -231,14 +259,24 @@ var BatchUpdater = class {
|
|
|
231
259
|
/**
|
|
232
260
|
* Execute batch update operation
|
|
233
261
|
* @param updateData - Data to update
|
|
234
|
-
* @param options - Update options (e.g., progress callback, log options, batchSize for pagination)
|
|
235
|
-
* @returns Update result with success/failure counts and optional log file path
|
|
262
|
+
* @param options - Update options (e.g., progress callback, log options, batchSize for pagination, dryRun)
|
|
263
|
+
* @returns Update result with success/failure counts and optional log file path, or DryRunResult if dryRun is true
|
|
236
264
|
*/
|
|
237
265
|
async update(updateData, options = {}) {
|
|
238
266
|
this.validateSetup();
|
|
239
267
|
if (!isValidUpdateData(updateData)) {
|
|
240
268
|
throw new Error("Update data must be a non-empty object");
|
|
241
269
|
}
|
|
270
|
+
if (options.dryRun) {
|
|
271
|
+
const query = this.buildQuery();
|
|
272
|
+
const snapshot = await query.limit(10).get();
|
|
273
|
+
const countSnapshot = await this.buildQuery().count().get();
|
|
274
|
+
return {
|
|
275
|
+
wouldAffect: countSnapshot.data().count,
|
|
276
|
+
sampleIds: snapshot.docs.map((doc) => doc.id),
|
|
277
|
+
operation: "update"
|
|
278
|
+
};
|
|
279
|
+
}
|
|
242
280
|
const logCollector = options.log?.enabled ? createLogCollector("update", this.collectionPath, this.conditions, updateData) : null;
|
|
243
281
|
let successCount = 0;
|
|
244
282
|
let failureCount = 0;
|
|
@@ -387,12 +425,16 @@ var BatchUpdater = class {
|
|
|
387
425
|
}
|
|
388
426
|
/**
|
|
389
427
|
* Create multiple documents in batch
|
|
428
|
+
* Note: This method does not work with collectionGroup()
|
|
390
429
|
* @param documents - Array of documents to create
|
|
391
430
|
* @param options - Create options (e.g., progress callback, log options)
|
|
392
431
|
* @returns Create result with success/failure counts, created IDs, and optional log file path
|
|
393
432
|
*/
|
|
394
433
|
async create(documents, options = {}) {
|
|
395
434
|
this.validateSetup();
|
|
435
|
+
if (this.isCollectionGroup) {
|
|
436
|
+
throw new Error("create() cannot be used with collectionGroup(). Use collection() with a specific path instead.");
|
|
437
|
+
}
|
|
396
438
|
if (!Array.isArray(documents) || documents.length === 0) {
|
|
397
439
|
throw new Error("Documents array must be non-empty");
|
|
398
440
|
}
|
|
@@ -453,14 +495,24 @@ var BatchUpdater = class {
|
|
|
453
495
|
* Upsert documents matching query conditions
|
|
454
496
|
* Updates existing documents or creates them if they don't exist
|
|
455
497
|
* @param updateData - Data to set/merge
|
|
456
|
-
* @param options - Upsert options (e.g., progress callback, log options, batchSize for pagination)
|
|
457
|
-
* @returns Upsert result with success/failure counts and optional log file path
|
|
498
|
+
* @param options - Upsert options (e.g., progress callback, log options, batchSize for pagination, dryRun)
|
|
499
|
+
* @returns Upsert result with success/failure counts and optional log file path, or DryRunResult if dryRun is true
|
|
458
500
|
*/
|
|
459
501
|
async upsert(updateData, options = {}) {
|
|
460
502
|
this.validateSetup();
|
|
461
503
|
if (!isValidUpdateData(updateData)) {
|
|
462
504
|
throw new Error("Update data must be a non-empty object");
|
|
463
505
|
}
|
|
506
|
+
if (options.dryRun) {
|
|
507
|
+
const query = this.buildQuery();
|
|
508
|
+
const snapshot = await query.limit(10).get();
|
|
509
|
+
const countSnapshot = await this.buildQuery().count().get();
|
|
510
|
+
return {
|
|
511
|
+
wouldAffect: countSnapshot.data().count,
|
|
512
|
+
sampleIds: snapshot.docs.map((doc) => doc.id),
|
|
513
|
+
operation: "upsert"
|
|
514
|
+
};
|
|
515
|
+
}
|
|
464
516
|
const logCollector = options.log?.enabled ? createLogCollector("upsert", this.collectionPath, this.conditions, updateData) : null;
|
|
465
517
|
let successCount = 0;
|
|
466
518
|
let failureCount = 0;
|
|
@@ -589,11 +641,21 @@ var BatchUpdater = class {
|
|
|
589
641
|
}
|
|
590
642
|
/**
|
|
591
643
|
* Delete documents matching query conditions
|
|
592
|
-
* @param options - Delete options (e.g., progress callback, log options, batchSize for pagination)
|
|
593
|
-
* @returns Delete result with success/failure counts, deleted IDs, and optional log file path
|
|
644
|
+
* @param options - Delete options (e.g., progress callback, log options, batchSize for pagination, dryRun)
|
|
645
|
+
* @returns Delete result with success/failure counts, deleted IDs, and optional log file path, or DryRunResult if dryRun is true
|
|
594
646
|
*/
|
|
595
647
|
async delete(options = {}) {
|
|
596
648
|
this.validateSetup();
|
|
649
|
+
if (options.dryRun) {
|
|
650
|
+
const query = this.buildQuery();
|
|
651
|
+
const snapshot = await query.limit(10).get();
|
|
652
|
+
const countSnapshot = await this.buildQuery().count().get();
|
|
653
|
+
return {
|
|
654
|
+
wouldAffect: countSnapshot.data().count,
|
|
655
|
+
sampleIds: snapshot.docs.map((doc) => doc.id),
|
|
656
|
+
operation: "delete"
|
|
657
|
+
};
|
|
658
|
+
}
|
|
597
659
|
const logCollector = options.log?.enabled ? createLogCollector("delete", this.collectionPath, this.conditions) : null;
|
|
598
660
|
let successCount = 0;
|
|
599
661
|
let failureCount = 0;
|
|
@@ -740,9 +802,7 @@ var BatchUpdater = class {
|
|
|
740
802
|
* @private
|
|
741
803
|
*/
|
|
742
804
|
buildQuery() {
|
|
743
|
-
let query = this.firestore.collection(
|
|
744
|
-
this.collectionPath
|
|
745
|
-
);
|
|
805
|
+
let query = this.isCollectionGroup ? this.firestore.collectionGroup(this.collectionPath) : this.firestore.collection(this.collectionPath);
|
|
746
806
|
for (const condition of this.conditions) {
|
|
747
807
|
query = query.where(condition.field, condition.operator, condition.value);
|
|
748
808
|
}
|