firestore-batch-updater 1.4.0 → 1.5.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 +69 -1
- package/README.md +69 -1
- package/dist/index.d.mts +65 -1
- package/dist/index.d.ts +65 -1
- package/dist/index.js +94 -2
- package/dist/index.mjs +92 -0
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -15,9 +15,11 @@
|
|
|
15
15
|
- 일괄 생성/Upsert/삭제 - 여러 문서를 한 번에 생성, upsert 또는 삭제
|
|
16
16
|
- 정렬 및 제한 - `orderBy()`와 `limit()`으로 정밀한 제어
|
|
17
17
|
- 필드 선택 - `select()`로 필요한 필드만 로드 (메모리 및 비용 절약)
|
|
18
|
-
- 단일 문서 작업 - `findOne()`, `updateOne()`, `deleteOne()`으로 효율적인 단일 문서 처리
|
|
18
|
+
- 단일 문서 작업 - `findOne()`, `createOne()`, `updateOne()`, `deleteOne()`으로 효율적인 단일 문서 처리
|
|
19
19
|
- 존재 여부 확인 - `exists()`로 매칭 문서 존재 여부 빠르게 확인
|
|
20
20
|
- 전체 문서 조회 - `getAll()`로 매칭되는 모든 문서 데이터 조회
|
|
21
|
+
- 집계 쿼리 - `aggregate()`로 서버 사이드 `sum`, `average`, `count` 연산
|
|
22
|
+
- 커서 페이지네이션 - `paginate()`로 메모리 효율적인 페이지 단위 조회
|
|
21
23
|
- FieldValue 지원 - `increment()`, `arrayUnion()`, `delete()`, `serverTimestamp()` 등 사용 가능
|
|
22
24
|
- 서브컬렉션 & 컬렉션 그룹 - 서브컬렉션 쿼리 또는 동일 이름의 모든 컬렉션 쿼리
|
|
23
25
|
- Dry Run 모드 - 실제 변경 없이 작업 시뮬레이션
|
|
@@ -96,9 +98,12 @@ console.log(`${result.successCount}개 문서 업데이트 완료`);
|
|
|
96
98
|
| `update(data, options?)` | 매칭되는 문서 업데이트 | `UpdateResult` |
|
|
97
99
|
| `updateOne(data)` | 첫 번째 매칭 문서 업데이트 | `{ success, id }` |
|
|
98
100
|
| `create(docs, options?)` | 새 문서 생성 | `CreateResult` |
|
|
101
|
+
| `createOne(data, id?)` | 단일 문서 생성 | `{ success, id }` |
|
|
99
102
|
| `upsert(data, options?)` | 업데이트 또는 생성 (set with merge) | `UpsertResult` |
|
|
100
103
|
| `delete(options?)` | 매칭되는 문서 삭제 | `DeleteResult` |
|
|
101
104
|
| `deleteOne()` | 첫 번째 매칭 문서 삭제 | `{ success, id }` |
|
|
105
|
+
| `aggregate(spec)` | sum/average/count 집계 쿼리 | `AggregateResult` |
|
|
106
|
+
| `paginate(options)` | 커서 기반 페이지네이션 | `PaginateResult` |
|
|
102
107
|
| `getFields(field)` | 특정 필드 값 조회 | `FieldValueResult[]` |
|
|
103
108
|
|
|
104
109
|
### 옵션
|
|
@@ -146,6 +151,8 @@ console.log(`${result.successCount}개 문서 업데이트 완료`);
|
|
|
146
151
|
| `CreateResult` | `successCount`, `failureCount`, `totalCount`, `createdIds[]`, `failedDocIds?`, `logFilePath?` |
|
|
147
152
|
| `UpsertResult` | `successCount`, `failureCount`, `totalCount`, `failedDocIds?`, `logFilePath?` |
|
|
148
153
|
| `DeleteResult` | `successCount`, `failureCount`, `totalCount`, `deletedIds[]`, `failedDocIds?`, `logFilePath?` |
|
|
154
|
+
| `AggregateResult` | `{ [alias]: number \| null }` |
|
|
155
|
+
| `PaginateResult` | `docs[]`, `nextCursor`, `hasMore` |
|
|
149
156
|
| `FieldValueResult` | `id`, `value` |
|
|
150
157
|
|
|
151
158
|
## 사용 예시
|
|
@@ -443,6 +450,67 @@ if (result.success) {
|
|
|
443
450
|
}
|
|
444
451
|
```
|
|
445
452
|
|
|
453
|
+
### 단일 문서 생성
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
// 자동 생성 ID로 문서 생성
|
|
457
|
+
const result = await updater
|
|
458
|
+
.collection("users")
|
|
459
|
+
.createOne({ name: "Alice", status: "active", score: 100 });
|
|
460
|
+
|
|
461
|
+
console.log(`문서 생성 완료: ${result.id}`);
|
|
462
|
+
|
|
463
|
+
// 커스텀 ID로 문서 생성
|
|
464
|
+
const result2 = await updater
|
|
465
|
+
.collection("users")
|
|
466
|
+
.createOne({ name: "Bob", status: "active" }, "custom-bob-id");
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### 집계 쿼리
|
|
470
|
+
|
|
471
|
+
```typescript
|
|
472
|
+
// 매칭 문서에 대해 sum, average, count 집계
|
|
473
|
+
const stats = await updater
|
|
474
|
+
.collection("orders")
|
|
475
|
+
.where("status", "==", "completed")
|
|
476
|
+
.aggregate({
|
|
477
|
+
totalAmount: { op: "sum", field: "amount" },
|
|
478
|
+
avgAmount: { op: "average", field: "amount" },
|
|
479
|
+
orderCount: { op: "count" },
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
console.log(`총액: ${stats.totalAmount}원`);
|
|
483
|
+
console.log(`평균: ${stats.avgAmount}원`);
|
|
484
|
+
console.log(`주문 수: ${stats.orderCount}건`);
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### 커서 기반 페이지네이션
|
|
488
|
+
|
|
489
|
+
```typescript
|
|
490
|
+
// 페이지 단위로 효율적으로 문서 조회
|
|
491
|
+
let nextCursor = undefined;
|
|
492
|
+
|
|
493
|
+
do {
|
|
494
|
+
const page = await updater
|
|
495
|
+
.collection("users")
|
|
496
|
+
.orderBy("createdAt", "desc")
|
|
497
|
+
.paginate({ pageSize: 20, startAfter: nextCursor });
|
|
498
|
+
|
|
499
|
+
page.docs.forEach((doc) => {
|
|
500
|
+
console.log(`${doc.id}: ${doc.data.name}`);
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
nextCursor = page.nextCursor;
|
|
504
|
+
} while (nextCursor);
|
|
505
|
+
|
|
506
|
+
// select와 함께 사용하여 메모리 효율 극대화
|
|
507
|
+
const page = await updater
|
|
508
|
+
.collection("users")
|
|
509
|
+
.select("name", "email")
|
|
510
|
+
.orderBy("name")
|
|
511
|
+
.paginate({ pageSize: 50 });
|
|
512
|
+
```
|
|
513
|
+
|
|
446
514
|
### Dry Run 모드
|
|
447
515
|
|
|
448
516
|
```typescript
|
package/README.md
CHANGED
|
@@ -15,9 +15,11 @@ English | [한국어](./README.ko.md)
|
|
|
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
17
|
- Field selection - Use `select()` to load only needed fields (saves memory and costs)
|
|
18
|
-
- Single document operations - Use `findOne()`, `updateOne()`, `deleteOne()` for efficient single-doc ops
|
|
18
|
+
- Single document operations - Use `findOne()`, `createOne()`, `updateOne()`, `deleteOne()` for efficient single-doc ops
|
|
19
19
|
- Existence check - Use `exists()` to quickly check if matching documents exist
|
|
20
20
|
- Get all documents - Use `getAll()` to retrieve all matching documents with data
|
|
21
|
+
- Aggregation - Use `aggregate()` for server-side `sum`, `average`, and `count` operations
|
|
22
|
+
- Cursor pagination - Use `paginate()` for memory-efficient page-by-page iteration
|
|
21
23
|
- FieldValue support - Use `increment()`, `arrayUnion()`, `delete()`, `serverTimestamp()`, etc.
|
|
22
24
|
- Subcollection & Collection Group - Query subcollections or all collections with the same name
|
|
23
25
|
- Dry run mode - Simulate operations without making changes
|
|
@@ -96,9 +98,12 @@ console.log(`Updated ${result.successCount} documents`);
|
|
|
96
98
|
| `update(data, options?)` | Update matching documents | `UpdateResult` |
|
|
97
99
|
| `updateOne(data)` | Update first matching document | `{ success, id }` |
|
|
98
100
|
| `create(docs, options?)` | Create new documents | `CreateResult` |
|
|
101
|
+
| `createOne(data, id?)` | Create a single document | `{ success, id }` |
|
|
99
102
|
| `upsert(data, options?)` | Update or create (set with merge) | `UpsertResult` |
|
|
100
103
|
| `delete(options?)` | Delete matching documents | `DeleteResult` |
|
|
101
104
|
| `deleteOne()` | Delete first matching document | `{ success, id }` |
|
|
105
|
+
| `aggregate(spec)` | Run sum/average/count queries | `AggregateResult` |
|
|
106
|
+
| `paginate(options)` | Cursor-based pagination | `PaginateResult` |
|
|
102
107
|
| `getFields(field)` | Get specific field values | `FieldValueResult[]` |
|
|
103
108
|
|
|
104
109
|
### Options
|
|
@@ -146,6 +151,8 @@ All write operations support an optional `options` parameter:
|
|
|
146
151
|
| `CreateResult` | `successCount`, `failureCount`, `totalCount`, `createdIds[]`, `failedDocIds?`, `logFilePath?` |
|
|
147
152
|
| `UpsertResult` | `successCount`, `failureCount`, `totalCount`, `failedDocIds?`, `logFilePath?` |
|
|
148
153
|
| `DeleteResult` | `successCount`, `failureCount`, `totalCount`, `deletedIds[]`, `failedDocIds?`, `logFilePath?` |
|
|
154
|
+
| `AggregateResult` | `{ [alias]: number \| null }` |
|
|
155
|
+
| `PaginateResult` | `docs[]`, `nextCursor`, `hasMore` |
|
|
149
156
|
| `FieldValueResult` | `id`, `value` |
|
|
150
157
|
|
|
151
158
|
## Usage Examples
|
|
@@ -442,6 +449,67 @@ if (result.success) {
|
|
|
442
449
|
}
|
|
443
450
|
```
|
|
444
451
|
|
|
452
|
+
### Create Single Document
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
// Create with auto-generated ID
|
|
456
|
+
const result = await updater
|
|
457
|
+
.collection("users")
|
|
458
|
+
.createOne({ name: "Alice", status: "active", score: 100 });
|
|
459
|
+
|
|
460
|
+
console.log(`Created document: ${result.id}`);
|
|
461
|
+
|
|
462
|
+
// Create with custom ID
|
|
463
|
+
const result2 = await updater
|
|
464
|
+
.collection("users")
|
|
465
|
+
.createOne({ name: "Bob", status: "active" }, "custom-bob-id");
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Aggregate Queries
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
// Sum, average, count on matching documents
|
|
472
|
+
const stats = await updater
|
|
473
|
+
.collection("orders")
|
|
474
|
+
.where("status", "==", "completed")
|
|
475
|
+
.aggregate({
|
|
476
|
+
totalAmount: { op: "sum", field: "amount" },
|
|
477
|
+
avgAmount: { op: "average", field: "amount" },
|
|
478
|
+
orderCount: { op: "count" },
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
console.log(`Total: $${stats.totalAmount}`);
|
|
482
|
+
console.log(`Average: $${stats.avgAmount}`);
|
|
483
|
+
console.log(`Orders: ${stats.orderCount}`);
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### Cursor-Based Pagination
|
|
487
|
+
|
|
488
|
+
```typescript
|
|
489
|
+
// Page through documents efficiently
|
|
490
|
+
let nextCursor = undefined;
|
|
491
|
+
|
|
492
|
+
do {
|
|
493
|
+
const page = await updater
|
|
494
|
+
.collection("users")
|
|
495
|
+
.orderBy("createdAt", "desc")
|
|
496
|
+
.paginate({ pageSize: 20, startAfter: nextCursor });
|
|
497
|
+
|
|
498
|
+
page.docs.forEach((doc) => {
|
|
499
|
+
console.log(`${doc.id}: ${doc.data.name}`);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
nextCursor = page.nextCursor;
|
|
503
|
+
} while (nextCursor);
|
|
504
|
+
|
|
505
|
+
// Works with select for memory efficiency
|
|
506
|
+
const page = await updater
|
|
507
|
+
.collection("users")
|
|
508
|
+
.select("name", "email")
|
|
509
|
+
.orderBy("name")
|
|
510
|
+
.paginate({ pageSize: 50 });
|
|
511
|
+
```
|
|
512
|
+
|
|
445
513
|
### Dry Run Mode
|
|
446
514
|
|
|
447
515
|
```typescript
|
package/dist/index.d.mts
CHANGED
|
@@ -260,6 +260,48 @@ interface OperationLog {
|
|
|
260
260
|
};
|
|
261
261
|
entries: LogEntry[];
|
|
262
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Result of createOne operation
|
|
265
|
+
*/
|
|
266
|
+
interface CreateOneResult {
|
|
267
|
+
success: boolean;
|
|
268
|
+
id: string;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Aggregate operation specification
|
|
272
|
+
* Each key is the alias for the result, value defines the operation
|
|
273
|
+
*/
|
|
274
|
+
interface AggregateSpec {
|
|
275
|
+
[alias: string]: {
|
|
276
|
+
op: "sum" | "average" | "count";
|
|
277
|
+
field?: string;
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Result of aggregate operation
|
|
282
|
+
* Keys match the aliases from AggregateSpec
|
|
283
|
+
*/
|
|
284
|
+
interface AggregateResult {
|
|
285
|
+
[alias: string]: number | null;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Options for paginate operation
|
|
289
|
+
*/
|
|
290
|
+
interface PaginateOptions {
|
|
291
|
+
pageSize: number;
|
|
292
|
+
startAfter?: unknown;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Result of paginate operation
|
|
296
|
+
*/
|
|
297
|
+
interface PaginateResult {
|
|
298
|
+
docs: {
|
|
299
|
+
id: string;
|
|
300
|
+
data: Record<string, any>;
|
|
301
|
+
}[];
|
|
302
|
+
nextCursor: unknown | null;
|
|
303
|
+
hasMore: boolean;
|
|
304
|
+
}
|
|
263
305
|
|
|
264
306
|
/**
|
|
265
307
|
* BatchUpdater - Core class for batch operations on Firestore
|
|
@@ -364,6 +406,28 @@ declare class BatchUpdater {
|
|
|
364
406
|
success: boolean;
|
|
365
407
|
id: string | null;
|
|
366
408
|
}>;
|
|
409
|
+
/**
|
|
410
|
+
* Create a single document in the collection
|
|
411
|
+
* @param data - Document data
|
|
412
|
+
* @param id - Optional document ID (auto-generated if not provided)
|
|
413
|
+
* @returns Result with success status and document id
|
|
414
|
+
*/
|
|
415
|
+
createOne(data: Record<string, any>, id?: string): Promise<{
|
|
416
|
+
success: boolean;
|
|
417
|
+
id: string;
|
|
418
|
+
}>;
|
|
419
|
+
/**
|
|
420
|
+
* Run aggregate queries (sum, average, count) on matching documents
|
|
421
|
+
* @param spec - Aggregate specification defining operations and fields
|
|
422
|
+
* @returns Object with alias keys and numeric results
|
|
423
|
+
*/
|
|
424
|
+
aggregate(spec: AggregateSpec): Promise<AggregateResult>;
|
|
425
|
+
/**
|
|
426
|
+
* Get documents with cursor-based pagination
|
|
427
|
+
* @param options - Pagination options (pageSize, startAfter cursor)
|
|
428
|
+
* @returns Page of documents with cursor for next page
|
|
429
|
+
*/
|
|
430
|
+
paginate(options: PaginateOptions): Promise<PaginateResult>;
|
|
367
431
|
/**
|
|
368
432
|
* Preview changes before executing update
|
|
369
433
|
* @param updateData - Data to update
|
|
@@ -490,4 +554,4 @@ declare function isValidUpdateData(value: any): value is Record<string, any>;
|
|
|
490
554
|
*/
|
|
491
555
|
declare function formatError(error: unknown, context?: string): string;
|
|
492
556
|
|
|
493
|
-
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 };
|
|
557
|
+
export { type AggregateResult, type AggregateSpec, BatchUpdater, type CountResult, type CreateDocumentInput, type CreateOneResult, type CreateOptions, type CreateResult, type DeleteOptions, type DeleteResult, type DocumentSnapshot, type DryRunResult, type FieldValueResult, type LogEntry, type LogOptions, type OperationLog, type OrderByCondition, type PaginateOptions, type PaginateResult, type PreviewResult, type ProgressInfo, type UpdateOptions, type UpdateResult, type UpsertOptions, type UpsertResult, type WhereCondition, calculateProgress, createLogCollector, formatError, formatOperationLog, getAffectedFields, isValidUpdateData, mergeUpdateData, writeOperationLog };
|
package/dist/index.d.ts
CHANGED
|
@@ -260,6 +260,48 @@ interface OperationLog {
|
|
|
260
260
|
};
|
|
261
261
|
entries: LogEntry[];
|
|
262
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Result of createOne operation
|
|
265
|
+
*/
|
|
266
|
+
interface CreateOneResult {
|
|
267
|
+
success: boolean;
|
|
268
|
+
id: string;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Aggregate operation specification
|
|
272
|
+
* Each key is the alias for the result, value defines the operation
|
|
273
|
+
*/
|
|
274
|
+
interface AggregateSpec {
|
|
275
|
+
[alias: string]: {
|
|
276
|
+
op: "sum" | "average" | "count";
|
|
277
|
+
field?: string;
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Result of aggregate operation
|
|
282
|
+
* Keys match the aliases from AggregateSpec
|
|
283
|
+
*/
|
|
284
|
+
interface AggregateResult {
|
|
285
|
+
[alias: string]: number | null;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Options for paginate operation
|
|
289
|
+
*/
|
|
290
|
+
interface PaginateOptions {
|
|
291
|
+
pageSize: number;
|
|
292
|
+
startAfter?: unknown;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Result of paginate operation
|
|
296
|
+
*/
|
|
297
|
+
interface PaginateResult {
|
|
298
|
+
docs: {
|
|
299
|
+
id: string;
|
|
300
|
+
data: Record<string, any>;
|
|
301
|
+
}[];
|
|
302
|
+
nextCursor: unknown | null;
|
|
303
|
+
hasMore: boolean;
|
|
304
|
+
}
|
|
263
305
|
|
|
264
306
|
/**
|
|
265
307
|
* BatchUpdater - Core class for batch operations on Firestore
|
|
@@ -364,6 +406,28 @@ declare class BatchUpdater {
|
|
|
364
406
|
success: boolean;
|
|
365
407
|
id: string | null;
|
|
366
408
|
}>;
|
|
409
|
+
/**
|
|
410
|
+
* Create a single document in the collection
|
|
411
|
+
* @param data - Document data
|
|
412
|
+
* @param id - Optional document ID (auto-generated if not provided)
|
|
413
|
+
* @returns Result with success status and document id
|
|
414
|
+
*/
|
|
415
|
+
createOne(data: Record<string, any>, id?: string): Promise<{
|
|
416
|
+
success: boolean;
|
|
417
|
+
id: string;
|
|
418
|
+
}>;
|
|
419
|
+
/**
|
|
420
|
+
* Run aggregate queries (sum, average, count) on matching documents
|
|
421
|
+
* @param spec - Aggregate specification defining operations and fields
|
|
422
|
+
* @returns Object with alias keys and numeric results
|
|
423
|
+
*/
|
|
424
|
+
aggregate(spec: AggregateSpec): Promise<AggregateResult>;
|
|
425
|
+
/**
|
|
426
|
+
* Get documents with cursor-based pagination
|
|
427
|
+
* @param options - Pagination options (pageSize, startAfter cursor)
|
|
428
|
+
* @returns Page of documents with cursor for next page
|
|
429
|
+
*/
|
|
430
|
+
paginate(options: PaginateOptions): Promise<PaginateResult>;
|
|
367
431
|
/**
|
|
368
432
|
* Preview changes before executing update
|
|
369
433
|
* @param updateData - Data to update
|
|
@@ -490,4 +554,4 @@ declare function isValidUpdateData(value: any): value is Record<string, any>;
|
|
|
490
554
|
*/
|
|
491
555
|
declare function formatError(error: unknown, context?: string): string;
|
|
492
556
|
|
|
493
|
-
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 };
|
|
557
|
+
export { type AggregateResult, type AggregateSpec, BatchUpdater, type CountResult, type CreateDocumentInput, type CreateOneResult, type CreateOptions, type CreateResult, type DeleteOptions, type DeleteResult, type DocumentSnapshot, type DryRunResult, type FieldValueResult, type LogEntry, type LogOptions, type OperationLog, type OrderByCondition, type PaginateOptions, type PaginateResult, type PreviewResult, type ProgressInfo, type UpdateOptions, type UpdateResult, type UpsertOptions, type UpsertResult, type WhereCondition, calculateProgress, createLogCollector, formatError, formatOperationLog, getAffectedFields, isValidUpdateData, mergeUpdateData, writeOperationLog };
|
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
BatchUpdater: () => BatchUpdater,
|
|
34
|
-
FieldValue: () =>
|
|
34
|
+
FieldValue: () => import_firestore2.FieldValue,
|
|
35
35
|
calculateProgress: () => calculateProgress,
|
|
36
36
|
createLogCollector: () => createLogCollector,
|
|
37
37
|
formatError: () => formatError,
|
|
@@ -43,6 +43,9 @@ __export(index_exports, {
|
|
|
43
43
|
});
|
|
44
44
|
module.exports = __toCommonJS(index_exports);
|
|
45
45
|
|
|
46
|
+
// src/core/batch-updater.ts
|
|
47
|
+
var import_firestore = require("firebase-admin/firestore");
|
|
48
|
+
|
|
46
49
|
// src/utils/logger.ts
|
|
47
50
|
var fs = __toESM(require("fs"));
|
|
48
51
|
var path = __toESM(require("path"));
|
|
@@ -363,6 +366,95 @@ var BatchUpdater = class {
|
|
|
363
366
|
await doc.ref.delete();
|
|
364
367
|
return { success: true, id: doc.id };
|
|
365
368
|
}
|
|
369
|
+
/**
|
|
370
|
+
* Create a single document in the collection
|
|
371
|
+
* @param data - Document data
|
|
372
|
+
* @param id - Optional document ID (auto-generated if not provided)
|
|
373
|
+
* @returns Result with success status and document id
|
|
374
|
+
*/
|
|
375
|
+
async createOne(data, id) {
|
|
376
|
+
this.validateSetup();
|
|
377
|
+
if (this.isCollectionGroup) {
|
|
378
|
+
throw new Error(
|
|
379
|
+
"createOne() cannot be used with collectionGroup(). Use collection() with a specific path instead."
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
if (!isValidUpdateData(data)) {
|
|
383
|
+
throw new Error("Document data must be a non-empty object");
|
|
384
|
+
}
|
|
385
|
+
const collection = this.firestore.collection(this.collectionPath);
|
|
386
|
+
const docRef = id ? collection.doc(id) : collection.doc();
|
|
387
|
+
await docRef.set(data);
|
|
388
|
+
return { success: true, id: docRef.id };
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Run aggregate queries (sum, average, count) on matching documents
|
|
392
|
+
* @param spec - Aggregate specification defining operations and fields
|
|
393
|
+
* @returns Object with alias keys and numeric results
|
|
394
|
+
*/
|
|
395
|
+
async aggregate(spec) {
|
|
396
|
+
this.validateSetup();
|
|
397
|
+
if (!spec || Object.keys(spec).length === 0) {
|
|
398
|
+
throw new Error("Aggregate spec must be a non-empty object");
|
|
399
|
+
}
|
|
400
|
+
const query = this.buildQuery();
|
|
401
|
+
const aggregateFields = {};
|
|
402
|
+
for (const [alias, definition] of Object.entries(spec)) {
|
|
403
|
+
switch (definition.op) {
|
|
404
|
+
case "sum":
|
|
405
|
+
if (!definition.field) {
|
|
406
|
+
throw new Error(`Field is required for sum operation (alias: ${alias})`);
|
|
407
|
+
}
|
|
408
|
+
aggregateFields[alias] = import_firestore.AggregateField.sum(definition.field);
|
|
409
|
+
break;
|
|
410
|
+
case "average":
|
|
411
|
+
if (!definition.field) {
|
|
412
|
+
throw new Error(`Field is required for average operation (alias: ${alias})`);
|
|
413
|
+
}
|
|
414
|
+
aggregateFields[alias] = import_firestore.AggregateField.average(definition.field);
|
|
415
|
+
break;
|
|
416
|
+
case "count":
|
|
417
|
+
aggregateFields[alias] = import_firestore.AggregateField.count();
|
|
418
|
+
break;
|
|
419
|
+
default:
|
|
420
|
+
throw new Error(`Unknown aggregate operation: ${definition.op}`);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const snapshot = await query.aggregate(aggregateFields).get();
|
|
424
|
+
const data = snapshot.data();
|
|
425
|
+
const result = {};
|
|
426
|
+
for (const alias of Object.keys(spec)) {
|
|
427
|
+
result[alias] = data[alias] ?? null;
|
|
428
|
+
}
|
|
429
|
+
return result;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Get documents with cursor-based pagination
|
|
433
|
+
* @param options - Pagination options (pageSize, startAfter cursor)
|
|
434
|
+
* @returns Page of documents with cursor for next page
|
|
435
|
+
*/
|
|
436
|
+
async paginate(options) {
|
|
437
|
+
this.validateSetup();
|
|
438
|
+
if (!options.pageSize || options.pageSize <= 0) {
|
|
439
|
+
throw new Error("pageSize must be a positive number");
|
|
440
|
+
}
|
|
441
|
+
let query = this.buildQuery().limit(options.pageSize + 1);
|
|
442
|
+
if (options.startAfter) {
|
|
443
|
+
query = query.startAfter(options.startAfter);
|
|
444
|
+
}
|
|
445
|
+
const snapshot = await query.get();
|
|
446
|
+
const hasMore = snapshot.docs.length > options.pageSize;
|
|
447
|
+
const docs = snapshot.docs.slice(0, options.pageSize).map((doc) => ({
|
|
448
|
+
id: doc.id,
|
|
449
|
+
data: doc.data()
|
|
450
|
+
}));
|
|
451
|
+
const lastDoc = snapshot.docs.length > 0 ? snapshot.docs[Math.min(snapshot.docs.length - 1, options.pageSize - 1)] : null;
|
|
452
|
+
return {
|
|
453
|
+
docs,
|
|
454
|
+
nextCursor: hasMore ? lastDoc : null,
|
|
455
|
+
hasMore
|
|
456
|
+
};
|
|
457
|
+
}
|
|
366
458
|
/**
|
|
367
459
|
* Preview changes before executing update
|
|
368
460
|
* @param updateData - Data to update
|
|
@@ -973,7 +1065,7 @@ var BatchUpdater = class {
|
|
|
973
1065
|
};
|
|
974
1066
|
|
|
975
1067
|
// src/index.ts
|
|
976
|
-
var
|
|
1068
|
+
var import_firestore2 = require("firebase-admin/firestore");
|
|
977
1069
|
// Annotate the CommonJS export names for ESM import in node:
|
|
978
1070
|
0 && (module.exports = {
|
|
979
1071
|
BatchUpdater,
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// src/core/batch-updater.ts
|
|
2
|
+
import { AggregateField } from "firebase-admin/firestore";
|
|
3
|
+
|
|
1
4
|
// src/utils/logger.ts
|
|
2
5
|
import * as fs from "fs";
|
|
3
6
|
import * as path from "path";
|
|
@@ -318,6 +321,95 @@ var BatchUpdater = class {
|
|
|
318
321
|
await doc.ref.delete();
|
|
319
322
|
return { success: true, id: doc.id };
|
|
320
323
|
}
|
|
324
|
+
/**
|
|
325
|
+
* Create a single document in the collection
|
|
326
|
+
* @param data - Document data
|
|
327
|
+
* @param id - Optional document ID (auto-generated if not provided)
|
|
328
|
+
* @returns Result with success status and document id
|
|
329
|
+
*/
|
|
330
|
+
async createOne(data, id) {
|
|
331
|
+
this.validateSetup();
|
|
332
|
+
if (this.isCollectionGroup) {
|
|
333
|
+
throw new Error(
|
|
334
|
+
"createOne() cannot be used with collectionGroup(). Use collection() with a specific path instead."
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
if (!isValidUpdateData(data)) {
|
|
338
|
+
throw new Error("Document data must be a non-empty object");
|
|
339
|
+
}
|
|
340
|
+
const collection = this.firestore.collection(this.collectionPath);
|
|
341
|
+
const docRef = id ? collection.doc(id) : collection.doc();
|
|
342
|
+
await docRef.set(data);
|
|
343
|
+
return { success: true, id: docRef.id };
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Run aggregate queries (sum, average, count) on matching documents
|
|
347
|
+
* @param spec - Aggregate specification defining operations and fields
|
|
348
|
+
* @returns Object with alias keys and numeric results
|
|
349
|
+
*/
|
|
350
|
+
async aggregate(spec) {
|
|
351
|
+
this.validateSetup();
|
|
352
|
+
if (!spec || Object.keys(spec).length === 0) {
|
|
353
|
+
throw new Error("Aggregate spec must be a non-empty object");
|
|
354
|
+
}
|
|
355
|
+
const query = this.buildQuery();
|
|
356
|
+
const aggregateFields = {};
|
|
357
|
+
for (const [alias, definition] of Object.entries(spec)) {
|
|
358
|
+
switch (definition.op) {
|
|
359
|
+
case "sum":
|
|
360
|
+
if (!definition.field) {
|
|
361
|
+
throw new Error(`Field is required for sum operation (alias: ${alias})`);
|
|
362
|
+
}
|
|
363
|
+
aggregateFields[alias] = AggregateField.sum(definition.field);
|
|
364
|
+
break;
|
|
365
|
+
case "average":
|
|
366
|
+
if (!definition.field) {
|
|
367
|
+
throw new Error(`Field is required for average operation (alias: ${alias})`);
|
|
368
|
+
}
|
|
369
|
+
aggregateFields[alias] = AggregateField.average(definition.field);
|
|
370
|
+
break;
|
|
371
|
+
case "count":
|
|
372
|
+
aggregateFields[alias] = AggregateField.count();
|
|
373
|
+
break;
|
|
374
|
+
default:
|
|
375
|
+
throw new Error(`Unknown aggregate operation: ${definition.op}`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const snapshot = await query.aggregate(aggregateFields).get();
|
|
379
|
+
const data = snapshot.data();
|
|
380
|
+
const result = {};
|
|
381
|
+
for (const alias of Object.keys(spec)) {
|
|
382
|
+
result[alias] = data[alias] ?? null;
|
|
383
|
+
}
|
|
384
|
+
return result;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Get documents with cursor-based pagination
|
|
388
|
+
* @param options - Pagination options (pageSize, startAfter cursor)
|
|
389
|
+
* @returns Page of documents with cursor for next page
|
|
390
|
+
*/
|
|
391
|
+
async paginate(options) {
|
|
392
|
+
this.validateSetup();
|
|
393
|
+
if (!options.pageSize || options.pageSize <= 0) {
|
|
394
|
+
throw new Error("pageSize must be a positive number");
|
|
395
|
+
}
|
|
396
|
+
let query = this.buildQuery().limit(options.pageSize + 1);
|
|
397
|
+
if (options.startAfter) {
|
|
398
|
+
query = query.startAfter(options.startAfter);
|
|
399
|
+
}
|
|
400
|
+
const snapshot = await query.get();
|
|
401
|
+
const hasMore = snapshot.docs.length > options.pageSize;
|
|
402
|
+
const docs = snapshot.docs.slice(0, options.pageSize).map((doc) => ({
|
|
403
|
+
id: doc.id,
|
|
404
|
+
data: doc.data()
|
|
405
|
+
}));
|
|
406
|
+
const lastDoc = snapshot.docs.length > 0 ? snapshot.docs[Math.min(snapshot.docs.length - 1, options.pageSize - 1)] : null;
|
|
407
|
+
return {
|
|
408
|
+
docs,
|
|
409
|
+
nextCursor: hasMore ? lastDoc : null,
|
|
410
|
+
hasMore
|
|
411
|
+
};
|
|
412
|
+
}
|
|
321
413
|
/**
|
|
322
414
|
* Preview changes before executing update
|
|
323
415
|
* @param updateData - Data to update
|