firestore-batch-updater 1.13.0 → 1.15.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 +47 -1
- package/README.md +47 -1
- package/dist/index.d.mts +21 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +68 -0
- package/dist/index.mjs +68 -0
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
- 비어있는지 확인 - `isEmpty()`로 매칭 문서가 없는지 확인 (`exists()`의 반대)
|
|
21
21
|
- 전체 문서 조회 - `getAll()`로 매칭되는 모든 문서 데이터 조회
|
|
22
22
|
- 집계 쿼리 - `aggregate()`로 서버 사이드 `sum`, `average`, `count` 연산
|
|
23
|
-
- 간편 집계 - `sum()
|
|
23
|
+
- 간편 집계 - `sum()`, `avg()`, `min()`, `max()`로 단일 필드 간편 집계
|
|
24
24
|
- 커서 페이지네이션 - `paginate()`로 메모리 효율적인 페이지 단위 조회
|
|
25
25
|
- ID 직접 조회 - `getOne()`으로 문서 ID로 빠른 조회
|
|
26
26
|
- 벌크 작업 - `bulkCreate()`, `bulkUpdate()`, `bulkDelete()`로 여러 문서에 각기 다른 데이터로 효율적 처리
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
- JSON 내보내기/가져오기 - `toJSON()` / `fromJSON()`으로 문서 JSON 파일 내보내기/가져오기
|
|
31
31
|
- 그룹별 개수 조회 - `countBy()`로 특정 필드 값별 문서 수 집계
|
|
32
32
|
- 랜덤 샘플링 - `sample()`로 쿼리 결과에서 랜덤 문서 추출
|
|
33
|
+
- 필드 값 추출 - `pluck()`로 특정 필드 값만 간단하게 배열로 추출
|
|
33
34
|
- FieldValue 지원 - `increment()`, `arrayUnion()`, `delete()`, `serverTimestamp()` 등 사용 가능
|
|
34
35
|
- 서브컬렉션 & 컬렉션 그룹 - 서브컬렉션 쿼리 또는 동일 이름의 모든 컬렉션 쿼리
|
|
35
36
|
- Dry Run 모드 - 실제 변경 없이 작업 시뮬레이션
|
|
@@ -117,6 +118,8 @@ console.log(`${result.successCount}개 문서 업데이트 완료`);
|
|
|
117
118
|
| `aggregate(spec)` | sum/average/count 집계 쿼리 | `AggregateResult` |
|
|
118
119
|
| `sum(field)` | 숫자 필드 합계 조회 | `number \| null` |
|
|
119
120
|
| `avg(field)` | 숫자 필드 평균 조회 | `number \| null` |
|
|
121
|
+
| `min(field)` | 필드 최소값 조회 | `any` |
|
|
122
|
+
| `max(field)` | 필드 최대값 조회 | `any` |
|
|
120
123
|
| `paginate(options)` | 커서 기반 페이지네이션 | `PaginateResult` |
|
|
121
124
|
| `bulkCreate(docs, options?)` | 여러 문서를 각기 다른 데이터로 생성 | `BulkCreateResult` |
|
|
122
125
|
| `bulkUpdate(updates, options?)` | 여러 문서에 각기 다른 데이터 업데이트 | `BulkUpdateResult` |
|
|
@@ -125,6 +128,7 @@ console.log(`${result.successCount}개 문서 업데이트 완료`);
|
|
|
125
128
|
| `copyTo(target, options?)` | 다른 컬렉션으로 문서 복사/이동 | `CopyToResult` |
|
|
126
129
|
| `distinct(field)` | 특정 필드의 고유값 조회 | `any[]` |
|
|
127
130
|
| `sample(n)` | 매칭 문서에서 랜덤 샘플 추출 | `{ id, data }[]` |
|
|
131
|
+
| `pluck(field)` | 특정 필드 값만 배열로 추출 | `any[]` |
|
|
128
132
|
| `toJSON(path, options?)` | 문서를 JSON 파일로 내보내기 | `ToJSONResult` |
|
|
129
133
|
| `fromJSON(path, options?)` | JSON 파일에서 문서 가져오기 | `FromJSONResult` |
|
|
130
134
|
| `countBy(field)` | 필드 값별 문서 수 집계 | `CountByResult` |
|
|
@@ -540,6 +544,29 @@ console.log(`평균 점수: ${avgScore}`);
|
|
|
540
544
|
// aggregate({ avg: { op: "average", field: "score" } }) → avg("score")
|
|
541
545
|
```
|
|
542
546
|
|
|
547
|
+
### 최솟값 & 최댓값
|
|
548
|
+
|
|
549
|
+
```typescript
|
|
550
|
+
// 필드의 최솟값/최댓값 조회
|
|
551
|
+
const cheapest = await updater.collection("products").min("price");
|
|
552
|
+
const mostExpensive = await updater.collection("products").max("price");
|
|
553
|
+
console.log(`가격 범위: ${cheapest}원 - ${mostExpensive}원`);
|
|
554
|
+
|
|
555
|
+
// 날짜/타임스탬프에도 사용 가능
|
|
556
|
+
const earliestOrder = await updater
|
|
557
|
+
.collection("orders")
|
|
558
|
+
.where("status", "==", "completed")
|
|
559
|
+
.min("createdAt");
|
|
560
|
+
|
|
561
|
+
// 매칭 문서가 없으면 null 반환
|
|
562
|
+
const maxScore = await updater
|
|
563
|
+
.collection("users")
|
|
564
|
+
.where("status", "==", "nonexistent")
|
|
565
|
+
.max("score"); // null
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
> 참고: 한 필드에 `where()`를 걸고 다른 필드에 `min()/max()`를 사용할 경우 Firestore 복합 인덱스가 필요할 수 있습니다. `FAILED_PRECONDITION` 오류가 발생하면 오류 메시지의 링크를 통해 인덱스를 생성하세요.
|
|
569
|
+
|
|
543
570
|
### 커서 기반 페이지네이션
|
|
544
571
|
|
|
545
572
|
```typescript
|
|
@@ -781,6 +808,25 @@ await updater.collection("users").toJSON("./backup.json");
|
|
|
781
808
|
await updater.collection("users_backup").fromJSON("./backup.json");
|
|
782
809
|
```
|
|
783
810
|
|
|
811
|
+
### 필드 값 추출
|
|
812
|
+
|
|
813
|
+
```typescript
|
|
814
|
+
// 이메일 값만 간단한 배열로 추출
|
|
815
|
+
const emails = await updater
|
|
816
|
+
.collection("users")
|
|
817
|
+
.where("status", "==", "active")
|
|
818
|
+
.pluck("email");
|
|
819
|
+
console.log(emails); // ["alice@test.com", "bob@test.com", ...]
|
|
820
|
+
|
|
821
|
+
// 가격 값 추출 후 계산
|
|
822
|
+
const prices = await updater.collection("products").pluck("price");
|
|
823
|
+
const total = prices.reduce((sum, p) => sum + p, 0);
|
|
824
|
+
|
|
825
|
+
// 중첩 필드 지원
|
|
826
|
+
const countries = await updater.collection("users").pluck("address.country");
|
|
827
|
+
// ["US", "KR", "JP", ...]
|
|
828
|
+
```
|
|
829
|
+
|
|
784
830
|
### 랜덤 샘플링
|
|
785
831
|
|
|
786
832
|
```typescript
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ English | [한국어](./README.ko.md)
|
|
|
20
20
|
- Empty check - Use `isEmpty()` to check if no matching documents exist (opposite of `exists()`)
|
|
21
21
|
- Get all documents - Use `getAll()` to retrieve all matching documents with data
|
|
22
22
|
- Aggregation - Use `aggregate()` for server-side `sum`, `average`, and `count` operations
|
|
23
|
-
- Quick aggregation - Use `sum()`
|
|
23
|
+
- Quick aggregation - Use `sum()`, `avg()`, `min()`, `max()` for simple single-field aggregation
|
|
24
24
|
- Cursor pagination - Use `paginate()` for memory-efficient page-by-page iteration
|
|
25
25
|
- Direct ID lookup - Use `getOne()` for fast document retrieval by ID
|
|
26
26
|
- Bulk operations - Use `bulkCreate()`, `bulkUpdate()`, `bulkDelete()` for efficient multi-document operations with different data each
|
|
@@ -30,6 +30,7 @@ English | [한국어](./README.ko.md)
|
|
|
30
30
|
- JSON export/import - Use `toJSON()` / `fromJSON()` to export/import documents as JSON
|
|
31
31
|
- Group counting - Use `countBy()` to count documents grouped by field value
|
|
32
32
|
- Random sampling - Use `sample()` to get random documents from query results
|
|
33
|
+
- Field value extraction - Use `pluck()` to get a simple array of field values
|
|
33
34
|
- FieldValue support - Use `increment()`, `arrayUnion()`, `delete()`, `serverTimestamp()`, etc.
|
|
34
35
|
- Subcollection & Collection Group - Query subcollections or all collections with the same name
|
|
35
36
|
- Dry run mode - Simulate operations without making changes
|
|
@@ -117,6 +118,8 @@ console.log(`Updated ${result.successCount} documents`);
|
|
|
117
118
|
| `aggregate(spec)` | Run sum/average/count queries | `AggregateResult` |
|
|
118
119
|
| `sum(field)` | Get sum of a numeric field | `number \| null` |
|
|
119
120
|
| `avg(field)` | Get average of a numeric field | `number \| null` |
|
|
121
|
+
| `min(field)` | Get minimum value of a field | `any` |
|
|
122
|
+
| `max(field)` | Get maximum value of a field | `any` |
|
|
120
123
|
| `paginate(options)` | Cursor-based pagination | `PaginateResult` |
|
|
121
124
|
| `bulkCreate(docs, options?)` | Create multiple docs with different data | `BulkCreateResult` |
|
|
122
125
|
| `bulkUpdate(updates, options?)` | Update multiple docs with different data | `BulkUpdateResult` |
|
|
@@ -125,6 +128,7 @@ console.log(`Updated ${result.successCount} documents`);
|
|
|
125
128
|
| `copyTo(target, options?)` | Copy/move docs to another collection | `CopyToResult` |
|
|
126
129
|
| `distinct(field)` | Get unique values of a field | `any[]` |
|
|
127
130
|
| `sample(n)` | Get random sample of matching documents | `{ id, data }[]` |
|
|
131
|
+
| `pluck(field)` | Get array of values for a specific field | `any[]` |
|
|
128
132
|
| `toJSON(path, options?)` | Export documents to JSON file | `ToJSONResult` |
|
|
129
133
|
| `fromJSON(path, options?)` | Import documents from JSON file | `FromJSONResult` |
|
|
130
134
|
| `countBy(field)` | Count documents grouped by field value | `CountByResult` |
|
|
@@ -564,6 +568,29 @@ console.log(`Average score: ${avgScore}`);
|
|
|
564
568
|
// aggregate({ avg: { op: "average", field: "score" } }) → avg("score")
|
|
565
569
|
```
|
|
566
570
|
|
|
571
|
+
### Min & Max
|
|
572
|
+
|
|
573
|
+
```typescript
|
|
574
|
+
// Get the minimum/maximum value of a field
|
|
575
|
+
const cheapest = await updater.collection("products").min("price");
|
|
576
|
+
const mostExpensive = await updater.collection("products").max("price");
|
|
577
|
+
console.log(`Price range: $${cheapest} - $${mostExpensive}`);
|
|
578
|
+
|
|
579
|
+
// Works with dates/timestamps too
|
|
580
|
+
const earliestOrder = await updater
|
|
581
|
+
.collection("orders")
|
|
582
|
+
.where("status", "==", "completed")
|
|
583
|
+
.min("createdAt");
|
|
584
|
+
|
|
585
|
+
// Returns null if no documents match
|
|
586
|
+
const maxScore = await updater
|
|
587
|
+
.collection("users")
|
|
588
|
+
.where("status", "==", "nonexistent")
|
|
589
|
+
.max("score"); // null
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
> Note: Combining `where()` on one field with `min()/max()` on a different field may require a Firestore composite index. If you see a `FAILED_PRECONDITION` error, follow the link in the error message to create the required index.
|
|
593
|
+
|
|
567
594
|
### Cursor-Based Pagination
|
|
568
595
|
|
|
569
596
|
```typescript
|
|
@@ -794,6 +821,25 @@ await updater.collection("users").toJSON("./backup.json");
|
|
|
794
821
|
await updater.collection("users_backup").fromJSON("./backup.json");
|
|
795
822
|
```
|
|
796
823
|
|
|
824
|
+
### Pluck Field Values
|
|
825
|
+
|
|
826
|
+
```typescript
|
|
827
|
+
// Get just the email values as a simple array
|
|
828
|
+
const emails = await updater
|
|
829
|
+
.collection("users")
|
|
830
|
+
.where("status", "==", "active")
|
|
831
|
+
.pluck("email");
|
|
832
|
+
console.log(emails); // ["alice@test.com", "bob@test.com", ...]
|
|
833
|
+
|
|
834
|
+
// Get prices for calculation
|
|
835
|
+
const prices = await updater.collection("products").pluck("price");
|
|
836
|
+
const total = prices.reduce((sum, p) => sum + p, 0);
|
|
837
|
+
|
|
838
|
+
// Nested field support
|
|
839
|
+
const countries = await updater.collection("users").pluck("address.country");
|
|
840
|
+
// ["US", "KR", "JP", ...]
|
|
841
|
+
```
|
|
842
|
+
|
|
797
843
|
### Random Sampling
|
|
798
844
|
|
|
799
845
|
```typescript
|
package/dist/index.d.mts
CHANGED
|
@@ -655,6 +655,20 @@ declare class BatchUpdater {
|
|
|
655
655
|
* @returns Average of the field values, or null if no documents match
|
|
656
656
|
*/
|
|
657
657
|
avg(field: string): Promise<number | null>;
|
|
658
|
+
/**
|
|
659
|
+
* Get the minimum value of a numeric field from matching documents
|
|
660
|
+
* Uses orderBy + limit(1) since Firestore doesn't support min/max aggregation natively
|
|
661
|
+
* @param field - Field path to find the minimum value of
|
|
662
|
+
* @returns Minimum field value, or null if no documents match
|
|
663
|
+
*/
|
|
664
|
+
min(field: string): Promise<any>;
|
|
665
|
+
/**
|
|
666
|
+
* Get the maximum value of a numeric field from matching documents
|
|
667
|
+
* Uses orderBy + limit(1) since Firestore doesn't support min/max aggregation natively
|
|
668
|
+
* @param field - Field path to find the maximum value of
|
|
669
|
+
* @returns Maximum field value, or null if no documents match
|
|
670
|
+
*/
|
|
671
|
+
max(field: string): Promise<any>;
|
|
658
672
|
/**
|
|
659
673
|
* Get documents with cursor-based pagination
|
|
660
674
|
* @param options - Pagination options (pageSize, startAfter cursor)
|
|
@@ -682,6 +696,13 @@ declare class BatchUpdater {
|
|
|
682
696
|
* @returns Array of field values with document IDs
|
|
683
697
|
*/
|
|
684
698
|
getFields(fieldPath: string): Promise<FieldValueResult[]>;
|
|
699
|
+
/**
|
|
700
|
+
* Get an array of values for a specific field from matching documents
|
|
701
|
+
* Convenience wrapper around getFields() that returns only values (no IDs)
|
|
702
|
+
* @param field - Field path to extract values from
|
|
703
|
+
* @returns Array of field values (null/undefined values are excluded)
|
|
704
|
+
*/
|
|
705
|
+
pluck(field: string): Promise<any[]>;
|
|
685
706
|
/**
|
|
686
707
|
* Create multiple documents in batch
|
|
687
708
|
* Note: This method does not work with collectionGroup()
|
package/dist/index.d.ts
CHANGED
|
@@ -655,6 +655,20 @@ declare class BatchUpdater {
|
|
|
655
655
|
* @returns Average of the field values, or null if no documents match
|
|
656
656
|
*/
|
|
657
657
|
avg(field: string): Promise<number | null>;
|
|
658
|
+
/**
|
|
659
|
+
* Get the minimum value of a numeric field from matching documents
|
|
660
|
+
* Uses orderBy + limit(1) since Firestore doesn't support min/max aggregation natively
|
|
661
|
+
* @param field - Field path to find the minimum value of
|
|
662
|
+
* @returns Minimum field value, or null if no documents match
|
|
663
|
+
*/
|
|
664
|
+
min(field: string): Promise<any>;
|
|
665
|
+
/**
|
|
666
|
+
* Get the maximum value of a numeric field from matching documents
|
|
667
|
+
* Uses orderBy + limit(1) since Firestore doesn't support min/max aggregation natively
|
|
668
|
+
* @param field - Field path to find the maximum value of
|
|
669
|
+
* @returns Maximum field value, or null if no documents match
|
|
670
|
+
*/
|
|
671
|
+
max(field: string): Promise<any>;
|
|
658
672
|
/**
|
|
659
673
|
* Get documents with cursor-based pagination
|
|
660
674
|
* @param options - Pagination options (pageSize, startAfter cursor)
|
|
@@ -682,6 +696,13 @@ declare class BatchUpdater {
|
|
|
682
696
|
* @returns Array of field values with document IDs
|
|
683
697
|
*/
|
|
684
698
|
getFields(fieldPath: string): Promise<FieldValueResult[]>;
|
|
699
|
+
/**
|
|
700
|
+
* Get an array of values for a specific field from matching documents
|
|
701
|
+
* Convenience wrapper around getFields() that returns only values (no IDs)
|
|
702
|
+
* @param field - Field path to extract values from
|
|
703
|
+
* @returns Array of field values (null/undefined values are excluded)
|
|
704
|
+
*/
|
|
705
|
+
pluck(field: string): Promise<any[]>;
|
|
685
706
|
/**
|
|
686
707
|
* Create multiple documents in batch
|
|
687
708
|
* Note: This method does not work with collectionGroup()
|
package/dist/index.js
CHANGED
|
@@ -501,6 +501,52 @@ var BatchUpdater = class {
|
|
|
501
501
|
});
|
|
502
502
|
return result._avg;
|
|
503
503
|
}
|
|
504
|
+
/**
|
|
505
|
+
* Get the minimum value of a numeric field from matching documents
|
|
506
|
+
* Uses orderBy + limit(1) since Firestore doesn't support min/max aggregation natively
|
|
507
|
+
* @param field - Field path to find the minimum value of
|
|
508
|
+
* @returns Minimum field value, or null if no documents match
|
|
509
|
+
*/
|
|
510
|
+
async min(field) {
|
|
511
|
+
this.validateSetup();
|
|
512
|
+
if (!field || typeof field !== "string") {
|
|
513
|
+
throw new Error("Field is required for min operation");
|
|
514
|
+
}
|
|
515
|
+
let query = this.isCollectionGroup ? this.firestore.collectionGroup(this.collectionPath) : this.firestore.collection(this.collectionPath);
|
|
516
|
+
for (const condition of this.conditions) {
|
|
517
|
+
query = query.where(condition.field, condition.operator, condition.value);
|
|
518
|
+
}
|
|
519
|
+
query = query.orderBy(field, "asc").limit(1);
|
|
520
|
+
const snapshot = await query.get();
|
|
521
|
+
if (snapshot.empty) {
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
const value = this.getNestedValue(snapshot.docs[0].data(), field);
|
|
525
|
+
return value ?? null;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Get the maximum value of a numeric field from matching documents
|
|
529
|
+
* Uses orderBy + limit(1) since Firestore doesn't support min/max aggregation natively
|
|
530
|
+
* @param field - Field path to find the maximum value of
|
|
531
|
+
* @returns Maximum field value, or null if no documents match
|
|
532
|
+
*/
|
|
533
|
+
async max(field) {
|
|
534
|
+
this.validateSetup();
|
|
535
|
+
if (!field || typeof field !== "string") {
|
|
536
|
+
throw new Error("Field is required for max operation");
|
|
537
|
+
}
|
|
538
|
+
let query = this.isCollectionGroup ? this.firestore.collectionGroup(this.collectionPath) : this.firestore.collection(this.collectionPath);
|
|
539
|
+
for (const condition of this.conditions) {
|
|
540
|
+
query = query.where(condition.field, condition.operator, condition.value);
|
|
541
|
+
}
|
|
542
|
+
query = query.orderBy(field, "desc").limit(1);
|
|
543
|
+
const snapshot = await query.get();
|
|
544
|
+
if (snapshot.empty) {
|
|
545
|
+
return null;
|
|
546
|
+
}
|
|
547
|
+
const value = this.getNestedValue(snapshot.docs[0].data(), field);
|
|
548
|
+
return value ?? null;
|
|
549
|
+
}
|
|
504
550
|
/**
|
|
505
551
|
* Get documents with cursor-based pagination
|
|
506
552
|
* @param options - Pagination options (pageSize, startAfter cursor)
|
|
@@ -726,6 +772,28 @@ var BatchUpdater = class {
|
|
|
726
772
|
}
|
|
727
773
|
return results;
|
|
728
774
|
}
|
|
775
|
+
/**
|
|
776
|
+
* Get an array of values for a specific field from matching documents
|
|
777
|
+
* Convenience wrapper around getFields() that returns only values (no IDs)
|
|
778
|
+
* @param field - Field path to extract values from
|
|
779
|
+
* @returns Array of field values (null/undefined values are excluded)
|
|
780
|
+
*/
|
|
781
|
+
async pluck(field) {
|
|
782
|
+
this.validateSetup();
|
|
783
|
+
if (!field || typeof field !== "string") {
|
|
784
|
+
throw new Error("Field path is required");
|
|
785
|
+
}
|
|
786
|
+
const query = this.buildQuery();
|
|
787
|
+
const snapshot = await query.get();
|
|
788
|
+
const values = [];
|
|
789
|
+
for (const doc of snapshot.docs) {
|
|
790
|
+
const value = this.getNestedValue(doc.data(), field);
|
|
791
|
+
if (value !== void 0 && value !== null) {
|
|
792
|
+
values.push(value);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
return values;
|
|
796
|
+
}
|
|
729
797
|
/**
|
|
730
798
|
* Create multiple documents in batch
|
|
731
799
|
* Note: This method does not work with collectionGroup()
|
package/dist/index.mjs
CHANGED
|
@@ -456,6 +456,52 @@ var BatchUpdater = class {
|
|
|
456
456
|
});
|
|
457
457
|
return result._avg;
|
|
458
458
|
}
|
|
459
|
+
/**
|
|
460
|
+
* Get the minimum value of a numeric field from matching documents
|
|
461
|
+
* Uses orderBy + limit(1) since Firestore doesn't support min/max aggregation natively
|
|
462
|
+
* @param field - Field path to find the minimum value of
|
|
463
|
+
* @returns Minimum field value, or null if no documents match
|
|
464
|
+
*/
|
|
465
|
+
async min(field) {
|
|
466
|
+
this.validateSetup();
|
|
467
|
+
if (!field || typeof field !== "string") {
|
|
468
|
+
throw new Error("Field is required for min operation");
|
|
469
|
+
}
|
|
470
|
+
let query = this.isCollectionGroup ? this.firestore.collectionGroup(this.collectionPath) : this.firestore.collection(this.collectionPath);
|
|
471
|
+
for (const condition of this.conditions) {
|
|
472
|
+
query = query.where(condition.field, condition.operator, condition.value);
|
|
473
|
+
}
|
|
474
|
+
query = query.orderBy(field, "asc").limit(1);
|
|
475
|
+
const snapshot = await query.get();
|
|
476
|
+
if (snapshot.empty) {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
const value = this.getNestedValue(snapshot.docs[0].data(), field);
|
|
480
|
+
return value ?? null;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Get the maximum value of a numeric field from matching documents
|
|
484
|
+
* Uses orderBy + limit(1) since Firestore doesn't support min/max aggregation natively
|
|
485
|
+
* @param field - Field path to find the maximum value of
|
|
486
|
+
* @returns Maximum field value, or null if no documents match
|
|
487
|
+
*/
|
|
488
|
+
async max(field) {
|
|
489
|
+
this.validateSetup();
|
|
490
|
+
if (!field || typeof field !== "string") {
|
|
491
|
+
throw new Error("Field is required for max operation");
|
|
492
|
+
}
|
|
493
|
+
let query = this.isCollectionGroup ? this.firestore.collectionGroup(this.collectionPath) : this.firestore.collection(this.collectionPath);
|
|
494
|
+
for (const condition of this.conditions) {
|
|
495
|
+
query = query.where(condition.field, condition.operator, condition.value);
|
|
496
|
+
}
|
|
497
|
+
query = query.orderBy(field, "desc").limit(1);
|
|
498
|
+
const snapshot = await query.get();
|
|
499
|
+
if (snapshot.empty) {
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
const value = this.getNestedValue(snapshot.docs[0].data(), field);
|
|
503
|
+
return value ?? null;
|
|
504
|
+
}
|
|
459
505
|
/**
|
|
460
506
|
* Get documents with cursor-based pagination
|
|
461
507
|
* @param options - Pagination options (pageSize, startAfter cursor)
|
|
@@ -681,6 +727,28 @@ var BatchUpdater = class {
|
|
|
681
727
|
}
|
|
682
728
|
return results;
|
|
683
729
|
}
|
|
730
|
+
/**
|
|
731
|
+
* Get an array of values for a specific field from matching documents
|
|
732
|
+
* Convenience wrapper around getFields() that returns only values (no IDs)
|
|
733
|
+
* @param field - Field path to extract values from
|
|
734
|
+
* @returns Array of field values (null/undefined values are excluded)
|
|
735
|
+
*/
|
|
736
|
+
async pluck(field) {
|
|
737
|
+
this.validateSetup();
|
|
738
|
+
if (!field || typeof field !== "string") {
|
|
739
|
+
throw new Error("Field path is required");
|
|
740
|
+
}
|
|
741
|
+
const query = this.buildQuery();
|
|
742
|
+
const snapshot = await query.get();
|
|
743
|
+
const values = [];
|
|
744
|
+
for (const doc of snapshot.docs) {
|
|
745
|
+
const value = this.getNestedValue(doc.data(), field);
|
|
746
|
+
if (value !== void 0 && value !== null) {
|
|
747
|
+
values.push(value);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return values;
|
|
751
|
+
}
|
|
684
752
|
/**
|
|
685
753
|
* Create multiple documents in batch
|
|
686
754
|
* Note: This method does not work with collectionGroup()
|