firestore-batch-updater 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +48 -0
- package/README.md +48 -0
- package/dist/index.d.mts +46 -3
- package/dist/index.d.ts +46 -3
- package/dist/index.js +41 -2
- package/dist/index.mjs +41 -2
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
- 진행 상황 추적 - 실시간 진행률 콜백
|
|
15
15
|
- 일괄 생성/Upsert/삭제 - 여러 문서를 한 번에 생성, upsert 또는 삭제
|
|
16
16
|
- 정렬 및 제한 - `orderBy()`와 `limit()`으로 정밀한 제어
|
|
17
|
+
- 필드 선택 - `select()`로 필요한 필드만 로드 (메모리 및 비용 절약)
|
|
18
|
+
- 단일 문서 조회 - `findOne()`으로 효율적인 단일 문서 검색
|
|
17
19
|
- FieldValue 지원 - `increment()`, `arrayUnion()`, `delete()`, `serverTimestamp()` 등 사용 가능
|
|
18
20
|
- 서브컬렉션 & 컬렉션 그룹 - 서브컬렉션 쿼리 또는 동일 이름의 모든 컬렉션 쿼리
|
|
19
21
|
- Dry Run 모드 - 실제 변경 없이 작업 시뮬레이션
|
|
@@ -83,7 +85,9 @@ console.log(`${result.successCount}개 문서 업데이트 완료`);
|
|
|
83
85
|
| `where(field, op, value)` | 필터 조건 추가 (체이닝 가능) | `this` |
|
|
84
86
|
| `orderBy(field, direction?)` | 정렬 추가 (체이닝 가능) | `this` |
|
|
85
87
|
| `limit(count)` | 문서 수 제한 (체이닝 가능) | `this` |
|
|
88
|
+
| `select(...fields)` | 특정 필드만 조회 (체이닝 가능) | `this` |
|
|
86
89
|
| `count()` | 매칭되는 문서 개수 조회 | `CountResult` |
|
|
90
|
+
| `findOne()` | 첫 번째 매칭 문서 조회 | `{ id, data } \| null` |
|
|
87
91
|
| `preview(data)` | 업데이트 전 미리보기 | `PreviewResult` |
|
|
88
92
|
| `update(data, options?)` | 매칭되는 문서 업데이트 | `UpdateResult` |
|
|
89
93
|
| `create(docs, options?)` | 새 문서 생성 | `CreateResult` |
|
|
@@ -316,6 +320,50 @@ const result = await updater
|
|
|
316
320
|
console.log(`${result.count}명의 비활성 사용자 발견`);
|
|
317
321
|
```
|
|
318
322
|
|
|
323
|
+
### 특정 필드만 조회
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
// name, email 필드만 로드 (메모리 및 읽기 비용 절약)
|
|
327
|
+
const result = await updater
|
|
328
|
+
.collection("users")
|
|
329
|
+
.select("name", "email")
|
|
330
|
+
.where("status", "==", "active")
|
|
331
|
+
.findOne();
|
|
332
|
+
|
|
333
|
+
console.log(result?.data); // { name, email }만 포함
|
|
334
|
+
|
|
335
|
+
// 모든 작업에서 사용 가능 - 문서에 선택된 필드만 포함됨
|
|
336
|
+
const emails = await updater
|
|
337
|
+
.collection("users")
|
|
338
|
+
.select("email")
|
|
339
|
+
.where("verified", "==", true)
|
|
340
|
+
.getFields("email");
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### 단일 문서 조회
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
// 첫 번째 매칭 문서 찾기
|
|
347
|
+
const user = await updater
|
|
348
|
+
.collection("users")
|
|
349
|
+
.where("email", "==", "user@example.com")
|
|
350
|
+
.findOne();
|
|
351
|
+
|
|
352
|
+
if (user) {
|
|
353
|
+
console.log("사용자 발견:", user.id);
|
|
354
|
+
console.log("사용자 데이터:", user.data);
|
|
355
|
+
} else {
|
|
356
|
+
console.log("사용자를 찾을 수 없음");
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// select와 함께 사용하여 효율적인 조회
|
|
360
|
+
const profile = await updater
|
|
361
|
+
.collection("users")
|
|
362
|
+
.select("name", "avatar", "tier")
|
|
363
|
+
.where("username", "==", "johndoe")
|
|
364
|
+
.findOne();
|
|
365
|
+
```
|
|
366
|
+
|
|
319
367
|
### Dry Run 모드
|
|
320
368
|
|
|
321
369
|
```typescript
|
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@ 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
|
+
- Field selection - Use `select()` to load only needed fields (saves memory and costs)
|
|
18
|
+
- Find single document - Use `findOne()` for efficient single-document retrieval
|
|
17
19
|
- FieldValue support - Use `increment()`, `arrayUnion()`, `delete()`, `serverTimestamp()`, etc.
|
|
18
20
|
- Subcollection & Collection Group - Query subcollections or all collections with the same name
|
|
19
21
|
- Dry run mode - Simulate operations without making changes
|
|
@@ -83,7 +85,9 @@ console.log(`Updated ${result.successCount} documents`);
|
|
|
83
85
|
| `where(field, op, value)` | Add filter condition (chainable) | `this` |
|
|
84
86
|
| `orderBy(field, direction?)` | Add sorting (chainable) | `this` |
|
|
85
87
|
| `limit(count)` | Limit number of documents (chainable) | `this` |
|
|
88
|
+
| `select(...fields)` | Select specific fields to retrieve (chainable) | `this` |
|
|
86
89
|
| `count()` | Count matching documents | `CountResult` |
|
|
90
|
+
| `findOne()` | Find first matching document | `{ id, data } \| null` |
|
|
87
91
|
| `preview(data)` | Preview changes before update | `PreviewResult` |
|
|
88
92
|
| `update(data, options?)` | Update matching documents | `UpdateResult` |
|
|
89
93
|
| `create(docs, options?)` | Create new documents | `CreateResult` |
|
|
@@ -315,6 +319,50 @@ const result = await updater
|
|
|
315
319
|
console.log(`Found ${result.count} inactive users`);
|
|
316
320
|
```
|
|
317
321
|
|
|
322
|
+
### Select Specific Fields
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
// Only load name and email fields (reduces memory and read costs)
|
|
326
|
+
const result = await updater
|
|
327
|
+
.collection("users")
|
|
328
|
+
.select("name", "email")
|
|
329
|
+
.where("status", "==", "active")
|
|
330
|
+
.findOne();
|
|
331
|
+
|
|
332
|
+
console.log(result?.data); // Only contains { name, email }
|
|
333
|
+
|
|
334
|
+
// Works with all operations - documents will only have selected fields
|
|
335
|
+
const emails = await updater
|
|
336
|
+
.collection("users")
|
|
337
|
+
.select("email")
|
|
338
|
+
.where("verified", "==", true)
|
|
339
|
+
.getFields("email");
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Find Single Document
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
// Find first matching document
|
|
346
|
+
const user = await updater
|
|
347
|
+
.collection("users")
|
|
348
|
+
.where("email", "==", "user@example.com")
|
|
349
|
+
.findOne();
|
|
350
|
+
|
|
351
|
+
if (user) {
|
|
352
|
+
console.log("Found user:", user.id);
|
|
353
|
+
console.log("User data:", user.data);
|
|
354
|
+
} else {
|
|
355
|
+
console.log("User not found");
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Combine with select for efficient lookup
|
|
359
|
+
const profile = await updater
|
|
360
|
+
.collection("users")
|
|
361
|
+
.select("name", "avatar", "tier")
|
|
362
|
+
.where("username", "==", "johndoe")
|
|
363
|
+
.findOne();
|
|
364
|
+
```
|
|
365
|
+
|
|
318
366
|
### Dry Run Mode
|
|
319
367
|
|
|
320
368
|
```typescript
|
package/dist/index.d.mts
CHANGED
|
@@ -12,6 +12,9 @@ interface ProgressInfo {
|
|
|
12
12
|
current: number;
|
|
13
13
|
total: number;
|
|
14
14
|
percentage: number;
|
|
15
|
+
elapsedTime: number;
|
|
16
|
+
docsPerSecond: number;
|
|
17
|
+
eta: number;
|
|
15
18
|
}
|
|
16
19
|
/**
|
|
17
20
|
* Options for update operations
|
|
@@ -37,6 +40,14 @@ interface UpdateOptions {
|
|
|
37
40
|
* Returns what would happen without making any changes
|
|
38
41
|
*/
|
|
39
42
|
dryRun?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Maximum number of retry attempts for failed operations (default: 0)
|
|
45
|
+
*/
|
|
46
|
+
retries?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Delay between retry attempts in milliseconds (default: 1000)
|
|
49
|
+
*/
|
|
50
|
+
retryDelay?: number;
|
|
40
51
|
}
|
|
41
52
|
/**
|
|
42
53
|
* Result of batch update operation
|
|
@@ -140,6 +151,14 @@ interface UpsertOptions {
|
|
|
140
151
|
* Returns what would happen without making any changes
|
|
141
152
|
*/
|
|
142
153
|
dryRun?: boolean;
|
|
154
|
+
/**
|
|
155
|
+
* Maximum number of retry attempts for failed operations (default: 0)
|
|
156
|
+
*/
|
|
157
|
+
retries?: number;
|
|
158
|
+
/**
|
|
159
|
+
* Delay between retry attempts in milliseconds (default: 1000)
|
|
160
|
+
*/
|
|
161
|
+
retryDelay?: number;
|
|
143
162
|
}
|
|
144
163
|
/**
|
|
145
164
|
* Result of batch upsert operation
|
|
@@ -174,6 +193,14 @@ interface DeleteOptions {
|
|
|
174
193
|
* Returns what would happen without making any changes
|
|
175
194
|
*/
|
|
176
195
|
dryRun?: boolean;
|
|
196
|
+
/**
|
|
197
|
+
* Maximum number of retry attempts for failed operations (default: 0)
|
|
198
|
+
*/
|
|
199
|
+
retries?: number;
|
|
200
|
+
/**
|
|
201
|
+
* Delay between retry attempts in milliseconds (default: 1000)
|
|
202
|
+
*/
|
|
203
|
+
retryDelay?: number;
|
|
177
204
|
}
|
|
178
205
|
/**
|
|
179
206
|
* Result of batch delete operation
|
|
@@ -248,6 +275,7 @@ declare class BatchUpdater {
|
|
|
248
275
|
private conditions;
|
|
249
276
|
private orderByConditions;
|
|
250
277
|
private limitCount?;
|
|
278
|
+
private selectedFields?;
|
|
251
279
|
/**
|
|
252
280
|
* Create a new BatchUpdater instance
|
|
253
281
|
* @param firestore - Initialized Firestore instance from firebase-admin
|
|
@@ -287,11 +315,25 @@ declare class BatchUpdater {
|
|
|
287
315
|
* @returns This instance for chaining
|
|
288
316
|
*/
|
|
289
317
|
limit(count: number): this;
|
|
318
|
+
/**
|
|
319
|
+
* Select specific fields to retrieve (reduces memory usage and read costs)
|
|
320
|
+
* @param fields - Field paths to retrieve
|
|
321
|
+
* @returns This instance for chaining
|
|
322
|
+
*/
|
|
323
|
+
select(...fields: string[]): this;
|
|
290
324
|
/**
|
|
291
325
|
* Count documents matching the query conditions
|
|
292
326
|
* @returns Count result with number of matching documents
|
|
293
327
|
*/
|
|
294
328
|
count(): Promise<CountResult>;
|
|
329
|
+
/**
|
|
330
|
+
* Find the first document matching the query conditions
|
|
331
|
+
* @returns First matching document with id and data, or null if not found
|
|
332
|
+
*/
|
|
333
|
+
findOne(): Promise<{
|
|
334
|
+
id: string;
|
|
335
|
+
data: Record<string, any>;
|
|
336
|
+
} | null>;
|
|
295
337
|
/**
|
|
296
338
|
* Preview changes before executing update
|
|
297
339
|
* @param updateData - Data to update
|
|
@@ -384,12 +426,13 @@ declare function createLogCollector(operation: "update" | "create" | "upsert" |
|
|
|
384
426
|
*/
|
|
385
427
|
|
|
386
428
|
/**
|
|
387
|
-
* Calculate progress information
|
|
429
|
+
* Calculate progress information with timing details
|
|
388
430
|
* @param current - Number of documents processed so far
|
|
389
431
|
* @param total - Total number of documents to process
|
|
390
|
-
* @
|
|
432
|
+
* @param startTime - Start time of the operation (from Date.now())
|
|
433
|
+
* @returns Progress information with percentage, timing, and ETA
|
|
391
434
|
*/
|
|
392
|
-
declare function calculateProgress(current: number, total: number): ProgressInfo;
|
|
435
|
+
declare function calculateProgress(current: number, total: number, startTime?: number): ProgressInfo;
|
|
393
436
|
/**
|
|
394
437
|
* Extract field names from update data
|
|
395
438
|
* @param updateData - Data to be updated
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,9 @@ interface ProgressInfo {
|
|
|
12
12
|
current: number;
|
|
13
13
|
total: number;
|
|
14
14
|
percentage: number;
|
|
15
|
+
elapsedTime: number;
|
|
16
|
+
docsPerSecond: number;
|
|
17
|
+
eta: number;
|
|
15
18
|
}
|
|
16
19
|
/**
|
|
17
20
|
* Options for update operations
|
|
@@ -37,6 +40,14 @@ interface UpdateOptions {
|
|
|
37
40
|
* Returns what would happen without making any changes
|
|
38
41
|
*/
|
|
39
42
|
dryRun?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Maximum number of retry attempts for failed operations (default: 0)
|
|
45
|
+
*/
|
|
46
|
+
retries?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Delay between retry attempts in milliseconds (default: 1000)
|
|
49
|
+
*/
|
|
50
|
+
retryDelay?: number;
|
|
40
51
|
}
|
|
41
52
|
/**
|
|
42
53
|
* Result of batch update operation
|
|
@@ -140,6 +151,14 @@ interface UpsertOptions {
|
|
|
140
151
|
* Returns what would happen without making any changes
|
|
141
152
|
*/
|
|
142
153
|
dryRun?: boolean;
|
|
154
|
+
/**
|
|
155
|
+
* Maximum number of retry attempts for failed operations (default: 0)
|
|
156
|
+
*/
|
|
157
|
+
retries?: number;
|
|
158
|
+
/**
|
|
159
|
+
* Delay between retry attempts in milliseconds (default: 1000)
|
|
160
|
+
*/
|
|
161
|
+
retryDelay?: number;
|
|
143
162
|
}
|
|
144
163
|
/**
|
|
145
164
|
* Result of batch upsert operation
|
|
@@ -174,6 +193,14 @@ interface DeleteOptions {
|
|
|
174
193
|
* Returns what would happen without making any changes
|
|
175
194
|
*/
|
|
176
195
|
dryRun?: boolean;
|
|
196
|
+
/**
|
|
197
|
+
* Maximum number of retry attempts for failed operations (default: 0)
|
|
198
|
+
*/
|
|
199
|
+
retries?: number;
|
|
200
|
+
/**
|
|
201
|
+
* Delay between retry attempts in milliseconds (default: 1000)
|
|
202
|
+
*/
|
|
203
|
+
retryDelay?: number;
|
|
177
204
|
}
|
|
178
205
|
/**
|
|
179
206
|
* Result of batch delete operation
|
|
@@ -248,6 +275,7 @@ declare class BatchUpdater {
|
|
|
248
275
|
private conditions;
|
|
249
276
|
private orderByConditions;
|
|
250
277
|
private limitCount?;
|
|
278
|
+
private selectedFields?;
|
|
251
279
|
/**
|
|
252
280
|
* Create a new BatchUpdater instance
|
|
253
281
|
* @param firestore - Initialized Firestore instance from firebase-admin
|
|
@@ -287,11 +315,25 @@ declare class BatchUpdater {
|
|
|
287
315
|
* @returns This instance for chaining
|
|
288
316
|
*/
|
|
289
317
|
limit(count: number): this;
|
|
318
|
+
/**
|
|
319
|
+
* Select specific fields to retrieve (reduces memory usage and read costs)
|
|
320
|
+
* @param fields - Field paths to retrieve
|
|
321
|
+
* @returns This instance for chaining
|
|
322
|
+
*/
|
|
323
|
+
select(...fields: string[]): this;
|
|
290
324
|
/**
|
|
291
325
|
* Count documents matching the query conditions
|
|
292
326
|
* @returns Count result with number of matching documents
|
|
293
327
|
*/
|
|
294
328
|
count(): Promise<CountResult>;
|
|
329
|
+
/**
|
|
330
|
+
* Find the first document matching the query conditions
|
|
331
|
+
* @returns First matching document with id and data, or null if not found
|
|
332
|
+
*/
|
|
333
|
+
findOne(): Promise<{
|
|
334
|
+
id: string;
|
|
335
|
+
data: Record<string, any>;
|
|
336
|
+
} | null>;
|
|
295
337
|
/**
|
|
296
338
|
* Preview changes before executing update
|
|
297
339
|
* @param updateData - Data to update
|
|
@@ -384,12 +426,13 @@ declare function createLogCollector(operation: "update" | "create" | "upsert" |
|
|
|
384
426
|
*/
|
|
385
427
|
|
|
386
428
|
/**
|
|
387
|
-
* Calculate progress information
|
|
429
|
+
* Calculate progress information with timing details
|
|
388
430
|
* @param current - Number of documents processed so far
|
|
389
431
|
* @param total - Total number of documents to process
|
|
390
|
-
* @
|
|
432
|
+
* @param startTime - Start time of the operation (from Date.now())
|
|
433
|
+
* @returns Progress information with percentage, timing, and ETA
|
|
391
434
|
*/
|
|
392
|
-
declare function calculateProgress(current: number, total: number): ProgressInfo;
|
|
435
|
+
declare function calculateProgress(current: number, total: number, startTime?: number): ProgressInfo;
|
|
393
436
|
/**
|
|
394
437
|
* Extract field names from update data
|
|
395
438
|
* @param updateData - Data to be updated
|
package/dist/index.js
CHANGED
|
@@ -164,12 +164,20 @@ function createLogCollector(operation, collection, conditions, updateData) {
|
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
// src/utils/index.ts
|
|
167
|
-
function calculateProgress(current, total) {
|
|
167
|
+
function calculateProgress(current, total, startTime) {
|
|
168
168
|
const percentage = total === 0 ? 0 : Math.round(current / total * 100);
|
|
169
|
+
const now = Date.now();
|
|
170
|
+
const elapsedTime = startTime ? now - startTime : 0;
|
|
171
|
+
const docsPerSecond = elapsedTime > 0 ? Math.round(current / elapsedTime * 1e3 * 100) / 100 : 0;
|
|
172
|
+
const remaining = total - current;
|
|
173
|
+
const eta = docsPerSecond > 0 ? Math.round(remaining / docsPerSecond * 100) / 100 : 0;
|
|
169
174
|
return {
|
|
170
175
|
current,
|
|
171
176
|
total,
|
|
172
|
-
percentage
|
|
177
|
+
percentage,
|
|
178
|
+
elapsedTime,
|
|
179
|
+
docsPerSecond,
|
|
180
|
+
eta
|
|
173
181
|
};
|
|
174
182
|
}
|
|
175
183
|
function getAffectedFields(updateData) {
|
|
@@ -213,6 +221,7 @@ var BatchUpdater = class {
|
|
|
213
221
|
this.conditions = [];
|
|
214
222
|
this.orderByConditions = [];
|
|
215
223
|
this.limitCount = void 0;
|
|
224
|
+
this.selectedFields = void 0;
|
|
216
225
|
return this;
|
|
217
226
|
}
|
|
218
227
|
/**
|
|
@@ -226,6 +235,7 @@ var BatchUpdater = class {
|
|
|
226
235
|
this.conditions = [];
|
|
227
236
|
this.orderByConditions = [];
|
|
228
237
|
this.limitCount = void 0;
|
|
238
|
+
this.selectedFields = void 0;
|
|
229
239
|
return this;
|
|
230
240
|
}
|
|
231
241
|
/**
|
|
@@ -258,6 +268,15 @@ var BatchUpdater = class {
|
|
|
258
268
|
this.limitCount = count;
|
|
259
269
|
return this;
|
|
260
270
|
}
|
|
271
|
+
/**
|
|
272
|
+
* Select specific fields to retrieve (reduces memory usage and read costs)
|
|
273
|
+
* @param fields - Field paths to retrieve
|
|
274
|
+
* @returns This instance for chaining
|
|
275
|
+
*/
|
|
276
|
+
select(...fields) {
|
|
277
|
+
this.selectedFields = fields;
|
|
278
|
+
return this;
|
|
279
|
+
}
|
|
261
280
|
/**
|
|
262
281
|
* Count documents matching the query conditions
|
|
263
282
|
* @returns Count result with number of matching documents
|
|
@@ -270,6 +289,23 @@ var BatchUpdater = class {
|
|
|
270
289
|
count: snapshot.data().count
|
|
271
290
|
};
|
|
272
291
|
}
|
|
292
|
+
/**
|
|
293
|
+
* Find the first document matching the query conditions
|
|
294
|
+
* @returns First matching document with id and data, or null if not found
|
|
295
|
+
*/
|
|
296
|
+
async findOne() {
|
|
297
|
+
this.validateSetup();
|
|
298
|
+
const query = this.buildQuery().limit(1);
|
|
299
|
+
const snapshot = await query.get();
|
|
300
|
+
if (snapshot.empty) {
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
const doc = snapshot.docs[0];
|
|
304
|
+
return {
|
|
305
|
+
id: doc.id,
|
|
306
|
+
data: doc.data()
|
|
307
|
+
};
|
|
308
|
+
}
|
|
273
309
|
/**
|
|
274
310
|
* Preview changes before executing update
|
|
275
311
|
* @param updateData - Data to update
|
|
@@ -857,6 +893,9 @@ var BatchUpdater = class {
|
|
|
857
893
|
if (this.limitCount !== void 0 && this.limitCount > 0) {
|
|
858
894
|
query = query.limit(this.limitCount);
|
|
859
895
|
}
|
|
896
|
+
if (this.selectedFields && this.selectedFields.length > 0) {
|
|
897
|
+
query = query.select(...this.selectedFields);
|
|
898
|
+
}
|
|
860
899
|
return query;
|
|
861
900
|
}
|
|
862
901
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -119,12 +119,20 @@ function createLogCollector(operation, collection, conditions, updateData) {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
// src/utils/index.ts
|
|
122
|
-
function calculateProgress(current, total) {
|
|
122
|
+
function calculateProgress(current, total, startTime) {
|
|
123
123
|
const percentage = total === 0 ? 0 : Math.round(current / total * 100);
|
|
124
|
+
const now = Date.now();
|
|
125
|
+
const elapsedTime = startTime ? now - startTime : 0;
|
|
126
|
+
const docsPerSecond = elapsedTime > 0 ? Math.round(current / elapsedTime * 1e3 * 100) / 100 : 0;
|
|
127
|
+
const remaining = total - current;
|
|
128
|
+
const eta = docsPerSecond > 0 ? Math.round(remaining / docsPerSecond * 100) / 100 : 0;
|
|
124
129
|
return {
|
|
125
130
|
current,
|
|
126
131
|
total,
|
|
127
|
-
percentage
|
|
132
|
+
percentage,
|
|
133
|
+
elapsedTime,
|
|
134
|
+
docsPerSecond,
|
|
135
|
+
eta
|
|
128
136
|
};
|
|
129
137
|
}
|
|
130
138
|
function getAffectedFields(updateData) {
|
|
@@ -168,6 +176,7 @@ var BatchUpdater = class {
|
|
|
168
176
|
this.conditions = [];
|
|
169
177
|
this.orderByConditions = [];
|
|
170
178
|
this.limitCount = void 0;
|
|
179
|
+
this.selectedFields = void 0;
|
|
171
180
|
return this;
|
|
172
181
|
}
|
|
173
182
|
/**
|
|
@@ -181,6 +190,7 @@ var BatchUpdater = class {
|
|
|
181
190
|
this.conditions = [];
|
|
182
191
|
this.orderByConditions = [];
|
|
183
192
|
this.limitCount = void 0;
|
|
193
|
+
this.selectedFields = void 0;
|
|
184
194
|
return this;
|
|
185
195
|
}
|
|
186
196
|
/**
|
|
@@ -213,6 +223,15 @@ var BatchUpdater = class {
|
|
|
213
223
|
this.limitCount = count;
|
|
214
224
|
return this;
|
|
215
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Select specific fields to retrieve (reduces memory usage and read costs)
|
|
228
|
+
* @param fields - Field paths to retrieve
|
|
229
|
+
* @returns This instance for chaining
|
|
230
|
+
*/
|
|
231
|
+
select(...fields) {
|
|
232
|
+
this.selectedFields = fields;
|
|
233
|
+
return this;
|
|
234
|
+
}
|
|
216
235
|
/**
|
|
217
236
|
* Count documents matching the query conditions
|
|
218
237
|
* @returns Count result with number of matching documents
|
|
@@ -225,6 +244,23 @@ var BatchUpdater = class {
|
|
|
225
244
|
count: snapshot.data().count
|
|
226
245
|
};
|
|
227
246
|
}
|
|
247
|
+
/**
|
|
248
|
+
* Find the first document matching the query conditions
|
|
249
|
+
* @returns First matching document with id and data, or null if not found
|
|
250
|
+
*/
|
|
251
|
+
async findOne() {
|
|
252
|
+
this.validateSetup();
|
|
253
|
+
const query = this.buildQuery().limit(1);
|
|
254
|
+
const snapshot = await query.get();
|
|
255
|
+
if (snapshot.empty) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
const doc = snapshot.docs[0];
|
|
259
|
+
return {
|
|
260
|
+
id: doc.id,
|
|
261
|
+
data: doc.data()
|
|
262
|
+
};
|
|
263
|
+
}
|
|
228
264
|
/**
|
|
229
265
|
* Preview changes before executing update
|
|
230
266
|
* @param updateData - Data to update
|
|
@@ -812,6 +848,9 @@ var BatchUpdater = class {
|
|
|
812
848
|
if (this.limitCount !== void 0 && this.limitCount > 0) {
|
|
813
849
|
query = query.limit(this.limitCount);
|
|
814
850
|
}
|
|
851
|
+
if (this.selectedFields && this.selectedFields.length > 0) {
|
|
852
|
+
query = query.select(...this.selectedFields);
|
|
853
|
+
}
|
|
815
854
|
return query;
|
|
816
855
|
}
|
|
817
856
|
/**
|