Skip to content

Commit 4232ac1

Browse files
wu-huidconeybeehsannas
authored
Public count (#10254)
* Revert "Revert "Public count (#10246)" (#10252)" This reverts commit b695d99. * Firestore: Re-write API docs for COUNT API (#10258) * Add isEqual and hash for aggregate classes (#10261) Co-authored-by: Denver Coneybeare <[email protected]> Co-authored-by: Ehsan <[email protected]>
1 parent 7ad2380 commit 4232ac1

File tree

13 files changed

+284
-70
lines changed

13 files changed

+284
-70
lines changed

Firestore/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# Unreleased
2+
- [feature] Added `Query.count()`, which fetches the number of documents in the
3+
result set without actually downloading the documents (#10246).
4+
15
# 10.0.0
26
- [fixed] Fixed compiler warning about `@param comparator` (#10226).
37

Firestore/Example/Tests/Integration/API/FIRCountTests.mm

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,47 @@
1919
#import <XCTest/XCTest.h>
2020

2121
#import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h"
22-
#import "Firestore/Source/API/FIRAggregateQuery+Internal.h"
23-
#import "Firestore/Source/API/FIRAggregateQuerySnapshot+Internal.h"
24-
#import "Firestore/Source/API/FIRQuery+Internal.h"
22+
#import "Firestore/Source/Public/FirebaseFirestore/FIRAggregateQuery.h"
23+
#import "Firestore/Source/Public/FirebaseFirestore/FIRAggregateQuerySnapshot.h"
24+
#import "Firestore/Source/Public/FirebaseFirestore/FIRAggregateSource.h"
2525

2626
@interface FIRCountTests : FSTIntegrationTestCase
2727
@end
2828

2929
@implementation FIRCountTests
3030

31+
- (void)testAggregateQueryEquals {
32+
FIRCollectionReference* coll1 = [self collectionRefWithDocuments:@{}];
33+
FIRCollectionReference* coll1Same = [[coll1 firestore] collectionWithPath:[coll1 path]];
34+
FIRAggregateQuery* query1 = [coll1 count];
35+
FIRAggregateQuery* query1Same = [coll1Same count];
36+
37+
FIRCollectionReference* sub = [[coll1 documentWithPath:@"bar"] collectionWithPath:@"baz"];
38+
FIRAggregateQuery* query2 = [[[sub queryWhereField:@"a" isEqualTo:@1] queryLimitedTo:100] count];
39+
FIRAggregateQuery* query2Same = [[[sub queryWhereField:@"a"
40+
isEqualTo:@1] queryLimitedTo:100] count];
41+
FIRAggregateQuery* query3 = [[[sub queryWhereField:@"b"
42+
isEqualTo:@1] queryOrderedByField:@"c"] count];
43+
FIRAggregateQuery* query3Same = [[[sub queryWhereField:@"b"
44+
isEqualTo:@1] queryOrderedByField:@"c"] count];
45+
46+
XCTAssertEqualObjects(query1, query1Same);
47+
XCTAssertEqualObjects(query2, query2Same);
48+
XCTAssertEqualObjects(query3, query3Same);
49+
50+
XCTAssertEqual([query1 hash], [query1Same hash]);
51+
XCTAssertEqual([query2 hash], [query2Same hash]);
52+
XCTAssertEqual([query3 hash], [query3Same hash]);
53+
54+
XCTAssertFalse([query1 isEqual:nil]);
55+
XCTAssertFalse([query1 isEqual:@"string"]);
56+
XCTAssertFalse([query1 isEqual:query2]);
57+
XCTAssertFalse([query2 isEqual:query3]);
58+
59+
XCTAssertNotEqual([query1 hash], [query2 hash]);
60+
XCTAssertNotEqual([query2 hash], [query3 hash]);
61+
}
62+
3163
- (void)testCanRunCountQuery {
3264
// TODO(b/246758022): Remove this (and below) once COUNT is release for the backend.
3365
if (![FSTIntegrationTestCase isRunningAgainstEmulator]) {
@@ -77,6 +109,48 @@ - (void)testCanRunCountWithOrderBys {
77109
XCTAssertEqual(snapshot.count, [NSNumber numberWithLong:3L]);
78110
}
79111

112+
- (void)testSnapshotEquals {
113+
if (![FSTIntegrationTestCase isRunningAgainstEmulator]) {
114+
return;
115+
}
116+
117+
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
118+
@"a" : @{@"k" : @"a"},
119+
@"b" : @{@"k" : @"b"},
120+
@"c" : @{@"k" : @"c"}
121+
}];
122+
123+
FIRAggregateQuerySnapshot* snapshot1 =
124+
[self readSnapshotForAggregate:[[testCollection queryWhereField:@"k" isEqualTo:@"b"] count]];
125+
FIRAggregateQuerySnapshot* snapshot1Same =
126+
[self readSnapshotForAggregate:[[testCollection queryWhereField:@"k" isEqualTo:@"b"] count]];
127+
128+
FIRAggregateQuerySnapshot* snapshot2 =
129+
[self readSnapshotForAggregate:[[testCollection queryWhereField:@"k" isEqualTo:@"a"] count]];
130+
[self writeDocumentRef:[testCollection documentWithPath:@"d"] data:@{@"k" : @"a"}];
131+
FIRAggregateQuerySnapshot* snapshot2Different =
132+
[self readSnapshotForAggregate:[[testCollection queryWhereField:@"k" isEqualTo:@"a"] count]];
133+
134+
FIRAggregateQuerySnapshot* snapshot3 =
135+
[self readSnapshotForAggregate:[[testCollection queryWhereField:@"k" isEqualTo:@"b"] count]];
136+
FIRAggregateQuerySnapshot* snapshot3Different =
137+
[self readSnapshotForAggregate:[[testCollection queryWhereField:@"k" isEqualTo:@"c"] count]];
138+
139+
XCTAssertEqualObjects(snapshot1, snapshot1Same);
140+
XCTAssertEqual([snapshot1 hash], [snapshot1Same hash]);
141+
XCTAssertEqualObjects([snapshot1 query], [[testCollection queryWhereField:@"k"
142+
isEqualTo:@"b"] count]);
143+
144+
XCTAssertNotEqualObjects(snapshot1, nil);
145+
XCTAssertNotEqualObjects(snapshot1, @"string");
146+
XCTAssertNotEqualObjects(snapshot1, snapshot2);
147+
XCTAssertNotEqual([snapshot1 hash], [snapshot2 hash]);
148+
XCTAssertNotEqualObjects(snapshot2, snapshot2Different);
149+
XCTAssertNotEqual([snapshot2 hash], [snapshot2Different hash]);
150+
XCTAssertNotEqualObjects(snapshot3, snapshot3Different);
151+
XCTAssertNotEqual([snapshot3 hash], [snapshot3Different hash]);
152+
}
153+
80154
- (void)testTerminateDoesNotCrashWithFlyingCountQuery {
81155
if (![FSTIntegrationTestCase isRunningAgainstEmulator]) {
82156
return;

Firestore/Source/API/FIRAggregateQuery+Internal.h

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,16 @@
1414
* limitations under the License.
1515
*/
1616

17-
// TODO(b/246760853): Move FIRAggregateQuery to public headers to release it.
18-
19-
#import "FIRAggregateSource+Internal.h"
17+
#import "FIRAggregateQuery.h"
2018
#import "FIRQuery.h"
2119

22-
@class FIRAggregateQuerySnapshot;
23-
24-
/**
25-
* An `AggregateQuery` computes some aggregation statistics from the result set of a base
26-
* `Query`.
27-
*/
28-
NS_SWIFT_NAME(AggregateQuery)
29-
@interface FIRAggregateQuery : NSObject
20+
NS_ASSUME_NONNULL_BEGIN
3021

31-
- (instancetype _Nonnull)init NS_UNAVAILABLE;
32-
- (instancetype _Nonnull)initWithQuery:(FIRQuery *_Nonnull)query NS_DESIGNATED_INITIALIZER;
22+
@interface FIRAggregateQuery (/* init */)
3323

34-
/** The base `Query` for this aggregate query. */
35-
@property(nonatomic, readonly) FIRQuery *_Nonnull query;
24+
- (instancetype)init NS_UNAVAILABLE;
25+
- (instancetype)initWithQuery:(FIRQuery *)query NS_DESIGNATED_INITIALIZER;
3626

37-
/**
38-
* Executes the aggregate query and reads back the results as a `FIRAggregateQuerySnapshot`.
39-
*
40-
* @param source indicates where the results should be fetched from.
41-
* @param completion a block to execute once the results have been successfully read.
42-
* snapshot will be `nil` only if error is `non-nil`.
43-
*/
44-
- (void)aggregationWithSource:(FIRAggregateSource)source
45-
completion:(void (^_Nonnull)(FIRAggregateQuerySnapshot *_Nullable snapshot,
46-
NSError *_Nullable error))completion
47-
NS_SWIFT_NAME(aggregation(source:completion:));
4827
@end
28+
29+
NS_ASSUME_NONNULL_END

Firestore/Source/API/FIRAggregateQuery.mm

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,39 @@
2424
#include "Firestore/core/src/util/error_apple.h"
2525
#include "Firestore/core/src/util/statusor.h"
2626

27+
NS_ASSUME_NONNULL_BEGIN
28+
29+
#pragma mark - FIRAggregateQuery
30+
2731
@implementation FIRAggregateQuery {
2832
FIRQuery *_query;
2933
std::unique_ptr<api::AggregateQuery> _aggregation;
3034
}
3135

32-
- (instancetype _Nonnull)initWithQuery:(FIRQuery *)query {
36+
- (instancetype)initWithQuery:(FIRQuery *)query {
3337
if (self = [super init]) {
3438
_query = query;
3539
_aggregation = absl::make_unique<api::AggregateQuery>(query.apiQuery.Count());
3640
}
3741
return self;
3842
}
3943

44+
#pragma mark - NSObject Methods
45+
46+
- (BOOL)isEqual:(nullable id)other {
47+
if (other == self) return YES;
48+
if (![[other class] isEqual:[self class]]) return NO;
49+
50+
auto otherQuery = static_cast<FIRAggregateQuery *>(other);
51+
return [_query isEqual:otherQuery->_query];
52+
}
53+
54+
- (NSUInteger)hash {
55+
return [_query hash];
56+
}
57+
58+
#pragma mark - Public Methods
59+
4060
- (FIRQuery *)query {
4161
return _query;
4262
}
@@ -46,7 +66,7 @@ - (void)aggregationWithSource:(FIRAggregateSource)source
4666
NSError *_Nullable error))completion {
4767
_aggregation->Get([self, completion](const firebase::firestore::util::StatusOr<int64_t> &result) {
4868
if (result.ok()) {
49-
completion([[FIRAggregateQuerySnapshot alloc] initWithCount:result.ValueOrDie() Query:self],
69+
completion([[FIRAggregateQuerySnapshot alloc] initWithCount:result.ValueOrDie() query:self],
5070
nil);
5171
} else {
5272
completion(nil, MakeNSError(result.status()));
@@ -55,3 +75,5 @@ - (void)aggregationWithSource:(FIRAggregateSource)source
5575
}
5676

5777
@end
78+
79+
NS_ASSUME_NONNULL_END

Firestore/Source/API/FIRAggregateQuerySnapshot+Internal.h

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,18 @@
1414
* limitations under the License.
1515
*/
1616

17-
// TODO(b/246760853): Move FIRAggregateQuerySnapshot to public headers to release it.
18-
19-
#import "FIRAggregateQuery+Internal.h"
17+
#import "FIRAggregateQuerySnapshot.h"
2018

2119
@class FIRAggregateQuery;
2220

23-
/**
24-
* An `AggregateQuerySnapshot` contains results of a `AggregateQuery`.
25-
*/
26-
NS_SWIFT_NAME(AggregateQuerySnapshot)
27-
@interface FIRAggregateQuerySnapshot : NSObject
21+
NS_ASSUME_NONNULL_BEGIN
2822

29-
- (instancetype _Nonnull)init NS_UNAVAILABLE;
30-
- (instancetype _Nonnull)initWithCount:(int64_t)result
31-
Query:(FIRAggregateQuery* _Nonnull)query NS_DESIGNATED_INITIALIZER;
23+
@interface FIRAggregateQuerySnapshot (/* init */)
3224

33-
/** The original `AggregateQuery` this snapshot is a result of. */
34-
@property(nonatomic, readonly) FIRAggregateQuery* _Nonnull query;
35-
36-
/**
37-
* The result of a document count aggregation. Null if no count aggregation is
38-
* available in the result.
39-
*/
40-
@property(nonatomic, readonly) NSNumber* _Nullable count;
25+
- (instancetype)init NS_UNAVAILABLE;
26+
- (instancetype)initWithCount:(int64_t)result
27+
query:(FIRAggregateQuery *)query NS_DESIGNATED_INITIALIZER;
4128

4229
@end
30+
31+
NS_ASSUME_NONNULL_END

Firestore/Source/API/FIRAggregateQuerySnapshot.mm

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,41 @@
1616

1717
#import "FIRAggregateQuerySnapshot+Internal.h"
1818

19+
#import "FIRAggregateQuery.h"
20+
21+
NS_ASSUME_NONNULL_BEGIN
22+
1923
@implementation FIRAggregateQuerySnapshot {
2024
int64_t _result;
2125
FIRAggregateQuery* _query;
2226
}
2327

24-
- (instancetype _Nonnull)initWithCount:(int64_t)count Query:(FIRAggregateQuery*)query {
28+
- (instancetype)initWithCount:(int64_t)count query:(FIRAggregateQuery*)query {
2529
if (self = [super init]) {
2630
_result = count;
2731
_query = query;
2832
}
2933
return self;
3034
}
3135

36+
#pragma mark - NSObject Methods
37+
38+
- (BOOL)isEqual:(nullable id)other {
39+
if (other == self) return YES;
40+
if (![[other class] isEqual:[self class]]) return NO;
41+
42+
auto otherSnap = static_cast<FIRAggregateQuerySnapshot*>(other);
43+
return _result == otherSnap->_result && [_query isEqual:otherSnap->_query];
44+
}
45+
46+
- (NSUInteger)hash {
47+
NSUInteger result = [_query hash];
48+
result = 31 * result + [[self count] hash];
49+
return result;
50+
}
51+
52+
#pragma mark - Public Methods
53+
3254
- (NSNumber*)count {
3355
return [NSNumber numberWithLongLong:_result];
3456
}
@@ -38,3 +60,5 @@ - (FIRAggregateQuery*)query {
3860
}
3961

4062
@end
63+
64+
NS_ASSUME_NONNULL_END

Firestore/Source/API/FIRQuery+Internal.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
* limitations under the License.
1515
*/
1616

17-
#import "FIRAggregateQuery+Internal.h"
1817
#import "FIRQuery.h"
1918

2019
#include <memory>
@@ -48,12 +47,6 @@ NS_ASSUME_NONNULL_BEGIN
4847
// TODO(orquery): This method will become public API. Change visibility and add documentation.
4948
- (FIRQuery *)queryWhereFilter:(FIRFilter *)filter;
5049

51-
// TODO(b/246760853): This property will become public API.
52-
/**
53-
* An `AggregateQuery` counting the number of documents matching this query.
54-
*/
55-
@property(nonatomic, readonly) FIRAggregateQuery *count;
56-
5750
@end
5851

5952
NS_ASSUME_NONNULL_END

Firestore/Source/API/FIRQuery.mm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <utility>
2121
#include <vector>
2222

23+
#import "FIRAggregateQuery+Internal.h"
2324
#import "FIRDocumentReference.h"
2425
#import "FIRFirestoreErrors.h"
2526
#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2022 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <Foundation/Foundation.h>
18+
19+
#import "FIRAggregateSource.h"
20+
21+
NS_ASSUME_NONNULL_BEGIN
22+
23+
@class FIRQuery;
24+
@class FIRAggregateQuerySnapshot;
25+
26+
/**
27+
* A query that calculates aggregations over an underlying query.
28+
*/
29+
NS_SWIFT_NAME(AggregateQuery)
30+
@interface FIRAggregateQuery : NSObject
31+
32+
/** :nodoc: */
33+
- (instancetype)init __attribute__((unavailable("FIRAggregateQuery cannot be created directly.")));
34+
35+
/** The query whose aggregations will be calculated by this object. */
36+
@property(nonatomic, readonly) FIRQuery *query;
37+
38+
/**
39+
* Executes this query.
40+
*
41+
* @param source The source from which to acquire the aggregate results.
42+
* @param completion a block to execute once the results have been successfully read.
43+
* snapshot will be `nil` only if error is `non-nil`.
44+
*/
45+
- (void)aggregationWithSource:(FIRAggregateSource)source
46+
completion:(void (^)(FIRAggregateQuerySnapshot *_Nullable snapshot,
47+
NSError *_Nullable error))completion
48+
NS_SWIFT_NAME(getAggregation(source:completion:));
49+
@end
50+
51+
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)