Mark Duckworth 2 lat temu
rodzic
commit
54d4d703b6

+ 3 - 0
Firestore/CHANGELOG.md

@@ -1,3 +1,6 @@
+# 10.17.0
+- [feature] Add support for sum and average aggregate queries.
+
 # 10.16.0
 - [fixed] Fixed an issue where Firestore's binary SwiftPM distribution would
   not link properly when building a target for testing. This issue affected

+ 116 - 239
Firestore/Example/Tests/Integration/API/FIRAggregateTests.mm

@@ -14,19 +14,14 @@
  * limitations under the License.
  */
 
+#import <FirebaseFirestore/FIRAggregateField.h>
 #import <FirebaseFirestore/FIRFieldPath.h>
 #import <FirebaseFirestore/FirebaseFirestore.h>
 
 #import <XCTest/XCTest.h>
 
-// TODO(sum/avg) update these imports with public imports when sum/avg is public
-#import "Firestore/Source/API/FIRAggregateField.h"
-#import "Firestore/Source/API/FIRAggregateQuerySnapshot+Internal.h"
-#import "Firestore/Source/API/FIRQuery+Internal.h"
-
-#include "Firestore/core/src/util/sanitizers.h"
-
 #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h"
+#import "Firestore/core/src/util/exception.h"
 
 @interface FIRAggregateTests : FSTIntegrationTestCase
 @end
@@ -204,9 +199,6 @@
 }
 
 - (void)testCanRunAggregateQuery {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"author" : @"authorA",
@@ -234,39 +226,26 @@
       [self readSnapshotForAggregate:[testCollection aggregate:@[
               [FIRAggregateField aggregateFieldForCount],
               [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
-              [FIRAggregateField aggregateFieldForSumOfField:@"weight"],
-              [FIRAggregateField aggregateFieldForAverageOfField:@"pages"],
-              [FIRAggregateField aggregateFieldForAverageOfField:@"weight"]
+              [FIRAggregateField aggregateFieldForAverageOfField:@"pages"]
             ]]];
 
   // Count
-  XCTAssertEqual([snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]],
+  XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]],
                  [NSNumber numberWithLong:2L]);
   XCTAssertEqual([snapshot count], [NSNumber numberWithLong:2L]);
 
   // Sum
   XCTAssertEqual(
-      [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
+      [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
       [NSNumber numberWithLong:150L], );
-  XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"weight"]]
-          doubleValue],
-      99.6);
 
   // Average
-  XCTAssertEqual(
-      [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]],
-      [NSNumber numberWithDouble:75.0]);
-  XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"weight"]]
-          doubleValue],
-      49.8);
+  XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField
+                                                      aggregateFieldForAverageOfField:@"pages"]],
+                 [NSNumber numberWithDouble:75.0]);
 }
 
 - (void)testCanRunEmptyAggregateQuery {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"author" : @"authorA",
@@ -311,8 +290,6 @@
 // (TODO:b/283101111): Try thread sanitizer to see if timeout on Github Actions is gone.
 #if !defined(THREAD_SANITIZER)
 - (void)testAggregateFieldQuerySnapshotEquality {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"author" : @"authorA",
@@ -340,40 +317,33 @@
       [self readSnapshotForAggregate:[testCollection aggregate:@[
               [FIRAggregateField aggregateFieldForCount],
               [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
-              [FIRAggregateField aggregateFieldForSumOfField:@"weight"],
-              [FIRAggregateField aggregateFieldForAverageOfField:@"pages"],
-              [FIRAggregateField aggregateFieldForAverageOfField:@"weight"]
+              [FIRAggregateField aggregateFieldForAverageOfField:@"pages"]
             ]]];
 
   FIRAggregateQuerySnapshot* snapshot2 =
       [self readSnapshotForAggregate:[testCollection aggregate:@[
               [FIRAggregateField aggregateFieldForCount],
               [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
-              [FIRAggregateField aggregateFieldForSumOfField:@"weight"],
-              [FIRAggregateField aggregateFieldForAverageOfField:@"pages"],
-              [FIRAggregateField aggregateFieldForAverageOfField:@"weight"]
+              [FIRAggregateField aggregateFieldForAverageOfField:@"pages"]
             ]]];
 
   // different aggregates
   FIRAggregateQuerySnapshot* snapshot3 =
       [self readSnapshotForAggregate:[testCollection aggregate:@[
               [FIRAggregateField aggregateFieldForCount],
-              [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
               [FIRAggregateField aggregateFieldForSumOfField:@"weight"],
-              [FIRAggregateField aggregateFieldForAverageOfField:@"pages"]
+              [FIRAggregateField aggregateFieldForAverageOfField:@"weight"]
             ]]];
 
   // different data set
-  FIRAggregateQuerySnapshot* snapshot4 = [self
-      readSnapshotForAggregate:[[testCollection queryWhereField:@"pages" isGreaterThan:@50]
-                                   aggregate:@[
-                                     [FIRAggregateField aggregateFieldForCount],
-                                     [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
-                                     [FIRAggregateField aggregateFieldForSumOfField:@"weight"],
-                                     [FIRAggregateField aggregateFieldForAverageOfField:@"pages"],
-                                     [FIRAggregateField
-                                         aggregateFieldForAverageOfField:@"weight"]
-                                   ]]];
+  FIRAggregateQuerySnapshot* snapshot4 =
+      [self readSnapshotForAggregate:[[testCollection queryWhereField:@"pages" isGreaterThan:@50]
+                                         aggregate:@[
+                                           [FIRAggregateField aggregateFieldForCount],
+                                           [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
+                                           [FIRAggregateField
+                                               aggregateFieldForAverageOfField:@"pages"]
+                                         ]]];
 
   XCTAssertEqualObjects(snapshot1, snapshot2);
   XCTAssertNotEqualObjects(snapshot1, snapshot3);
@@ -387,15 +357,8 @@
 }
 #endif  // #if !defined(THREAD_SANITIZER)
 
-- (void)testAllowsAliasesLongerThan1500Bytes {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
-  // The longest field name allowed is 1500. The alias chosen by the client is <op>_<fieldName>.
-  // If the field name is
-  // 1500 bytes, the alias will be longer than 1500, which is the limit for aliases. This is to
-  // make sure the client
-  // can handle this corner case correctly.
+- (void)testAggregateOnFieldNameWithMaxLength {
+  // The longest field name and alias allowed is 1500 bytes or 1499 characters.
   NSString* longField = [@"" stringByPaddingToLength:1499
                                           withString:@"0123456789"
                                      startingAtIndex:0];
@@ -410,14 +373,11 @@
 
   // Sum
   XCTAssertEqual(
-      [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:longField]],
+      [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:longField]],
       [NSNumber numberWithLong:3], );
 }
 
 - (void)testCanGetDuplicateAggregations {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"author" : @"authorA",
@@ -449,19 +409,16 @@
             ]]];
 
   // Count
-  XCTAssertEqual([snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]],
+  XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]],
                  [NSNumber numberWithLong:2L]);
 
   // Sum
   XCTAssertEqual(
-      [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
+      [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
       [NSNumber numberWithLong:150L], );
 }
 
 - (void)testTerminateDoesNotCrashWithFlyingAggregateQuery {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"author" : @"authorA",
@@ -503,19 +460,18 @@
   [self awaitExpectation:expectation];
 
   // Count
-  XCTAssertEqual([result valueForAggregation:[FIRAggregateField aggregateFieldForCount]],
+  XCTAssertEqual([result valueForAggregateField:[FIRAggregateField aggregateFieldForCount]],
                  [NSNumber numberWithLong:2L]);
 
   // Sum
   XCTAssertEqual(
-      [result valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
+      [result valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
       [NSNumber numberWithLong:150L], );
 }
 
 - (void)testCanPerformMaxAggregations {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
+  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator],
+            "Skip this test if running against production because it requires a composite index.");
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"author" : @"authorA",
@@ -550,28 +506,25 @@
             ]]];
 
   // Assert
-  XCTAssertEqual([snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]],
+  XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]],
                  [NSNumber numberWithLong:2L]);
   XCTAssertEqual(
-      [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
+      [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
       [NSNumber numberWithLong:150L], );
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"weight"]]
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"weight"]]
           doubleValue],
       99.6);
-  XCTAssertEqual(
-      [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]],
-      [NSNumber numberWithDouble:75.0]);
-  XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"weight"]]
-          doubleValue],
-      49.8);
+  XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField
+                                                      aggregateFieldForAverageOfField:@"pages"]],
+                 [NSNumber numberWithDouble:75.0]);
+  XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
+                                                       aggregateFieldForAverageOfField:@"weight"]]
+                     doubleValue],
+                 49.8);
 }
 
 - (void)testCannotPerformMoreThanMaxAggregations {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"author" : @"authorA",
@@ -622,8 +575,8 @@
 }
 
 - (void)testCanRunAggregateCollectionGroupQuery {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
+  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator],
+            "Skip this test if running against production because it requires a composite index.");
 
   NSString* collectionGroup =
       [NSString stringWithFormat:@"%@%@", @"b",
@@ -648,19 +601,19 @@
               [FIRAggregateField aggregateFieldForAverageOfField:@"x"]
             ]]];
   // "cg-doc1", "cg-doc2", "cg-doc3", "cg-doc4", "cg-doc5",
-  XCTAssertEqual([snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]],
+  XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]],
                  [NSNumber numberWithLong:5L]);
   XCTAssertEqual(
-      [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"x"]],
+      [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"x"]],
       [NSNumber numberWithLong:10L]);
   XCTAssertEqual(
-      [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"x"]],
+      [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForAverageOfField:@"x"]],
       [NSNumber numberWithDouble:2.0]);
 }
 
 - (void)testPerformsAggregationsWhenNaNExistsForSomeFieldValues {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
+  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator],
+            "Skip this test if running against production because it requires a composite index.");
 
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
@@ -703,27 +656,24 @@
 
   // Sum
   XCTAssertEqual(
-      [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
+      [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
       [NSNumber numberWithDouble:NAN]);
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]]
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]]
           longValue],
       300L);
 
   // Average
+  XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField
+                                                      aggregateFieldForAverageOfField:@"rating"]],
+                 [NSNumber numberWithDouble:NAN]);
   XCTAssertEqual(
-      [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]],
-      [NSNumber numberWithDouble:NAN]);
-  XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"year"]]
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForAverageOfField:@"year"]]
           doubleValue],
       2000.0);
 }
 
 - (void)testThrowsAnErrorWhenGettingTheResultOfAnUnrequestedAggregation {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"author" : @"authorA",
@@ -763,7 +713,7 @@
 
   @
   try {
-    [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"foo"]];
+    [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"foo"]];
     XCTAssertTrue(false, "Exception expected");
   } @catch (NSException* exception) {
     XCTAssertEqualObjects(exception.name, @"FIRInvalidArgumentException");
@@ -773,7 +723,7 @@
 
   @
   try {
-    [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]];
+    [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]];
     XCTAssertTrue(false, "Exception expected");
   } @catch (NSException* exception) {
     XCTAssertEqualObjects(exception.name, @"FIRInvalidArgumentException");
@@ -783,9 +733,6 @@
 }
 
 - (void)testPerformsAggregationWhenUsingInOperator {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"author" : @"authorA",
@@ -821,40 +768,30 @@
       readSnapshotForAggregate:[[testCollection queryWhereField:@"rating" in:@[ @5, @3 ]]
                                    aggregate:@[
                                      [FIRAggregateField aggregateFieldForSumOfField:@"rating"],
-                                     [FIRAggregateField aggregateFieldForSumOfField:@"pages"],
                                      [FIRAggregateField aggregateFieldForAverageOfField:@"rating"],
-                                     [FIRAggregateField aggregateFieldForAverageOfField:@"pages"],
                                      [FIRAggregateField aggregateFieldForCount]
                                    ]]];
 
   // Count
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
 
   // Sum
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
           longValue],
       8L);
-  XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]]
-          longValue],
-      200L);
 
   // Average
-  XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]]
-          doubleValue],
-      4.0);
-  XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]]
-          doubleValue],
-      100.0);
+  XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
+                                                       aggregateFieldForAverageOfField:@"rating"]]
+                     doubleValue],
+                 4.0);
 }
 
 - (void)testPerformsAggregationWhenUsingArrayContainsAnyOperator {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
+  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator],
+            "Skip this test if running against production because it requires a composite index.");
 
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
@@ -900,32 +837,30 @@
 
   // Count
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
 
   // Sum
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
           longValue],
       0L);
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]]
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]]
           longValue],
       200L);
 
   // Average
   XCTAssertEqualObjects(
-      [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]],
+      [snapshot
+          valueForAggregateField:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]],
       [NSNull null]);
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]]
-          doubleValue],
+      [[snapshot valueForAggregateField:[FIRAggregateField
+                                            aggregateFieldForAverageOfField:@"pages"]] doubleValue],
       100.0);
 }
 
 - (void)testPerformsAggregationsOnNestedMapValues {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"author" : @"authorA",
@@ -942,44 +877,30 @@
   FIRAggregateQuerySnapshot* snapshot =
       [self readSnapshotForAggregate:[testCollection aggregate:@[
               [FIRAggregateField aggregateFieldForSumOfField:@"metadata.pages"],
-              [FIRAggregateField aggregateFieldForSumOfField:@"metadata.rating.user"],
               [FIRAggregateField aggregateFieldForAverageOfField:@"metadata.pages"],
-              [FIRAggregateField aggregateFieldForAverageOfField:@"metadata.rating.critic"],
               [FIRAggregateField aggregateFieldForCount]
             ]]];
 
   // Count
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
 
   // Sum
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField
-                                         aggregateFieldForSumOfField:@"metadata.pages"]] longValue],
-      150L);
-  XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField
-                                         aggregateFieldForSumOfField:@"metadata.rating.user"]]
+      [[snapshot
+          valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"metadata.pages"]]
           longValue],
-      9);
+      150L);
 
   // Average
   XCTAssertEqual(
-      [[snapshot
-          valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"metadata.pages"]]
+      [[snapshot valueForAggregateField:[FIRAggregateField
+                                            aggregateFieldForAverageOfField:@"metadata.pages"]]
           doubleValue],
       75.0);
-  XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField
-                                         aggregateFieldForAverageOfField:@"metadata.rating.critic"]]
-          doubleValue],
-      3.0);
 }
 
 - (void)testPerformsSumThatOverflowsMaxLong {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"author" : @"authorA",
@@ -1000,16 +921,13 @@
 
   // Sum
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
           doubleValue],
       [[NSNumber numberWithLong:LLONG_MAX] doubleValue] +
           [[NSNumber numberWithLong:LLONG_MAX] doubleValue]);
 }
 
 - (void)testPerformsSumThatCanOverflowLongValuesDuringAccumulation {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"author" : @"authorA",
@@ -1028,15 +946,12 @@
 
   // Sum
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
           longLongValue],
       [[NSNumber numberWithLong:LLONG_MAX - 100] longLongValue]);
 }
 
 - (void)testPerformsSumThatIsNegative {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"author" : @"authorA",
@@ -1064,15 +979,12 @@
 
   // Sum
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
           longLongValue],
       [[NSNumber numberWithLong:-10101] longLongValue]);
 }
 
 - (void)testPerformsSumThatIsPositiveInfinity {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"author" : @"authorA",
@@ -1093,14 +1005,11 @@
 
   // Sum
   XCTAssertEqual(
-      [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
+      [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
       [NSNumber numberWithDouble:INFINITY]);
 }
 
 - (void)testPerformsSumThatIsNegativeInfinity {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"author" : @"authorA",
@@ -1121,23 +1030,16 @@
 
   // Sum
   XCTAssertEqual(
-      [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
+      [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
       [NSNumber numberWithDouble:-INFINITY]);
 }
 
 - (void)testPerformsSumThatIsValidButCouldOverflowDuringAggregation {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
     @"b" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
     @"c" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]},
-    @"d" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]},
-    @"e" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
-    @"f" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]},
-    @"g" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]},
-    @"h" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]}
+    @"d" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]}
   }];
 
   FIRAggregateQuerySnapshot* snapshot =
@@ -1146,20 +1048,19 @@
                                                        aggregateFieldForSumOfField:@"rating"] ]]];
 
   // Sum
-  XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
-          longLongValue],
-      [[NSNumber numberWithLong:0] longLongValue]);
-  XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
-          doubleValue],
-      [[NSNumber numberWithLong:0] doubleValue]);
+  long long ratingL =
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
+          longLongValue];
+  XCTAssertTrue(ratingL == [[NSNumber numberWithDouble:-INFINITY] longLongValue] || ratingL == 0 ||
+                ratingL == [[NSNumber numberWithDouble:INFINITY] longLongValue]);
+
+  double ratingD =
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
+          doubleValue];
+  XCTAssertTrue(ratingD == -INFINITY || ratingD == 0 || ratingD == INFINITY);
 }
 
 - (void)testPerformsSumOverResultSetOfZeroDocuments {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"author" : @"authorA",
@@ -1190,14 +1091,11 @@
 
   // Sum
   XCTAssertEqual(
-      [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
+      [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
       [NSNumber numberWithLong:0L]);
 }
 
 - (void)testPerformsSumOnlyOnNumericFields {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{@"rating" : [NSNumber numberWithLong:5]},
     @"b" : @{@"rating" : [NSNumber numberWithLong:4]},
@@ -1213,19 +1111,16 @@
 
   // Count
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]] longValue], 4L);
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]] longValue], 4L);
 
   // Sum
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
           longLongValue],
       [[NSNumber numberWithLong:10] longLongValue]);
 }
 
 - (void)testPerformsSumOfMinIEEE754 {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]}
   }];
@@ -1237,15 +1132,12 @@
 
   // Sum
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
           doubleValue],
       [[NSNumber numberWithDouble:__DBL_DENORM_MIN__] doubleValue]);
 }
 
 - (void)testPerformsAverageOfVariousNumericTypes {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"x" : @1,
@@ -1306,17 +1198,14 @@
         [self readSnapshotForAggregate:[testCollection aggregate:@[ testCase[@"agg"] ]]];
 
     // Average
-    XCTAssertEqual([[snapshot valueForAggregation:testCase[@"agg"]] longValue],
+    XCTAssertEqual([[snapshot valueForAggregateField:testCase[@"agg"]] longValue],
                    [testCase[@"expected"] longLongValue]);
-    XCTAssertEqualWithAccuracy([[snapshot valueForAggregation:testCase[@"agg"]] doubleValue],
+    XCTAssertEqualWithAccuracy([[snapshot valueForAggregateField:testCase[@"agg"]] doubleValue],
                                [testCase[@"expected"] doubleValue], 0.00000000000001);
   }
 }
 
 - (void)testPerformsAverageCausingUnderflow {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]},
     @"b" : @{@"rating" : [NSNumber numberWithDouble:0]}
@@ -1328,16 +1217,13 @@
             ]]];
 
   // Average
-  XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]]
-          doubleValue],
-      [[NSNumber numberWithDouble:0] doubleValue]);
+  XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
+                                                       aggregateFieldForAverageOfField:@"rating"]]
+                     doubleValue],
+                 [[NSNumber numberWithDouble:0] doubleValue]);
 }
 
 - (void)testPerformsAverageOfMinIEEE754 {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]}
   }];
@@ -1348,16 +1234,13 @@
             ]]];
 
   // Average
-  XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]]
-          doubleValue],
-      [[NSNumber numberWithDouble:__DBL_DENORM_MIN__] doubleValue]);
+  XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
+                                                       aggregateFieldForAverageOfField:@"rating"]]
+                     doubleValue],
+                 [[NSNumber numberWithDouble:__DBL_DENORM_MIN__] doubleValue]);
 }
 
 - (void)testPerformsAverageOverflowIEEE754DuringAccumulation {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
     @"b" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]}
@@ -1369,16 +1252,13 @@
             ]]];
 
   // Average
-  XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]]
-          doubleValue],
-      [[NSNumber numberWithDouble:INFINITY] doubleValue]);
+  XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
+                                                       aggregateFieldForAverageOfField:@"rating"]]
+                     doubleValue],
+                 [[NSNumber numberWithDouble:INFINITY] doubleValue]);
 }
 
 - (void)testPerformsAverageOverResultSetOfZeroDocuments {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{
       @"author" : @"authorA",
@@ -1408,15 +1288,12 @@
                                                  aggregateFieldForAverageOfField:@"pages"] ]]];
 
   // Average
-  XCTAssertEqual(
-      [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]],
-      [NSNull null]);
+  XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField
+                                                      aggregateFieldForAverageOfField:@"pages"]],
+                 [NSNull null]);
 }
 
 - (void)testPerformsAverageOnlyOnNumericFields {
-  // TODO(sum/avg) remove the check below when sum and avg are supported in production
-  XCTSkipIf(![FSTIntegrationTestCase isRunningAgainstEmulator], @"only tested against emulator");
-
   FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
     @"a" : @{@"rating" : [NSNumber numberWithLong:5]},
     @"b" : @{@"rating" : [NSNumber numberWithLong:4]},
@@ -1432,13 +1309,13 @@
 
   // Count
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]] longValue], 4L);
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]] longValue], 4L);
 
   // Average
-  XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]]
-          doubleValue],
-      [[NSNumber numberWithDouble:5] doubleValue]);
+  XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
+                                                       aggregateFieldForAverageOfField:@"rating"]]
+                     doubleValue],
+                 [[NSNumber numberWithDouble:5] doubleValue]);
 }
 
 @end

+ 3 - 3
Firestore/Example/Tests/Integration/API/FIRQueryTests.mm

@@ -1508,14 +1508,14 @@
                                      [FIRAggregateField aggregateFieldForSumOfField:@"sort"],
                                      [FIRAggregateField aggregateFieldForAverageOfField:@"v"],
                                    ]]];
-  XCTAssertEqual([snapshot valueForAggregation:[FIRAggregateField aggregateFieldForCount]],
+  XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]],
                  [NSNumber numberWithLong:3L]);
   XCTAssertEqual(
-      [[snapshot valueForAggregation:[FIRAggregateField aggregateFieldForSumOfField:@"sort"]]
+      [[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"sort"]]
           longValue],
       6L);
   XCTAssertEqual(
-      [snapshot valueForAggregation:[FIRAggregateField aggregateFieldForAverageOfField:@"v"]],
+      [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForAverageOfField:@"v"]],
       [NSNumber numberWithDouble:1.0]);
 }
 

+ 2 - 2
Firestore/Source/API/FIRAggregateField.mm

@@ -16,11 +16,11 @@
 
 #import "FIRAggregateField.h"
 
-#include <string>
-
 #import "Firestore/Source/API/FIRAggregateField+Internal.h"
 #import "Firestore/Source/API/FIRFieldPath+Internal.h"
 
+#import "Firestore/core/src/model/aggregate_field.h"
+
 using firebase::firestore::model::AggregateField;
 
 NS_ASSUME_NONNULL_BEGIN

+ 2 - 2
Firestore/Source/API/FIRAggregateQuery+Internal.h

@@ -24,8 +24,8 @@ NS_ASSUME_NONNULL_BEGIN
 @interface FIRAggregateQuery (/* init */)
 
 - (instancetype)init NS_UNAVAILABLE;
-- (instancetype)initWithQueryAndAggregations:(FIRQuery *)query
-                                aggregations:(NSArray<FIRAggregateField *> *)aggregations
+- (instancetype)initWithQuery:(FIRQuery *)query
+              aggregateFields:(NSArray<FIRAggregateField *> *)aggregations
     NS_DESIGNATED_INITIALIZER;
 
 @end

+ 3 - 3
Firestore/Source/API/FIRAggregateQuery.mm

@@ -37,13 +37,13 @@ NS_ASSUME_NONNULL_BEGIN
   std::unique_ptr<AggregateQuery> _aggregateQuery;
 }
 
-- (instancetype)initWithQueryAndAggregations:(FIRQuery *)query
-                                aggregations:(NSArray<FIRAggregateField *> *)aggregations {
+- (instancetype)initWithQuery:(FIRQuery *)query
+              aggregateFields:(NSArray<FIRAggregateField *> *)aggregateFields {
   if (self = [super init]) {
     _query = query;
 
     std::vector<AggregateField> _aggregateFields;
-    for (FIRAggregateField *field in aggregations) {
+    for (FIRAggregateField *field in aggregateFields) {
       _aggregateFields.push_back([field createInternalValue]);
     }
 

+ 0 - 22
Firestore/Source/API/FIRAggregateQuerySnapshot+Internal.h

@@ -35,26 +35,4 @@ NS_ASSUME_NONNULL_BEGIN
 
 @end
 
-// TODO(sum/avg) move the contents of this FuturePublicApi category to
-// ../Public/FirebaseFirestore/FIRAggregateQuerySnapshot.h
-@interface FIRAggregateQuerySnapshot (FuturePublicApi)
-
-/**
- * Gets the aggregation result for the specified aggregation without loss of precision. No coercion
- * of data types or values is performed.
- *
- * See the `AggregateField` class for the expected aggregration result values and types. Numeric
- * aggregation results will be boxed in an `NSNumber`.
- *
- * @param aggregation An instance of `AggregateField` that specifies which aggregation result to
- * return.
- * @return Returns the aggregation result from the server without loss of precision.
- * @warning Throws an `InvalidArgument` exception if the aggregation was not requested in the
- * `AggregateQuery`.
- * @see `AggregateField`
- */
-- (nullable id)valueForAggregation:(FIRAggregateField *)aggregation NS_SWIFT_NAME(get(_:));
-
-@end
-
 NS_ASSUME_NONNULL_END

+ 7 - 7
Firestore/Source/API/FIRAggregateQuerySnapshot.mm

@@ -71,25 +71,25 @@ NS_ASSUME_NONNULL_BEGIN
 #pragma mark - Public Methods
 
 - (NSNumber *)count {
-  return (NSNumber *)[self valueForAggregation:[FIRAggregateField aggregateFieldForCount]];
+  return (NSNumber *)[self valueForAggregateField:[FIRAggregateField aggregateFieldForCount]];
 }
 
 - (FIRAggregateQuery *)query {
   return _query;
 }
 
-- (nullable id)valueForAggregation:(FIRAggregateField *)aggregation {
+- (id)valueForAggregateField:(FIRAggregateField *)aggregateField {
   FIRServerTimestampBehavior serverTimestampBehavior = FIRServerTimestampBehaviorNone;
-  AggregateAlias alias = [aggregation createAlias];
+  AggregateAlias alias = [aggregateField createAlias];
   absl::optional<google_firestore_v1_Value> fieldValue = _result.Get(alias.StringValue());
   if (!fieldValue) {
     std::string path{""};
-    if (aggregation.fieldPath) {
-      path = [aggregation.fieldPath internalValue].CanonicalString();
+    if (aggregateField.fieldPath) {
+      path = [aggregateField.fieldPath internalValue].CanonicalString();
     }
 
-    ThrowInvalidArgument("'%s(%s)' was not requested in the aggregation query.", [aggregation name],
-                         path);
+    ThrowInvalidArgument("'%s(%s)' was not requested in the aggregation query.",
+                         [aggregateField name], path);
   }
   FSTUserDataWriter *dataWriter =
       [[FSTUserDataWriter alloc] initWithFirestore:_query.query.firestore.wrapped

+ 0 - 23
Firestore/Source/API/FIRQuery+Internal.h

@@ -47,27 +47,4 @@ NS_ASSUME_NONNULL_BEGIN
 
 @end
 
-// TODO(sum/avg) move the contents of this FuturePublicApi category to
-// ../Public/FirebaseFirestore/FIRAggregateQuerySnapshot.h
-@interface FIRQuery (FuturePublicApi)
-
-/**
- * Creates and returns a new `AggregateQuery` that aggregates the documents in the result set
- * of this query, without actually downloading the documents.
- *
- * Using an `AggregateQuery` to perform aggregations is efficient because only the final aggregation
- * values, not the documents' data, is downloaded. The query can even aggregate the documents if the
- * result set would be prohibitively large to download entirely (e.g. thousands of documents).
- *
- * @param aggregations Specifies the aggregation operations to perform on the result set of this
- * query.
- *
- * @return An `AggregateQuery` encapsulating this `Query` and `AggregateField`s, which can be used
- * to query the server for the aggregation results.
- */
-- (FIRAggregateQuery *)aggregate:(NSArray<FIRAggregateField *> *)aggregations
-    NS_SWIFT_NAME(aggregate(_:));
-
-@end
-
 NS_ASSUME_NONNULL_END

+ 3 - 3
Firestore/Source/API/FIRQuery.mm

@@ -485,11 +485,11 @@ int32_t SaturatedLimitValue(NSInteger limit) {
 
 - (FIRAggregateQuery *)count {
   FIRAggregateField *countAF = [FIRAggregateField aggregateFieldForCount];
-  return [[FIRAggregateQuery alloc] initWithQueryAndAggregations:self aggregations:@[ countAF ]];
+  return [[FIRAggregateQuery alloc] initWithQuery:self aggregateFields:@[ countAF ]];
 }
 
-- (FIRAggregateQuery *)aggregate:(NSArray<FIRAggregateField *> *)aggregations {
-  return [[FIRAggregateQuery alloc] initWithQueryAndAggregations:self aggregations:aggregations];
+- (FIRAggregateQuery *)aggregate:(NSArray<FIRAggregateField *> *)aggregateFields {
+  return [[FIRAggregateQuery alloc] initWithQuery:self aggregateFields:aggregateFields];
 }
 
 #pragma mark - Private Methods

+ 2 - 4
Firestore/Source/API/FIRAggregateField.h → Firestore/Source/Public/FirebaseFirestore/FIRAggregateField.h

@@ -20,8 +20,6 @@ NS_ASSUME_NONNULL_BEGIN
 
 @class FIRFieldPath;
 
-// TODO(sum/avg) move this entire file to ../Public/FirebaseFirestore when the API can be public
-
 /**
  * Represents an aggregation that can be performed by Firestore.
  */
@@ -84,7 +82,7 @@ NS_SWIFT_NAME(AggregateField)
  * Create an `AggregateField` object that can be used to compute the average of
  * a specified field over a range of documents in the result set of a query.
  *
- * The result of an average operation will always be a 64-bit integer value, a double, or NaN.
+ * The result of an average operation will always be a double or NaN.
  *
  * - Averaging over zero documents or fields will result in a double value representing NaN.
  * - Averaging over NaN will result in a double value representing NaN.
@@ -99,7 +97,7 @@ NS_SWIFT_NAME(AggregateField)
  * Create an `AggregateField` object that can be used to compute the average of
  * a specified field over a range of documents in the result set of a query.
  *
- * The result of an average operation will always be a 64-bit integer value, a double, or NaN.
+ * The result of an average operation will always be a double or NaN.
  *
  * - Averaging over zero documents or fields will result in a double value representing NaN.
  * - Averaging over NaN will result in a double value representing NaN.

+ 17 - 0
Firestore/Source/Public/FirebaseFirestore/FIRAggregateQuerySnapshot.h

@@ -19,6 +19,7 @@
 NS_ASSUME_NONNULL_BEGIN
 
 @class FIRAggregateQuery;
+@class FIRAggregateField;
 
 /**
  * The results of executing an `AggregateQuery`.
@@ -36,6 +37,22 @@ NS_SWIFT_NAME(AggregateQuerySnapshot)
 /** The number of documents in the result set of the underlying query. */
 @property(nonatomic, readonly) NSNumber* count;
 
+/**
+ * Gets the aggregate result for the specified aggregate field without loss of precision. No
+ * coercion of data types or values is performed.
+ *
+ * See the `AggregateField` class for the expected aggregate result values and types. Numeric
+ * aggregate results will be boxed in an `NSNumber`.
+ *
+ * @param aggregateField An instance of `AggregateField` that specifies which aggregate result to
+ * return.
+ * @return Returns the aggregate result from the server without loss of precision.
+ * @warning Throws an `InvalidArgument` exception if the aggregate field was not requested in the
+ * `AggregateQuery`.
+ * @see `AggregateField`
+ */
+- (id)valueForAggregateField:(FIRAggregateField*)aggregateField NS_SWIFT_NAME(get(_:));
+
 @end
 
 NS_ASSUME_NONNULL_END

+ 20 - 2
Firestore/Source/Public/FirebaseFirestore/FIRQuery.h

@@ -20,6 +20,7 @@
 #import "FIRListenerRegistration.h"
 
 @class FIRAggregateQuery;
+@class FIRAggregateField;
 @class FIRFieldPath;
 @class FIRFirestore;
 @class FIRFilter;
@@ -559,11 +560,28 @@ NS_SWIFT_NAME(Query)
  * the documents.
  *
  * Using this `AggregateQuery` to count the documents is efficient because only the final count,
- * not the documents' data, is downloaded. The query can even count the documents if the result
- * set would be prohibitively large to download entirely (e.g. thousands of documents).
+ * not the documents' data, is downloaded. This allows for counting document collections that would
+ * otherwise be too large to download (e.g. containing thousands of documents).
  */
 @property(nonatomic, readonly) FIRAggregateQuery *count;
 
+/**
+ * Creates and returns a new `AggregateQuery` that aggregates the documents in the result set
+ * of this query, without actually downloading the documents.
+ *
+ * Using an `AggregateQuery` to perform aggregations is efficient because only the final aggregation
+ * values, not the documents' data, is downloaded. This allows for aggregating document collections
+ * that would otherwise be too large to download (e.g. containing thousands of documents).
+ *
+ * @param aggregateFields Specifies the aggregate operations to perform on the result set of this
+ * query.
+ *
+ * @return An `AggregateQuery` encapsulating this `Query` and `AggregateField`s, which can be used
+ * to query the server for the aggregation results.
+ */
+- (FIRAggregateQuery *)aggregate:(NSArray<FIRAggregateField *> *)aggregateFields
+    NS_SWIFT_NAME(aggregate(_:));
+
 @end
 
 NS_ASSUME_NONNULL_END

+ 1 - 0
Firestore/Source/Public/FirebaseFirestore/FirebaseFirestore.h

@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#import "FIRAggregateField.h"
 #import "FIRAggregateQuery.h"
 #import "FIRAggregateQuerySnapshot.h"
 #import "FIRAggregateSource.h"

+ 278 - 350
Firestore/Swift/Tests/Integration/AggregationIntegrationTests.swift

@@ -18,386 +18,314 @@ import FirebaseFirestore
 import FirebaseFirestoreSwift
 import Foundation
 
-// TODO(sum/avg) remove `sumAvgIsPublic` from the directive below to enable these tests when sum/avg
-// is public
-#if sumAvgIsPublic && swift(>=5.5.2)
-  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-
-  class AggregationIntegrationTests: FSTIntegrationTestCase {
-    func testCount() async throws {
-      let collection = collectionRef()
-      try await collection.addDocument(data: [:])
-      let snapshot = try await collection.count.getAggregation(source: .server)
-      XCTAssertEqual(snapshot.count, 1)
-    }
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+class AggregationIntegrationTests: FSTIntegrationTestCase {
+  func testCount() async throws {
+    let collection = collectionRef()
+    try await collection.addDocument(data: [:])
+    let snapshot = try await collection.count.getAggregation(source: .server)
+    XCTAssertEqual(snapshot.count, 1)
+  }
 
-    func testCanRunAggregateQuery() async throws {
-      // TODO(sum/avg) remove the check below when sum and avg are supported in production
-      try XCTSkipIf(
-        !FSTIntegrationTestCase.isRunningAgainstEmulator(),
-        "only tested against emulator"
-      )
-
-      let collection = collectionRef()
-      try await collection.addDocument(data: ["author": "authorA",
-                                              "title": "titleA",
-                                              "pages": 100,
-                                              "height": 24.5,
-                                              "weight": 24.1,
-                                              "foo": 1,
-                                              "bar": 2,
-                                              "baz": 3])
-      try await collection.addDocument(data: ["author": "authorB",
-                                              "title": "titleB",
-                                              "pages": 50,
-                                              "height": 25.5,
-                                              "weight": 75.5,
-                                              "foo": 1,
-                                              "bar": 2,
-                                              "baz": 3])
+  func testCanRunAggregateQuery() async throws {
+    let collection = collectionRef()
+    try await collection.addDocument(data: ["author": "authorA",
+                                            "title": "titleA",
+                                            "pages": 100,
+                                            "height": 24.5,
+                                            "weight": 24.1,
+                                            "foo": 1,
+                                            "bar": 2,
+                                            "baz": 3])
+    try await collection.addDocument(data: ["author": "authorB",
+                                            "title": "titleB",
+                                            "pages": 50,
+                                            "height": 25.5,
+                                            "weight": 75.5,
+                                            "foo": 1,
+                                            "bar": 2,
+                                            "baz": 3])
+
+    let snapshot = try await collection.aggregate([
+      AggregateField.count(),
+      AggregateField.sum("pages"),
+      AggregateField.average("pages"),
+    ]).getAggregation(source: .server)
+
+    // Count
+    XCTAssertEqual(snapshot.get(AggregateField.count()) as? NSNumber, 2)
+
+    // Sum
+    XCTAssertEqual(snapshot.get(AggregateField.sum("pages")) as? NSNumber, 150)
+    XCTAssertEqual(snapshot.get(AggregateField.sum("pages")) as? Double, 150)
+    XCTAssertEqual(snapshot.get(AggregateField.sum("pages")) as? Int64, 150)
+
+    // Average
+    XCTAssertEqual(snapshot.get(AggregateField.average("pages")) as? NSNumber, 75.0)
+    XCTAssertEqual(snapshot.get(AggregateField.average("pages")) as? Double, 75.0)
+    XCTAssertEqual(snapshot.get(AggregateField.average("pages")) as? Int64, 75)
+  }
 
+  func testCannotPerformMoreThanMaxAggregations() async throws {
+    let collection = collectionRef()
+    try await collection.addDocument(data: ["author": "authorA",
+                                            "title": "titleA",
+                                            "pages": 100,
+                                            "height": 24.5,
+                                            "weight": 24.1,
+                                            "foo": 1,
+                                            "bar": 2,
+                                            "baz": 3])
+
+    // Max is 5, we're attempting 6. I also like to live dangerously.
+    do {
       let snapshot = try await collection.aggregate([
         AggregateField.count(),
         AggregateField.sum("pages"),
         AggregateField.sum("weight"),
         AggregateField.average("pages"),
         AggregateField.average("weight"),
+        AggregateField.average("foo"),
       ]).getAggregation(source: .server)
-
-      // Count
-      XCTAssertEqual(snapshot.get(AggregateField.count()) as? NSNumber, 2)
-
-      // Sum
-      XCTAssertEqual(snapshot.get(AggregateField.sum("pages")) as? NSNumber, 150)
-      XCTAssertEqual(snapshot.get(AggregateField.sum("pages")) as? Double, 150)
-      XCTAssertEqual(snapshot.get(AggregateField.sum("pages")) as? Int64, 150)
-      XCTAssertEqual(snapshot.get(AggregateField.sum("weight")) as? NSNumber, 99.6)
-      XCTAssertEqual(snapshot.get(AggregateField.sum("weight")) as? Double, 99.6)
-
-      // Average
-      XCTAssertEqual(snapshot.get(AggregateField.average("pages")) as? NSNumber, 75.0)
-      XCTAssertEqual(snapshot.get(AggregateField.average("pages")) as? Double, 75.0)
-      XCTAssertEqual(snapshot.get(AggregateField.average("pages")) as? Int64, 75)
-      XCTAssertEqual(snapshot.get(AggregateField.average("weight")) as? NSNumber, 49.8)
-      XCTAssertEqual(snapshot.get(AggregateField.average("weight")) as? Double, 49.8)
-    }
-
-    func testCannotPerformMoreThanMaxAggregations() async throws {
-      // TODO(sum/avg) remove the check below when sum and avg are supported in production
-      try XCTSkipIf(
-        !FSTIntegrationTestCase.isRunningAgainstEmulator(),
-        "only tested against emulator"
-      )
-
-      let collection = collectionRef()
-      try await collection.addDocument(data: ["author": "authorA",
-                                              "title": "titleA",
-                                              "pages": 100,
-                                              "height": 24.5,
-                                              "weight": 24.1,
-                                              "foo": 1,
-                                              "bar": 2,
-                                              "baz": 3])
-
-      // Max is 5, we're attempting 6. I also like to live dangerously.
-      do {
-        let snapshot = try await collection.aggregate([
-          AggregateField.count(),
-          AggregateField.sum("pages"),
-          AggregateField.sum("weight"),
-          AggregateField.average("pages"),
-          AggregateField.average("weight"),
-          AggregateField.average("foo"),
-        ]).getAggregation(source: .server)
-        XCTFail("Error expected.")
-      } catch let error as NSError {
-        XCTAssertNotNil(error)
-        XCTAssertTrue(error.localizedDescription.contains("maximum number of aggregations"))
-      }
-    }
-
-    func testPerformsAggregationsWhenNaNExistsForSomeFieldValues() async throws {
-      // TODO(sum/avg) remove the check below when sum and avg are supported in production
-      try XCTSkipIf(
-        !FSTIntegrationTestCase.isRunningAgainstEmulator(),
-        "only tested against emulator"
-      )
-
-      let collection = collectionRef()
-      try await collection.addDocument(data: ["author": "authorA",
-                                              "title": "titleA",
-                                              "pages": 100,
-                                              "year": 1980,
-                                              "rating": 4])
-      try await collection.addDocument(data: ["author": "authorB",
-                                              "title": "titleB",
-                                              "pages": 50,
-                                              "year": 2020,
-                                              "rating": Double.nan])
-
-      let snapshot = try await collection.aggregate([
-        AggregateField.sum("pages"),
-        AggregateField.sum("rating"),
-        AggregateField.average("pages"),
-        AggregateField.average("rating"),
-      ]).getAggregation(source: .server)
-
-      // Sum
-      XCTAssertEqual(snapshot.get(AggregateField.sum("pages")) as? NSNumber, 150)
-      XCTAssertTrue((snapshot.get(AggregateField.sum("rating")) as? Double)?.isNaN ?? false)
-
-      // Average
-      XCTAssertEqual(snapshot.get(AggregateField.average("pages")) as? NSNumber, 75.0)
-      XCTAssertTrue((snapshot.get(AggregateField.average("rating")) as? Double)?.isNaN ?? false)
-    }
-
-    func testThrowsAnErrorWhenGettingTheResultOfAnUnrequestedAggregation() async throws {
-      // TODO(sum/avg) remove the check below when sum and avg are supported in production
-      try XCTSkipIf(
-        !FSTIntegrationTestCase.isRunningAgainstEmulator(),
-        "only tested against emulator"
-      )
-
-      let collection = collectionRef()
-      try await collection.addDocument(data: [:])
-
-      let snapshot = try await collection.aggregate([AggregateField.average("foo")])
-        .getAggregation(source: .server)
-
-      XCTAssertTrue(FSTNSExceptionUtil.testForException({
-        snapshot.count
-      }, reasonContains: "'count()' was not requested in the aggregation query"))
-
-      XCTAssertTrue(FSTNSExceptionUtil.testForException({
-        snapshot.get(AggregateField.sum("foo"))
-      }, reasonContains: "'sum(foo)' was not requested in the aggregation query"))
-
-      XCTAssertTrue(FSTNSExceptionUtil.testForException({
-        snapshot.get(AggregateField.average("bar"))
-      }, reasonContains: "'avg(bar)' was not requested in the aggregation query"))
-    }
-
-    func testPerformsAggregationsOnNestedMapValues() async throws {
-      // TODO(sum/avg) remove the check below when sum and avg are supported in production
-      try XCTSkipIf(
-        !FSTIntegrationTestCase.isRunningAgainstEmulator(),
-        "only tested against emulator"
-      )
-
-      let collection = collectionRef()
-      try await collection.addDocument(data: ["metadata": [
-        "pages": 100,
-        "rating": [
-          "critic": 2,
-          "user": 5,
-        ],
-      ]])
-      try await collection.addDocument(data: ["metadata": [
-        "pages": 50,
-        "rating": [
-          "critic": 4,
-          "user": 4,
-        ],
-      ]])
-
-      let snapshot = try await collection.aggregate([
-        AggregateField.count(),
-        AggregateField.sum("metadata.pages"),
-        AggregateField.sum(FieldPath(["metadata", "rating", "user"])),
-        AggregateField.average("metadata.pages"),
-        AggregateField.average(FieldPath(["metadata", "rating", "critic"])),
-      ]).getAggregation(source: .server)
-
-      // Count
-      XCTAssertEqual(snapshot.get(AggregateField.count()) as? NSNumber, 2)
-
-      // Sum
-      XCTAssertEqual(
-        snapshot.get(AggregateField.sum(FieldPath(["metadata", "pages"]))) as? NSNumber,
-        150
-      )
-      XCTAssertEqual(snapshot.get(AggregateField.sum("metadata.pages")) as? NSNumber, 150)
-      XCTAssertEqual(snapshot.get(AggregateField.sum("metadata.rating.user")) as? NSNumber, 9)
-
-      // Average
-      XCTAssertEqual(
-        snapshot.get(AggregateField.average(FieldPath(["metadata", "pages"]))) as? Double,
-        75.0
-      )
-      XCTAssertEqual(
-        snapshot.get(AggregateField.average("metadata.rating.critic")) as? Double,
-        3.0
-      )
+      XCTFail("Error expected.")
+    } catch let error as NSError {
+      XCTAssertNotNil(error)
+      XCTAssertTrue(error.localizedDescription.contains("maximum number of aggregations"))
     }
+  }
 
-    func testSumOverflow() async throws {
-      // TODO(sum/avg) remove the check below when sum and avg are supported in production
-      try XCTSkipIf(
-        !FSTIntegrationTestCase.isRunningAgainstEmulator(),
-        "only tested against emulator"
-      )
-
-      let collection = collectionRef()
-      try await collection.addDocument(data: [
-        "longOverflow": Int64.max,
-        "accumulationOverflow": Int64.max,
-        "positiveInfinity": Double.greatestFiniteMagnitude,
-        "negativeInfinity": -Double.greatestFiniteMagnitude,
-      ])
-      try await collection.addDocument(data: [
-        "longOverflow": Int64.max,
-        "accumulationOverflow": 1,
-        "positiveInfinity": Double.greatestFiniteMagnitude,
-        "negativeInfinity": -Double.greatestFiniteMagnitude,
-      ])
-      try await collection.addDocument(data: [
-        "longOverflow": Int64.max,
-        "accumulationOverflow": -101,
-        "positiveInfinity": Double.greatestFiniteMagnitude,
-        "negativeInfinity": -Double.greatestFiniteMagnitude,
-      ])
+  func testPerformsAggregationsWhenNaNExistsForSomeFieldValues() async throws {
+    try XCTSkipIf(!FSTIntegrationTestCase.isRunningAgainstEmulator(),
+                  "Skip this test if running against production because it requires a composite index.")
+
+    let collection = collectionRef()
+    try await collection.addDocument(data: ["author": "authorA",
+                                            "title": "titleA",
+                                            "pages": 100,
+                                            "year": 1980,
+                                            "rating": 4])
+    try await collection.addDocument(data: ["author": "authorB",
+                                            "title": "titleB",
+                                            "pages": 50,
+                                            "year": 2020,
+                                            "rating": Double.nan])
+
+    let snapshot = try await collection.aggregate([
+      AggregateField.sum("pages"),
+      AggregateField.sum("rating"),
+      AggregateField.average("pages"),
+      AggregateField.average("rating"),
+    ]).getAggregation(source: .server)
+
+    // Sum
+    XCTAssertEqual(snapshot.get(AggregateField.sum("pages")) as? NSNumber, 150)
+    XCTAssertTrue((snapshot.get(AggregateField.sum("rating")) as? Double)?.isNaN ?? false)
+
+    // Average
+    XCTAssertEqual(snapshot.get(AggregateField.average("pages")) as? NSNumber, 75.0)
+    XCTAssertTrue((snapshot.get(AggregateField.average("rating")) as? Double)?.isNaN ?? false)
+  }
 
-      let snapshot = try await collection.aggregate([
-        AggregateField.sum("longOverflow"),
-        AggregateField.sum("accumulationOverflow"),
-        AggregateField.sum("positiveInfinity"),
-        AggregateField.sum("negativeInfinity"),
-      ]).getAggregation(source: .server)
+  func testThrowsAnErrorWhenGettingTheResultOfAnUnrequestedAggregation() async throws {
+    let collection = collectionRef()
+    try await collection.addDocument(data: [:])
 
-      // Sum
-      XCTAssertEqual(
-        snapshot.get(AggregateField.sum("longOverflow")) as? Double,
-        Double(Int64.max) + Double(Int64.max) + Double(Int64.max)
-      )
-      XCTAssertEqual(
-        snapshot.get(AggregateField.sum("accumulationOverflow")) as? Int64,
-        Int64.max - 100
-      )
-      XCTAssertEqual(
-        snapshot.get(AggregateField.sum("positiveInfinity")) as? Double,
-        Double.infinity
-      )
-      XCTAssertEqual(
-        snapshot.get(AggregateField.sum("negativeInfinity")) as? Double,
-        -Double.infinity
-      )
-    }
+    let snapshot = try await collection.aggregate([AggregateField.average("foo")])
+      .getAggregation(source: .server)
 
-    func testAverageOverflow() async throws {
-      // TODO(sum/avg) remove the check below when sum and avg are supported in production
-      try XCTSkipIf(
-        !FSTIntegrationTestCase.isRunningAgainstEmulator(),
-        "only tested against emulator"
-      )
-
-      let collection = collectionRef()
-      try await collection.addDocument(data: [
-        "longOverflow": Int64.max,
-        "doubleOverflow": Double.greatestFiniteMagnitude,
-        "negativeInfinity": -Double.greatestFiniteMagnitude,
-      ])
-      try await collection.addDocument(data: [
-        "longOverflow": Int64.max,
-        "doubleOverflow": Double.greatestFiniteMagnitude,
-        "negativeInfinity": -Double.greatestFiniteMagnitude,
-      ])
-      try await collection.addDocument(data: [
-        "longOverflow": Int64.max,
-        "doubleOverflow": Double.greatestFiniteMagnitude,
-        "negativeInfinity": -Double.greatestFiniteMagnitude,
-      ])
+    XCTAssertTrue(FSTNSExceptionUtil.testForException({
+      snapshot.count
+    }, reasonContains: "'count()' was not requested in the aggregation query"))
 
-      let snapshot = try await collection.aggregate([
-        AggregateField.average("longOverflow"),
-        AggregateField.average("doubleOverflow"),
-        AggregateField.average("negativeInfinity"),
-      ]).getAggregation(source: .server)
+    XCTAssertTrue(FSTNSExceptionUtil.testForException({
+      snapshot.get(AggregateField.sum("foo"))
+    }, reasonContains: "'sum(foo)' was not requested in the aggregation query"))
 
-      // Average
-      XCTAssertEqual(
-        snapshot.get(AggregateField.average("longOverflow")) as? Double,
-        Double(Int64.max)
-      )
-      XCTAssertEqual(
-        snapshot.get(AggregateField.average("doubleOverflow")) as? Double,
-        Double.infinity
-      )
-      XCTAssertEqual(
-        snapshot.get(AggregateField.average("negativeInfinity")) as? Double,
-        -Double.infinity
-      )
-    }
+    XCTAssertTrue(FSTNSExceptionUtil.testForException({
+      snapshot.get(AggregateField.average("bar"))
+    }, reasonContains: "'avg(bar)' was not requested in the aggregation query"))
+  }
 
-    func testAverageUnderflow() async throws {
-      // TODO(sum/avg) remove the check below when sum and avg are supported in production
-      try XCTSkipIf(
-        !FSTIntegrationTestCase.isRunningAgainstEmulator(),
-        "only tested against emulator"
-      )
+  func testPerformsAggregationsOnNestedMapValues() async throws {
+    let collection = collectionRef()
+    try await collection.addDocument(data: ["metadata": [
+      "pages": 100,
+      "rating": [
+        "critic": 2,
+        "user": 5,
+      ],
+    ]])
+    try await collection.addDocument(data: ["metadata": [
+      "pages": 50,
+      "rating": [
+        "critic": 4,
+        "user": 4,
+      ],
+    ]])
+
+    let snapshot = try await collection.aggregate([
+      AggregateField.count(),
+      AggregateField.sum("metadata.pages"),
+      AggregateField.average("metadata.pages"),
+    ]).getAggregation(source: .server)
+
+    // Count
+    XCTAssertEqual(snapshot.get(AggregateField.count()) as? NSNumber, 2)
+
+    // Sum
+    XCTAssertEqual(
+      snapshot.get(AggregateField.sum(FieldPath(["metadata", "pages"]))) as? NSNumber,
+      150
+    )
+    XCTAssertEqual(snapshot.get(AggregateField.sum("metadata.pages")) as? NSNumber, 150)
+
+    // Average
+    XCTAssertEqual(
+      snapshot.get(AggregateField.average(FieldPath(["metadata", "pages"]))) as? Double,
+      75.0
+    )
+  }
 
-      let collection = collectionRef()
-      try await collection.addDocument(data: ["underflowSmall": Double.leastNonzeroMagnitude])
-      try await collection.addDocument(data: ["underflowSmall": 0])
+  func testSumOverflow() async throws {
+    try XCTSkipIf(!FSTIntegrationTestCase.isRunningAgainstEmulator(),
+                  "Skip this test if running against production because it requires a composite index.")
+
+    let collection = collectionRef()
+    try await collection.addDocument(data: [
+      "longOverflow": Int64.max,
+      "accumulationOverflow": Int64.max,
+      "positiveInfinity": Double.greatestFiniteMagnitude,
+      "negativeInfinity": -Double.greatestFiniteMagnitude,
+    ])
+    try await collection.addDocument(data: [
+      "longOverflow": Int64.max,
+      "accumulationOverflow": 1,
+      "positiveInfinity": Double.greatestFiniteMagnitude,
+      "negativeInfinity": -Double.greatestFiniteMagnitude,
+    ])
+    try await collection.addDocument(data: [
+      "longOverflow": Int64.max,
+      "accumulationOverflow": -101,
+      "positiveInfinity": Double.greatestFiniteMagnitude,
+      "negativeInfinity": -Double.greatestFiniteMagnitude,
+    ])
+
+    let snapshot = try await collection.aggregate([
+      AggregateField.sum("longOverflow"),
+      AggregateField.sum("accumulationOverflow"),
+      AggregateField.sum("positiveInfinity"),
+      AggregateField.sum("negativeInfinity"),
+    ]).getAggregation(source: .server)
+
+    // Sum
+    XCTAssertEqual(
+      snapshot.get(AggregateField.sum("longOverflow")) as? Double,
+      Double(Int64.max) + Double(Int64.max) + Double(Int64.max)
+    )
+    XCTAssertEqual(
+      snapshot.get(AggregateField.sum("accumulationOverflow")) as? Int64,
+      Int64.max - 100
+    )
+    XCTAssertEqual(
+      snapshot.get(AggregateField.sum("positiveInfinity")) as? Double,
+      Double.infinity
+    )
+    XCTAssertEqual(
+      snapshot.get(AggregateField.sum("negativeInfinity")) as? Double,
+      -Double.infinity
+    )
+  }
 
-      let snapshot = try await collection.aggregate([AggregateField.average("underflowSmall")])
-        .getAggregation(source: .server)
+  func testAverageOverflow() async throws {
+    try XCTSkipIf(!FSTIntegrationTestCase.isRunningAgainstEmulator(),
+                  "Skip this test if running against production because it requires a composite index.")
+
+    let collection = collectionRef()
+    try await collection.addDocument(data: [
+      "longOverflow": Int64.max,
+      "doubleOverflow": Double.greatestFiniteMagnitude,
+      "negativeInfinity": -Double.greatestFiniteMagnitude,
+    ])
+    try await collection.addDocument(data: [
+      "longOverflow": Int64.max,
+      "doubleOverflow": Double.greatestFiniteMagnitude,
+      "negativeInfinity": -Double.greatestFiniteMagnitude,
+    ])
+    try await collection.addDocument(data: [
+      "longOverflow": Int64.max,
+      "doubleOverflow": Double.greatestFiniteMagnitude,
+      "negativeInfinity": -Double.greatestFiniteMagnitude,
+    ])
+
+    let snapshot = try await collection.aggregate([
+      AggregateField.average("longOverflow"),
+      AggregateField.average("doubleOverflow"),
+      AggregateField.average("negativeInfinity"),
+    ]).getAggregation(source: .server)
+
+    // Average
+    XCTAssertEqual(
+      snapshot.get(AggregateField.average("longOverflow")) as? Double,
+      Double(Int64.max)
+    )
+    XCTAssertEqual(
+      snapshot.get(AggregateField.average("doubleOverflow")) as? Double,
+      Double.infinity
+    )
+    XCTAssertEqual(
+      snapshot.get(AggregateField.average("negativeInfinity")) as? Double,
+      -Double.infinity
+    )
+  }
 
-      // Average
-      XCTAssertEqual(snapshot.get(AggregateField.average("underflowSmall")) as? Double, 0.0)
-    }
+  func testAverageUnderflow() async throws {
+    let collection = collectionRef()
+    try await collection.addDocument(data: ["underflowSmall": Double.leastNonzeroMagnitude])
+    try await collection.addDocument(data: ["underflowSmall": 0])
 
-    func testPerformsAggregateOverResultSetOfZeroDocuments() async throws {
-      // TODO(sum/avg) remove the check below when sum and avg are supported in production
-      try XCTSkipIf(
-        !FSTIntegrationTestCase.isRunningAgainstEmulator(),
-        "only tested against emulator"
-      )
+    let snapshot = try await collection.aggregate([AggregateField.average("underflowSmall")])
+      .getAggregation(source: .server)
 
-      let collection = collectionRef()
-      try await collection.addDocument(data: ["pages": 100])
-      try await collection.addDocument(data: ["pages": 50])
+    // Average
+    XCTAssertEqual(snapshot.get(AggregateField.average("underflowSmall")) as? Double, 0.0)
+  }
 
-      let snapshot = try await collection.whereField("pages", isGreaterThan: 200)
-        .aggregate([AggregateField.count(), AggregateField.sum("pages"),
-                    AggregateField.average("pages")]).getAggregation(source: .server)
+  func testPerformsAggregateOverResultSetOfZeroDocuments() async throws {
+    let collection = collectionRef()
+    try await collection.addDocument(data: ["pages": 100])
+    try await collection.addDocument(data: ["pages": 50])
 
-      // Count
-      XCTAssertEqual(snapshot.get(AggregateField.count()) as? NSNumber, 0)
+    let snapshot = try await collection.whereField("pages", isGreaterThan: 200)
+      .aggregate([AggregateField.count(), AggregateField.sum("pages"),
+                  AggregateField.average("pages")]).getAggregation(source: .server)
 
-      // Sum
-      XCTAssertEqual(snapshot.get(AggregateField.sum("pages")) as? NSNumber, 0)
+    // Count
+    XCTAssertEqual(snapshot.get(AggregateField.count()) as? NSNumber, 0)
 
-      // Average
-      // TODO: (sum/avg) this design is bad, will require and API update
-      XCTAssertEqual(snapshot.get(AggregateField.average("pages")) as? NSNull, NSNull())
-    }
+    // Sum
+    XCTAssertEqual(snapshot.get(AggregateField.sum("pages")) as? NSNumber, 0)
 
-    func testPerformsAggregateOverResultSetOfZeroFields() async throws {
-      // TODO(sum/avg) remove the check below when sum and avg are supported in production
-      try XCTSkipIf(
-        !FSTIntegrationTestCase.isRunningAgainstEmulator(),
-        "only tested against emulator"
-      )
+    // Average
+    XCTAssertEqual(snapshot.get(AggregateField.average("pages")) as? NSNull, NSNull())
+  }
 
-      let collection = collectionRef()
-      try await collection.addDocument(data: ["pages": 100])
-      try await collection.addDocument(data: ["pages": 50])
+  func testPerformsAggregateOverResultSetOfZeroFields() async throws {
+    let collection = collectionRef()
+    try await collection.addDocument(data: ["pages": 100])
+    try await collection.addDocument(data: ["pages": 50])
 
-      let snapshot = try await collection
-        .aggregate([AggregateField.count(), AggregateField.sum("notInMyDocs"),
-                    AggregateField.average("notInMyDocs")]).getAggregation(source: .server)
+    let snapshot = try await collection
+      .aggregate([AggregateField.count(), AggregateField.sum("notInMyDocs"),
+                  AggregateField.average("notInMyDocs")]).getAggregation(source: .server)
 
-      // Count  - 0 because aggregation is performed on documents matching the query AND documents
-      // that have all aggregated fields
-      XCTAssertEqual(snapshot.get(AggregateField.count()) as? NSNumber, 0)
+    // Count  - 0 because aggregation is performed on documents matching the query AND documents
+    // that have all aggregated fields
+    XCTAssertEqual(snapshot.get(AggregateField.count()) as? NSNumber, 0)
 
-      // Sum
-      XCTAssertEqual(snapshot.get(AggregateField.sum("notInMyDocs")) as? NSNumber, 0)
+    // Sum
+    XCTAssertEqual(snapshot.get(AggregateField.sum("notInMyDocs")) as? NSNumber, 0)
 
-      // Average
-      // TODO: (sum/avg) this design is bad, will require and API update
-      XCTAssertEqual(snapshot.get(AggregateField.average("notInMyDocs")) as? NSNull, NSNull())
-    }
+    // Average
+    XCTAssertEqual(snapshot.get(AggregateField.average("notInMyDocs")) as? NSNull, NSNull())
   }
-
-#endif
+}

+ 2 - 2
Firestore/core/src/api/query_core.cc

@@ -432,8 +432,8 @@ std::string Query::Describe(Operator op) const {
 }
 
 AggregateQuery Query::Aggregate(
-    std::vector<AggregateField>&& aggregations) const {
-  return AggregateQuery(*this, std::move(aggregations));
+    std::vector<AggregateField>&& aggregateFields) const {
+  return AggregateQuery(*this, std::move(aggregateFields));
 }
 
 // TODO(b/280805906) Remove this count specific API after the c++ SDK migrates

+ 2 - 2
Firestore/core/src/api/query_core.h

@@ -193,12 +193,12 @@ class Query {
   /**
    * Creates a new `AggregateQuery` that performs the specified aggregations.
    *
-   * @param aggregations The aggregations to be performed by the created
+   * @param aggregateFields The aggregations to be performed by the created
    * `AggregateQuery`.
    *
    * @return The created `AggregateQuery`.
    */
-  AggregateQuery Aggregate(std::vector<AggregateField>&& aggregations) const;
+  AggregateQuery Aggregate(std::vector<AggregateField>&& aggregateFields) const;
 
   // TODO(b/280805906) Remove this count specific API after the c++ SDK migrates
   // to the new Aggregate API

+ 1 - 1
Firestore/core/src/remote/remote_objc_bridge.cc

@@ -278,7 +278,7 @@ DatastoreSerializer::EncodeAggregateQueryRequest(
     const std::vector<AggregateField>& aggregates,
     absl::flat_hash_map<std::string, std::string>& aliasMap) const {
   Message<google_firestore_v1_RunAggregationQueryRequest> result;
-  auto encodedTarget = serializer_.EncodeQueryTarget(query.ToTarget());
+  auto encodedTarget = serializer_.EncodeQueryTarget(query.ToAggregateTarget());
   result->parent = encodedTarget.parent;
   result->which_query_type =
       google_firestore_v1_RunAggregationQueryRequest_structured_aggregation_query_tag;  // NOLINT