firestore-batch-updater 1.19.0 → 1.21.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 +49 -0
- package/README.md +49 -0
- package/dist/index.d.mts +28 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +74 -0
- package/dist/index.mjs +74 -0
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
- 커서 페이지네이션 - `paginate()`로 메모리 효율적인 페이지 단위 조회
|
|
26
26
|
- ID 직접 조회 - `getOne()`으로 문서 ID로 빠른 조회
|
|
27
27
|
- 문서 ID 존재 확인 - `has(id)`로 데이터 읽기 없이 특정 문서 ID 존재 여부 확인
|
|
28
|
+
- 다중 ID 조회 - `pick(ids)`로 여러 문서 ID를 한 번에 효율적으로 조회
|
|
29
|
+
- 처음 & 마지막 문서 - `first()` / `last()`로 정렬 기준 첫 번째/마지막 문서 조회
|
|
28
30
|
- 벌크 작업 - `bulkCreate()`, `bulkUpdate()`, `bulkDelete()`로 여러 문서에 각기 다른 데이터로 효율적 처리
|
|
29
31
|
- 문서 변환 - `transform()`으로 각 문서에 커스텀 로직 적용 (가격 인상, 데이터 마이그레이션 등)
|
|
30
32
|
- 복사 & 이동 - `copyTo()`로 컬렉션 간 문서 복사/이동 (데이터 변환 옵션 포함)
|
|
@@ -111,6 +113,9 @@ console.log(`${result.successCount}개 문서 업데이트 완료`);
|
|
|
111
113
|
| `findOne()` | 첫 번째 매칭 문서 조회 | `{ id, data } \| null` |
|
|
112
114
|
| `getOne(id)` | ID로 문서 직접 조회 | `{ id, data } \| null` |
|
|
113
115
|
| `has(id)` | 문서 ID 존재 여부 확인 | `boolean` |
|
|
116
|
+
| `pick(ids)` | 여러 문서 ID로 한 번에 조회 | `{ id, data }[]` |
|
|
117
|
+
| `first()` | 정렬 기준 첫 번째 문서 조회 | `{ id, data } \| null` |
|
|
118
|
+
| `last()` | 정렬 기준 마지막 문서 조회 | `{ id, data } \| null` |
|
|
114
119
|
| `getAll()` | 모든 매칭 문서 조회 | `{ id, data }[]` |
|
|
115
120
|
| `preview(data)` | 업데이트 전 미리보기 | `PreviewResult` |
|
|
116
121
|
| `update(data, options?)` | 매칭되는 문서 업데이트 | `UpdateResult` |
|
|
@@ -666,6 +671,50 @@ if (!(await updater.collection("users").has(userId))) {
|
|
|
666
671
|
}
|
|
667
672
|
```
|
|
668
673
|
|
|
674
|
+
### 여러 문서 ID로 조회
|
|
675
|
+
|
|
676
|
+
```typescript
|
|
677
|
+
// 여러 문서를 한 번에 조회 (getOne() 여러 번 호출보다 효율적)
|
|
678
|
+
const users = await updater
|
|
679
|
+
.collection("users")
|
|
680
|
+
.pick(["user-1", "user-2", "user-3"]);
|
|
681
|
+
|
|
682
|
+
console.log(`${users.length}명 조회됨`);
|
|
683
|
+
users.forEach((u) => console.log(`${u.id}: ${u.data.name}`));
|
|
684
|
+
|
|
685
|
+
// 존재하지 않는 ID는 자동으로 건너뜀
|
|
686
|
+
const docs = await updater
|
|
687
|
+
.collection("products")
|
|
688
|
+
.pick(["prod-1", "non-existent", "prod-3"]);
|
|
689
|
+
// prod-1, prod-3만 반환 (존재하는 경우)
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
### 처음 & 마지막 문서 조회
|
|
693
|
+
|
|
694
|
+
```typescript
|
|
695
|
+
// 정렬 기준 첫 번째 문서 조회
|
|
696
|
+
const youngest = await updater
|
|
697
|
+
.collection("users")
|
|
698
|
+
.orderBy("age", "asc")
|
|
699
|
+
.first();
|
|
700
|
+
console.log(`최연소: ${youngest?.data.name}`);
|
|
701
|
+
|
|
702
|
+
// 정렬 기준 마지막 문서 조회
|
|
703
|
+
const oldest = await updater
|
|
704
|
+
.collection("users")
|
|
705
|
+
.orderBy("age", "asc")
|
|
706
|
+
.last();
|
|
707
|
+
console.log(`최고령: ${oldest?.data.name}`);
|
|
708
|
+
|
|
709
|
+
// where, select와 함께 사용
|
|
710
|
+
const cheapest = await updater
|
|
711
|
+
.collection("products")
|
|
712
|
+
.where("price", ">=", 10)
|
|
713
|
+
.select("name", "price")
|
|
714
|
+
.orderBy("price", "asc")
|
|
715
|
+
.first();
|
|
716
|
+
```
|
|
717
|
+
|
|
669
718
|
### 벌크 업데이트
|
|
670
719
|
|
|
671
720
|
```typescript
|
package/README.md
CHANGED
|
@@ -25,6 +25,8 @@ English | [한국어](./README.ko.md)
|
|
|
25
25
|
- Cursor pagination - Use `paginate()` for memory-efficient page-by-page iteration
|
|
26
26
|
- Direct ID lookup - Use `getOne()` for fast document retrieval by ID
|
|
27
27
|
- Document ID check - Use `has(id)` to check if a specific document ID exists without reading data
|
|
28
|
+
- Multi-ID lookup - Use `pick(ids)` to get multiple documents by IDs in a single efficient call
|
|
29
|
+
- First & Last - Use `first()` / `last()` to get the first or last document by orderBy
|
|
28
30
|
- Bulk operations - Use `bulkCreate()`, `bulkUpdate()`, `bulkDelete()` for efficient multi-document operations with different data each
|
|
29
31
|
- Transform - Use `transform()` to apply custom logic to each document (e.g., price increase, data migration)
|
|
30
32
|
- Copy & Move - Use `copyTo()` to copy/move documents between collections with optional data transformation
|
|
@@ -111,6 +113,9 @@ console.log(`Updated ${result.successCount} documents`);
|
|
|
111
113
|
| `findOne()` | Find first matching document | `{ id, data } \| null` |
|
|
112
114
|
| `getOne(id)` | Get document by ID directly | `{ id, data } \| null` |
|
|
113
115
|
| `has(id)` | Check if document ID exists | `boolean` |
|
|
116
|
+
| `pick(ids)` | Get multiple documents by IDs | `{ id, data }[]` |
|
|
117
|
+
| `first()` | Get first document by orderBy | `{ id, data } \| null` |
|
|
118
|
+
| `last()` | Get last document by orderBy | `{ id, data } \| null` |
|
|
114
119
|
| `getAll()` | Get all matching documents | `{ id, data }[]` |
|
|
115
120
|
| `preview(data)` | Preview changes before update | `PreviewResult` |
|
|
116
121
|
| `update(data, options?)` | Update matching documents | `UpdateResult` |
|
|
@@ -685,6 +690,50 @@ if (!(await updater.collection("users").has(userId))) {
|
|
|
685
690
|
}
|
|
686
691
|
```
|
|
687
692
|
|
|
693
|
+
### Get Multiple Documents by IDs
|
|
694
|
+
|
|
695
|
+
```typescript
|
|
696
|
+
// Get multiple documents in a single call (more efficient than multiple getOne)
|
|
697
|
+
const users = await updater
|
|
698
|
+
.collection("users")
|
|
699
|
+
.pick(["user-1", "user-2", "user-3"]);
|
|
700
|
+
|
|
701
|
+
console.log(`Found ${users.length} users`);
|
|
702
|
+
users.forEach((u) => console.log(`${u.id}: ${u.data.name}`));
|
|
703
|
+
|
|
704
|
+
// Non-existent IDs are silently skipped
|
|
705
|
+
const docs = await updater
|
|
706
|
+
.collection("products")
|
|
707
|
+
.pick(["prod-1", "non-existent", "prod-3"]);
|
|
708
|
+
// Returns only prod-1 and prod-3 (if they exist)
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
### First & Last Document
|
|
712
|
+
|
|
713
|
+
```typescript
|
|
714
|
+
// Get the first document (by orderBy)
|
|
715
|
+
const youngest = await updater
|
|
716
|
+
.collection("users")
|
|
717
|
+
.orderBy("age", "asc")
|
|
718
|
+
.first();
|
|
719
|
+
console.log(`Youngest: ${youngest?.data.name}`);
|
|
720
|
+
|
|
721
|
+
// Get the last document (by orderBy)
|
|
722
|
+
const oldest = await updater
|
|
723
|
+
.collection("users")
|
|
724
|
+
.orderBy("age", "asc")
|
|
725
|
+
.last();
|
|
726
|
+
console.log(`Oldest: ${oldest?.data.name}`);
|
|
727
|
+
|
|
728
|
+
// Works with where and select
|
|
729
|
+
const cheapest = await updater
|
|
730
|
+
.collection("products")
|
|
731
|
+
.where("price", ">=", 10)
|
|
732
|
+
.select("name", "price")
|
|
733
|
+
.orderBy("price", "asc")
|
|
734
|
+
.first();
|
|
735
|
+
```
|
|
736
|
+
|
|
688
737
|
### Bulk Update with Different Data
|
|
689
738
|
|
|
690
739
|
```typescript
|
package/dist/index.d.mts
CHANGED
|
@@ -635,6 +635,34 @@ declare class BatchUpdater {
|
|
|
635
635
|
* @returns true if the document exists, false otherwise
|
|
636
636
|
*/
|
|
637
637
|
has(id: string): Promise<boolean>;
|
|
638
|
+
/**
|
|
639
|
+
* Get multiple documents by their IDs in a single call
|
|
640
|
+
* @param ids - Array of document IDs to retrieve
|
|
641
|
+
* @returns Array of documents with id and data (skips non-existent documents)
|
|
642
|
+
*/
|
|
643
|
+
pick(ids: string[]): Promise<{
|
|
644
|
+
id: string;
|
|
645
|
+
data: Record<string, any>;
|
|
646
|
+
}[]>;
|
|
647
|
+
/**
|
|
648
|
+
* Get the first document based on the current orderBy conditions
|
|
649
|
+
* Requires at least one orderBy() to be set
|
|
650
|
+
* @returns First document with id and data, or null if no documents match
|
|
651
|
+
*/
|
|
652
|
+
first(): Promise<{
|
|
653
|
+
id: string;
|
|
654
|
+
data: Record<string, any>;
|
|
655
|
+
} | null>;
|
|
656
|
+
/**
|
|
657
|
+
* Get the last document based on the current orderBy conditions
|
|
658
|
+
* Requires at least one orderBy() to be set
|
|
659
|
+
* Reverses the orderBy direction internally to fetch the last document efficiently
|
|
660
|
+
* @returns Last document with id and data, or null if no documents match
|
|
661
|
+
*/
|
|
662
|
+
last(): Promise<{
|
|
663
|
+
id: string;
|
|
664
|
+
data: Record<string, any>;
|
|
665
|
+
} | null>;
|
|
638
666
|
/**
|
|
639
667
|
* Update the first document matching the query conditions
|
|
640
668
|
* @param updateData - Data to update
|
package/dist/index.d.ts
CHANGED
|
@@ -635,6 +635,34 @@ declare class BatchUpdater {
|
|
|
635
635
|
* @returns true if the document exists, false otherwise
|
|
636
636
|
*/
|
|
637
637
|
has(id: string): Promise<boolean>;
|
|
638
|
+
/**
|
|
639
|
+
* Get multiple documents by their IDs in a single call
|
|
640
|
+
* @param ids - Array of document IDs to retrieve
|
|
641
|
+
* @returns Array of documents with id and data (skips non-existent documents)
|
|
642
|
+
*/
|
|
643
|
+
pick(ids: string[]): Promise<{
|
|
644
|
+
id: string;
|
|
645
|
+
data: Record<string, any>;
|
|
646
|
+
}[]>;
|
|
647
|
+
/**
|
|
648
|
+
* Get the first document based on the current orderBy conditions
|
|
649
|
+
* Requires at least one orderBy() to be set
|
|
650
|
+
* @returns First document with id and data, or null if no documents match
|
|
651
|
+
*/
|
|
652
|
+
first(): Promise<{
|
|
653
|
+
id: string;
|
|
654
|
+
data: Record<string, any>;
|
|
655
|
+
} | null>;
|
|
656
|
+
/**
|
|
657
|
+
* Get the last document based on the current orderBy conditions
|
|
658
|
+
* Requires at least one orderBy() to be set
|
|
659
|
+
* Reverses the orderBy direction internally to fetch the last document efficiently
|
|
660
|
+
* @returns Last document with id and data, or null if no documents match
|
|
661
|
+
*/
|
|
662
|
+
last(): Promise<{
|
|
663
|
+
id: string;
|
|
664
|
+
data: Record<string, any>;
|
|
665
|
+
} | null>;
|
|
638
666
|
/**
|
|
639
667
|
* Update the first document matching the query conditions
|
|
640
668
|
* @param updateData - Data to update
|
package/dist/index.js
CHANGED
|
@@ -392,6 +392,80 @@ var BatchUpdater = class {
|
|
|
392
392
|
const docSnapshot = await docRef.get();
|
|
393
393
|
return docSnapshot.exists;
|
|
394
394
|
}
|
|
395
|
+
/**
|
|
396
|
+
* Get multiple documents by their IDs in a single call
|
|
397
|
+
* @param ids - Array of document IDs to retrieve
|
|
398
|
+
* @returns Array of documents with id and data (skips non-existent documents)
|
|
399
|
+
*/
|
|
400
|
+
async pick(ids) {
|
|
401
|
+
this.validateSetup();
|
|
402
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
403
|
+
throw new Error("Document IDs array is required and must not be empty");
|
|
404
|
+
}
|
|
405
|
+
if (this.isCollectionGroup) {
|
|
406
|
+
throw new Error(
|
|
407
|
+
"pick() cannot be used with collectionGroup(). Use getAll() with where conditions instead."
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
const docRefs = ids.map(
|
|
411
|
+
(id) => this.firestore.collection(this.collectionPath).doc(id)
|
|
412
|
+
);
|
|
413
|
+
const snapshots = await this.firestore.getAll(...docRefs);
|
|
414
|
+
return snapshots.filter((snap) => snap.exists).map((snap) => ({
|
|
415
|
+
id: snap.id,
|
|
416
|
+
data: snap.data()
|
|
417
|
+
}));
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Get the first document based on the current orderBy conditions
|
|
421
|
+
* Requires at least one orderBy() to be set
|
|
422
|
+
* @returns First document with id and data, or null if no documents match
|
|
423
|
+
*/
|
|
424
|
+
async first() {
|
|
425
|
+
this.validateSetup();
|
|
426
|
+
if (this.orderByConditions.length === 0) {
|
|
427
|
+
throw new Error("first() requires at least one orderBy() condition");
|
|
428
|
+
}
|
|
429
|
+
const query = this.buildQuery().limit(1);
|
|
430
|
+
const snapshot = await query.get();
|
|
431
|
+
if (snapshot.empty) {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
const doc = snapshot.docs[0];
|
|
435
|
+
return { id: doc.id, data: doc.data() };
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Get the last document based on the current orderBy conditions
|
|
439
|
+
* Requires at least one orderBy() to be set
|
|
440
|
+
* Reverses the orderBy direction internally to fetch the last document efficiently
|
|
441
|
+
* @returns Last document with id and data, or null if no documents match
|
|
442
|
+
*/
|
|
443
|
+
async last() {
|
|
444
|
+
this.validateSetup();
|
|
445
|
+
if (this.orderByConditions.length === 0) {
|
|
446
|
+
throw new Error("last() requires at least one orderBy() condition");
|
|
447
|
+
}
|
|
448
|
+
let query = this.isCollectionGroup ? this.firestore.collectionGroup(this.collectionPath) : this.firestore.collection(this.collectionPath);
|
|
449
|
+
for (const condition of this.conditions) {
|
|
450
|
+
query = query.where(condition.field, condition.operator, condition.value);
|
|
451
|
+
}
|
|
452
|
+
for (const orderBy of this.orderByConditions) {
|
|
453
|
+
query = query.orderBy(
|
|
454
|
+
orderBy.field,
|
|
455
|
+
orderBy.direction === "asc" ? "desc" : "asc"
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
if (this.selectedFields && this.selectedFields.length > 0) {
|
|
459
|
+
query = query.select(...this.selectedFields);
|
|
460
|
+
}
|
|
461
|
+
query = query.limit(1);
|
|
462
|
+
const snapshot = await query.get();
|
|
463
|
+
if (snapshot.empty) {
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
const doc = snapshot.docs[0];
|
|
467
|
+
return { id: doc.id, data: doc.data() };
|
|
468
|
+
}
|
|
395
469
|
/**
|
|
396
470
|
* Update the first document matching the query conditions
|
|
397
471
|
* @param updateData - Data to update
|
package/dist/index.mjs
CHANGED
|
@@ -347,6 +347,80 @@ var BatchUpdater = class {
|
|
|
347
347
|
const docSnapshot = await docRef.get();
|
|
348
348
|
return docSnapshot.exists;
|
|
349
349
|
}
|
|
350
|
+
/**
|
|
351
|
+
* Get multiple documents by their IDs in a single call
|
|
352
|
+
* @param ids - Array of document IDs to retrieve
|
|
353
|
+
* @returns Array of documents with id and data (skips non-existent documents)
|
|
354
|
+
*/
|
|
355
|
+
async pick(ids) {
|
|
356
|
+
this.validateSetup();
|
|
357
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
358
|
+
throw new Error("Document IDs array is required and must not be empty");
|
|
359
|
+
}
|
|
360
|
+
if (this.isCollectionGroup) {
|
|
361
|
+
throw new Error(
|
|
362
|
+
"pick() cannot be used with collectionGroup(). Use getAll() with where conditions instead."
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
const docRefs = ids.map(
|
|
366
|
+
(id) => this.firestore.collection(this.collectionPath).doc(id)
|
|
367
|
+
);
|
|
368
|
+
const snapshots = await this.firestore.getAll(...docRefs);
|
|
369
|
+
return snapshots.filter((snap) => snap.exists).map((snap) => ({
|
|
370
|
+
id: snap.id,
|
|
371
|
+
data: snap.data()
|
|
372
|
+
}));
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Get the first document based on the current orderBy conditions
|
|
376
|
+
* Requires at least one orderBy() to be set
|
|
377
|
+
* @returns First document with id and data, or null if no documents match
|
|
378
|
+
*/
|
|
379
|
+
async first() {
|
|
380
|
+
this.validateSetup();
|
|
381
|
+
if (this.orderByConditions.length === 0) {
|
|
382
|
+
throw new Error("first() requires at least one orderBy() condition");
|
|
383
|
+
}
|
|
384
|
+
const query = this.buildQuery().limit(1);
|
|
385
|
+
const snapshot = await query.get();
|
|
386
|
+
if (snapshot.empty) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
const doc = snapshot.docs[0];
|
|
390
|
+
return { id: doc.id, data: doc.data() };
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Get the last document based on the current orderBy conditions
|
|
394
|
+
* Requires at least one orderBy() to be set
|
|
395
|
+
* Reverses the orderBy direction internally to fetch the last document efficiently
|
|
396
|
+
* @returns Last document with id and data, or null if no documents match
|
|
397
|
+
*/
|
|
398
|
+
async last() {
|
|
399
|
+
this.validateSetup();
|
|
400
|
+
if (this.orderByConditions.length === 0) {
|
|
401
|
+
throw new Error("last() requires at least one orderBy() condition");
|
|
402
|
+
}
|
|
403
|
+
let query = this.isCollectionGroup ? this.firestore.collectionGroup(this.collectionPath) : this.firestore.collection(this.collectionPath);
|
|
404
|
+
for (const condition of this.conditions) {
|
|
405
|
+
query = query.where(condition.field, condition.operator, condition.value);
|
|
406
|
+
}
|
|
407
|
+
for (const orderBy of this.orderByConditions) {
|
|
408
|
+
query = query.orderBy(
|
|
409
|
+
orderBy.field,
|
|
410
|
+
orderBy.direction === "asc" ? "desc" : "asc"
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
if (this.selectedFields && this.selectedFields.length > 0) {
|
|
414
|
+
query = query.select(...this.selectedFields);
|
|
415
|
+
}
|
|
416
|
+
query = query.limit(1);
|
|
417
|
+
const snapshot = await query.get();
|
|
418
|
+
if (snapshot.empty) {
|
|
419
|
+
return null;
|
|
420
|
+
}
|
|
421
|
+
const doc = snapshot.docs[0];
|
|
422
|
+
return { id: doc.id, data: doc.data() };
|
|
423
|
+
}
|
|
350
424
|
/**
|
|
351
425
|
* Update the first document matching the query conditions
|
|
352
426
|
* @param updateData - Data to update
|