From 1c40e7aada6b32bbc621f06fb5f380149606a58d Mon Sep 17 00:00:00 2001 From: zxu Date: Mon, 5 Mar 2018 11:30:59 -0500 Subject: add converters and port paths to FSTQuery (#869) * add converters and fix FSTQuery.{h,m} only * address changes * a change forget to address * add a dummy function to make inline-only-library buildable --- Firestore/Example/Tests/Core/FSTQueryTests.mm | 39 ++-- Firestore/Example/Tests/Core/FSTViewTests.mm | 24 ++- Firestore/Example/Tests/Model/FSTPathTests.mm | 24 +++ .../Example/Tests/Remote/FSTSerializerBetaTests.mm | 6 +- Firestore/Example/Tests/Util/FSTHelpers.mm | 15 +- Firestore/Source/API/FIRCollectionReference.mm | 31 ++- Firestore/Source/API/FIRDocumentReference.mm | 2 +- Firestore/Source/API/FIRQuery.mm | 52 +++-- Firestore/Source/Core/FSTQuery.h | 41 ++-- Firestore/Source/Core/FSTQuery.mm | 221 +++++++++++++-------- Firestore/Source/Core/FSTSyncEngine.mm | 3 +- Firestore/Source/Local/FSTLevelDBMutationQueue.mm | 2 +- .../Source/Local/FSTLevelDBRemoteDocumentCache.mm | 5 +- Firestore/Source/Local/FSTLocalDocumentsView.mm | 6 +- Firestore/Source/Local/FSTMemoryMutationQueue.mm | 4 +- .../Source/Local/FSTMemoryRemoteDocumentCache.mm | 5 +- Firestore/Source/Model/FSTPath.h | 26 +++ Firestore/Source/Model/FSTPath.mm | 43 ++++ Firestore/Source/Remote/FSTRemoteStore.mm | 4 +- Firestore/Source/Remote/FSTSerializerBeta.mm | 31 +-- .../core/src/firebase/firestore/model/base_path.h | 13 ++ .../firebase/firestore/testutil/CMakeLists.txt | 14 +- .../test/firebase/firestore/testutil/testutil.cc | 28 +++ .../test/firebase/firestore/testutil/testutil.h | 41 ++++ 24 files changed, 485 insertions(+), 195 deletions(-) create mode 100644 Firestore/core/test/firebase/firestore/testutil/testutil.cc create mode 100644 Firestore/core/test/firebase/firestore/testutil/testutil.h (limited to 'Firestore') diff --git a/Firestore/Example/Tests/Core/FSTQueryTests.mm b/Firestore/Example/Tests/Core/FSTQueryTests.mm index c0b2cd9..5f6d279 100644 --- a/Firestore/Example/Tests/Core/FSTQueryTests.mm +++ b/Firestore/Example/Tests/Core/FSTQueryTests.mm @@ -26,10 +26,16 @@ #import "Firestore/Example/Tests/Util/FSTHelpers.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" +#include "Firestore/core/test/firebase/firestore/testutil/testutil.h" +namespace testutil = firebase::firestore::testutil; namespace util = firebase::firestore::util; using firebase::firestore::model::DatabaseId; +using firebase::firestore::model::FieldPath; +using firebase::firestore::model::ResourcePath; NS_ASSUME_NONNULL_BEGIN @@ -41,8 +47,10 @@ NS_ASSUME_NONNULL_BEGIN @implementation FSTQuery (Tests) - (FSTQuery *)queryByAddingSortBy:(NSString *)key ascending:(BOOL)ascending { - return [self queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(key) - ascending:ascending]]; + return [self + queryByAddingSortOrder:[FSTSortOrder + sortOrderWithFieldPath:testutil::Field(util::MakeStringView(key)) + ascending:ascending]]; } @end @@ -55,11 +63,11 @@ NS_ASSUME_NONNULL_BEGIN - (void)testConstructor { FSTResourcePath *path = [FSTResourcePath pathWithSegments:@[ @"rooms", @"Firestore", @"messages", @"0001" ]]; - FSTQuery *query = [FSTQuery queryWithPath:path]; + FSTQuery *query = [FSTQuery queryWithPath:[path toCPPResourcePath]]; XCTAssertNotNil(query); XCTAssertEqual(query.sortOrders.count, 1); - XCTAssertEqualObjects(query.sortOrders[0].field.canonicalString, kDocumentKeyPath); + XCTAssertEqual(query.sortOrders[0].field.CanonicalString(), FieldPath::kDocumentKeyPath); XCTAssertEqual(query.sortOrders[0].ascending, YES); XCTAssertEqual(query.explicitSortOrders.count, 0); @@ -68,17 +76,17 @@ NS_ASSUME_NONNULL_BEGIN - (void)testOrderBy { FSTQuery *query = FSTTestQuery(@"rooms/Firestore/messages"); query = - [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"length") + [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:testutil::Field("length") ascending:NO]]; XCTAssertEqual(query.sortOrders.count, 2); - XCTAssertEqualObjects(query.sortOrders[0].field.canonicalString, @"length"); + XCTAssertEqual(query.sortOrders[0].field.CanonicalString(), "length"); XCTAssertEqual(query.sortOrders[0].ascending, NO); - XCTAssertEqualObjects(query.sortOrders[1].field.canonicalString, kDocumentKeyPath); + XCTAssertEqual(query.sortOrders[1].field.CanonicalString(), FieldPath::kDocumentKeyPath); XCTAssertEqual(query.sortOrders[1].ascending, NO); XCTAssertEqual(query.explicitSortOrders.count, 1); - XCTAssertEqualObjects(query.explicitSortOrders[0].field.canonicalString, @"length"); + XCTAssertEqual(query.explicitSortOrders[0].field.CanonicalString(), "length"); XCTAssertEqual(query.explicitSortOrders[0].ascending, NO); } @@ -211,7 +219,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)testDoesntRemoveComplexObjectsWithOrderBy { FSTQuery *query1 = [FSTTestQuery(@"collection") - queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"sort") + queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:testutil::Field("sort") ascending:YES]]; FSTDocument *doc1 = FSTTestDoc(@"collection/1", 0, @{ @"sort" : @2 }, NO); @@ -305,9 +313,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)testSortsDocumentsInTheCorrectOrder { FSTQuery *query = FSTTestQuery(@"collection"); - query = - [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"sort") - ascending:YES]]; + query = [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:testutil::Field("sort") + ascending:YES]]; // clang-format off NSArray *docs = @[ @@ -335,10 +342,10 @@ NS_ASSUME_NONNULL_BEGIN - (void)testSortsDocumentsUsingMultipleFields { FSTQuery *query = FSTTestQuery(@"collection"); query = - [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"sort1") + [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:testutil::Field("sort1") ascending:YES]]; query = - [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"sort2") + [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:testutil::Field("sort2") ascending:YES]]; // clang-format off @@ -362,10 +369,10 @@ NS_ASSUME_NONNULL_BEGIN - (void)testSortsDocumentsWithDescendingToo { FSTQuery *query = FSTTestQuery(@"collection"); query = - [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"sort1") + [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:testutil::Field("sort1") ascending:NO]]; query = - [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"sort2") + [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:testutil::Field("sort2") ascending:NO]]; // clang-format off diff --git a/Firestore/Example/Tests/Core/FSTViewTests.mm b/Firestore/Example/Tests/Core/FSTViewTests.mm index e6c4510..7df8195 100644 --- a/Firestore/Example/Tests/Core/FSTViewTests.mm +++ b/Firestore/Example/Tests/Core/FSTViewTests.mm @@ -30,6 +30,12 @@ #import "Firestore/Example/Tests/Util/FSTHelpers.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "Firestore/core/test/firebase/firestore/testutil/testutil.h" + +namespace testutil = firebase::firestore::testutil; +using firebase::firestore::model::ResourcePath; + NS_ASSUME_NONNULL_BEGIN @interface FSTViewTests : XCTestCase @@ -39,8 +45,7 @@ NS_ASSUME_NONNULL_BEGIN /** Returns a new empty query to use for testing. */ - (FSTQuery *)queryForMessages { - return [FSTQuery - queryWithPath:[FSTResourcePath pathWithSegments:@[ @"rooms", @"eros", @"messages" ]]]; + return [FSTQuery queryWithPath:ResourcePath{"rooms", "eros", "messages"}]; } - (void)testAddsDocumentsBasedOnQuery { @@ -128,7 +133,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)testFiltersDocumentsBasedOnQueryWithFilter { FSTQuery *query = [self queryForMessages]; FSTRelationFilter *filter = - [FSTRelationFilter filterWithField:FSTTestFieldPath(@"sort") + [FSTRelationFilter filterWithField:testutil::Field("sort") filterOperator:FSTRelationFilterOperatorLessThanOrEqual value:[FSTDoubleValue doubleValue:2]]; query = [query queryByAddingFilter:filter]; @@ -160,7 +165,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)testUpdatesDocumentsBasedOnQueryWithFilter { FSTQuery *query = [self queryForMessages]; FSTRelationFilter *filter = - [FSTRelationFilter filterWithField:FSTTestFieldPath(@"sort") + [FSTRelationFilter filterWithField:testutil::Field("sort") filterOperator:FSTRelationFilterOperatorLessThanOrEqual value:[FSTDoubleValue doubleValue:2]]; query = [query queryByAddingFilter:filter]; @@ -232,9 +237,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)testDoesntReportChangesForDocumentBeyondLimitOfQuery { FSTQuery *query = [self queryForMessages]; - query = - [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"num") - ascending:YES]]; + query = [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:testutil::Field("num") + ascending:YES]]; query = [query queryBySettingLimit:2]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:[FSTDocumentKeySet keySet]]; @@ -385,7 +389,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)testReturnsNeedsRefillOnReorderInLimitQuery { FSTQuery *query = [self queryForMessages]; query = - [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"order") + [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:testutil::Field("order") ascending:YES]]; query = [query queryBySettingLimit:2]; FSTDocument *doc1 = FSTTestDoc(@"rooms/eros/messages/0", 0, @{ @"order" : @1 }, NO); @@ -419,7 +423,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)testDoesntNeedRefillOnReorderWithinLimit { FSTQuery *query = [self queryForMessages]; query = - [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"order") + [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:testutil::Field("order") ascending:YES]]; query = [query queryBySettingLimit:3]; FSTDocument *doc1 = FSTTestDoc(@"rooms/eros/messages/0", 0, @{ @"order" : @1 }, NO); @@ -449,7 +453,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)testDoesntNeedRefillOnReorderAfterLimitQuery { FSTQuery *query = [self queryForMessages]; query = - [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"order") + [query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:testutil::Field("order") ascending:YES]]; query = [query queryBySettingLimit:3]; FSTDocument *doc1 = FSTTestDoc(@"rooms/eros/messages/0", 0, @{ @"order" : @1 }, NO); diff --git a/Firestore/Example/Tests/Model/FSTPathTests.mm b/Firestore/Example/Tests/Model/FSTPathTests.mm index 68bcc44..388c5c3 100644 --- a/Firestore/Example/Tests/Model/FSTPathTests.mm +++ b/Firestore/Example/Tests/Model/FSTPathTests.mm @@ -195,6 +195,30 @@ NS_ASSUME_NONNULL_BEGIN XCTAssertEqualObjects([[pathHead pathByRemovingFirstSegment] canonicalString], @"bar"); } +- (void)testRoundTrip { + FSTFieldPath *path = [FSTFieldPath pathWithSegments:@[ @"rooms", @"Eros", @"messages" ]]; + XCTAssertEqualObjects(path, [FSTFieldPath fieldPathWithCPPFieldPath:[path toCPPFieldPath]]); + + const firebase::firestore::model::FieldPath cppPath{"rooms", "Eros", "messages"}; + XCTAssertEqual(cppPath, [[FSTFieldPath fieldPathWithCPPFieldPath:cppPath] toCPPFieldPath]); +} + +@end + +@interface FSTResourcePathTests : XCTestCase @end +@implementation FSTResourcePathTests + +- (void)testRoundTrip { + FSTResourcePath *path = [FSTResourcePath pathWithSegments:@[ @"rooms", @"Eros", @"messages" ]]; + XCTAssertEqualObjects(path, + [FSTResourcePath resourcePathWithCPPResourcePath:[path toCPPResourcePath]]); + + const firebase::firestore::model::ResourcePath cppPath{"rooms", "Eros", "messages"}; + XCTAssertEqual(cppPath, + [[FSTResourcePath resourcePathWithCPPResourcePath:cppPath] toCPPResourcePath]); +} + +@end NS_ASSUME_NONNULL_END diff --git a/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm b/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm index fc4060b..5cd816f 100644 --- a/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm +++ b/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm @@ -48,7 +48,9 @@ #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" +#include "Firestore/core/test/firebase/firestore/testutil/testutil.h" +namespace testutil = firebase::firestore::testutil; namespace util = firebase::firestore::util; using firebase::firestore::model::DatabaseId; @@ -581,7 +583,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)testEncodesSortOrders { FSTQuery *q = [FSTTestQuery(@"docs") - queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"prop") + queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:testutil::Field("prop") ascending:YES]]; FSTQueryData *model = [self queryDataForQuery:q]; @@ -601,7 +603,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)testEncodesSortOrdersDescending { FSTQuery *q = [FSTTestQuery(@"rooms/1/messages/10/attachments") - queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"prop") + queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:testutil::Field("prop") ascending:NO]]; FSTQueryData *model = [self queryDataForQuery:q]; diff --git a/Firestore/Example/Tests/Util/FSTHelpers.mm b/Firestore/Example/Tests/Util/FSTHelpers.mm index 649486a..9b05604 100644 --- a/Firestore/Example/Tests/Util/FSTHelpers.mm +++ b/Firestore/Example/Tests/Util/FSTHelpers.mm @@ -183,7 +183,7 @@ FSTDocumentKeyReference *FSTTestRef(const absl::string_view projectID, } FSTQuery *FSTTestQuery(NSString *path) { - return [FSTQuery queryWithPath:FSTTestPath(path)]; + return [FSTQuery queryWithPath:[FSTTestPath(path) toCPPResourcePath]]; } id FSTTestFilter(NSString *field, NSString *opString, id value) { @@ -206,12 +206,12 @@ id FSTTestFilter(NSString *field, NSString *opString, id value) { FSTFieldValue *data = FSTTestFieldValue(value); if ([data isEqual:[FSTDoubleValue nanValue]]) { FSTCAssert(op == FSTRelationFilterOperatorEqual, @"Must use == with NAN."); - return [[FSTNanFilter alloc] initWithField:path]; + return [[FSTNanFilter alloc] initWithField:[path toCPPFieldPath]]; } else if ([data isEqual:[FSTNullValue nullValue]]) { FSTCAssert(op == FSTRelationFilterOperatorEqual, @"Must use == with Null."); - return [[FSTNullFilter alloc] initWithField:path]; + return [[FSTNullFilter alloc] initWithField:[path toCPPFieldPath]]; } else { - return [FSTRelationFilter filterWithField:path filterOperator:op value:data]; + return [FSTRelationFilter filterWithField:[path toCPPFieldPath] filterOperator:op value:data]; } } @@ -225,13 +225,14 @@ FSTSortOrder *FSTTestOrderBy(NSString *field, NSString *direction) { } else { FSTCFail(@"Unsupported direction: %@", direction); } - return [FSTSortOrder sortOrderWithFieldPath:path ascending:ascending]; + return [FSTSortOrder sortOrderWithFieldPath:[path toCPPFieldPath] ascending:ascending]; } NSComparator FSTTestDocComparator(NSString *fieldPath) { FSTQuery *query = [FSTTestQuery(@"docs") - queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(fieldPath) - ascending:YES]]; + queryByAddingSortOrder:[FSTSortOrder + sortOrderWithFieldPath:[FSTTestFieldPath(fieldPath) toCPPFieldPath] + ascending:YES]]; return [query comparator]; } diff --git a/Firestore/Source/API/FIRCollectionReference.mm b/Firestore/Source/API/FIRCollectionReference.mm index a8de29b..cb7b61a 100644 --- a/Firestore/Source/API/FIRCollectionReference.mm +++ b/Firestore/Source/API/FIRCollectionReference.mm @@ -28,6 +28,11 @@ #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::model::ResourcePath; using firebase::firestore::util::CreateAutoId; NS_ASSUME_NONNULL_BEGIN @@ -57,7 +62,8 @@ NS_ASSUME_NONNULL_BEGIN "number of segments, but %@ has %d", path.canonicalString, path.length); } - self = [super initWithQuery:[FSTQuery queryWithPath:path] firestore:firestore]; + self = + [super initWithQuery:[FSTQuery queryWithPath:[path toCPPResourcePath]] firestore:firestore]; return self; } @@ -87,30 +93,33 @@ NS_ASSUME_NONNULL_BEGIN } - (NSString *)collectionID { - return [self.query.path lastSegment]; + return util::WrapNSString(self.query.path.last_segment()); } - (FIRDocumentReference *_Nullable)parent { - FSTResourcePath *parentPath = [self.query.path pathByRemovingLastSegment]; - if (parentPath.isEmpty) { + const ResourcePath parentPath = self.query.path.PopLast(); + if (parentPath.empty()) { return nil; } else { - FSTDocumentKey *key = [FSTDocumentKey keyWithPath:parentPath]; + FSTDocumentKey *key = + [FSTDocumentKey keyWithPath:[FSTResourcePath resourcePathWithCPPResourcePath:parentPath]]; return [FIRDocumentReference referenceWithKey:key firestore:self.firestore]; } } - (NSString *)path { - return [self.query.path canonicalString]; + return util::WrapNSString(self.query.path.CanonicalString()); } - (FIRDocumentReference *)documentWithPath:(NSString *)documentPath { if (!documentPath) { FSTThrowInvalidArgument(@"Document path cannot be nil."); } - FSTResourcePath *subPath = [FSTResourcePath pathWithString:documentPath]; - FSTResourcePath *path = [self.query.path pathByAppendingPath:subPath]; - return [FIRDocumentReference referenceWithPath:path firestore:self.firestore]; + const ResourcePath subPath = ResourcePath::FromString(util::MakeStringView(documentPath)); + const ResourcePath path = self.query.path.Append(subPath); + return + [FIRDocumentReference referenceWithPath:[FSTResourcePath resourcePathWithCPPResourcePath:path] + firestore:self.firestore]; } - (FIRDocumentReference *)addDocumentWithData:(NSDictionary *)data { @@ -126,9 +135,9 @@ NS_ASSUME_NONNULL_BEGIN } - (FIRDocumentReference *)documentWithAutoID { - NSString *autoID = [NSString stringWithUTF8String:CreateAutoId().c_str()]; + const ResourcePath path = self.query.path.Append(CreateAutoId()); FSTDocumentKey *key = - [FSTDocumentKey keyWithPath:[self.query.path pathByAppendingSegment:autoID]]; + [FSTDocumentKey keyWithPath:[FSTResourcePath resourcePathWithCPPResourcePath:path]]; return [FIRDocumentReference referenceWithKey:key firestore:self.firestore]; } diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm index 05253f7..b1a5d49 100644 --- a/Firestore/Source/API/FIRDocumentReference.mm +++ b/Firestore/Source/API/FIRDocumentReference.mm @@ -266,7 +266,7 @@ NS_ASSUME_NONNULL_BEGIN addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions listener:(FIRDocumentSnapshotBlock)listener { FIRFirestore *firestore = self.firestore; - FSTQuery *query = [FSTQuery queryWithPath:self.key.path]; + FSTQuery *query = [FSTQuery queryWithPath:[self.key.path toCPPResourcePath]]; FSTDocumentKey *key = self.key; FSTViewSnapshotHandler snapshotHandler = ^(FSTViewSnapshot *snapshot, NSError *error) { diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index 1bbf91e..45ee482 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -38,6 +38,14 @@ #import "Firestore/Source/Util/FSTAsyncQueryListener.h" #import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::model::FieldPath; +using firebase::firestore::model::ResourcePath; + NS_ASSUME_NONNULL_BEGIN @interface FIRQueryListenOptions () @@ -371,7 +379,8 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions @"Invalid query. You must not specify an ending point before specifying the order by."); } FSTSortOrder *sortOrder = - [FSTSortOrder sortOrderWithFieldPath:fieldPath.internalValue ascending:!descending]; + [FSTSortOrder sortOrderWithFieldPath:[fieldPath.internalValue toCPPFieldPath] + ascending:!descending]; return [FIRQuery referenceWithQuery:[self.query queryByAddingSortOrder:sortOrder] firestore:self.firestore]; } @@ -460,7 +469,8 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions @"Invalid query. When querying by document ID you must provide " "a valid document ID, but it was an empty string."); } - FSTResourcePath *path = [self.query.path pathByAppendingSegment:documentKey]; + FSTResourcePath *path = [[FSTResourcePath resourcePathWithCPPResourcePath:self.query.path] + pathByAppendingSegment:documentKey]; fieldValue = [FSTReferenceValue referenceValue:[FSTDocumentKey keyWithPath:path] databaseID:self.firestore.databaseID]; } else if ([value isKindOfClass:[FIRDocumentReference class]]) { @@ -483,15 +493,15 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions @"Invalid Query. You can only perform equality comparisons on nil / " "NSNull."); } - filter = [[FSTNullFilter alloc] initWithField:fieldPath]; + filter = [[FSTNullFilter alloc] initWithField:[fieldPath toCPPFieldPath]]; } else if ([fieldValue isEqual:[FSTDoubleValue nanValue]]) { if (filterOperator != FSTRelationFilterOperatorEqual) { FSTThrowInvalidUsage(@"InvalidQueryException", @"Invalid Query. You can only perform equality comparisons on NaN."); } - filter = [[FSTNanFilter alloc] initWithField:fieldPath]; + filter = [[FSTNanFilter alloc] initWithField:[fieldPath toCPPFieldPath]]; } else { - filter = [FSTRelationFilter filterWithField:fieldPath + filter = [FSTRelationFilter filterWithField:[fieldPath toCPPFieldPath] filterOperator:filterOperator value:fieldValue]; [self validateNewRelationFilter:filter]; @@ -502,19 +512,21 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions - (void)validateNewRelationFilter:(FSTRelationFilter *)filter { if ([filter isInequality]) { - FSTFieldPath *existingField = [self.query inequalityFilterField]; - if (existingField && ![existingField isEqual:filter.field]) { + const FieldPath *existingField = [self.query inequalityFilterField]; + if (existingField && *existingField != filter.field) { FSTThrowInvalidUsage( @"InvalidQueryException", @"Invalid Query. All where filters with an inequality " "(lessThan, lessThanOrEqual, greaterThan, or greaterThanOrEqual) must be on the same " "field. But you have inequality filters on '%@' and '%@'", - existingField, filter.field); + util::WrapNSStringNoCopy(existingField->CanonicalString()), + util::WrapNSStringNoCopy(filter.field.CanonicalString())); } - FSTFieldPath *firstOrderByField = [self.query firstSortOrderField]; + const FieldPath *firstOrderByField = [self.query firstSortOrderField]; if (firstOrderByField) { - [self validateOrderByField:firstOrderByField matchesInequalityField:filter.field]; + [self validateOrderByField:[FSTFieldPath fieldPathWithCPPFieldPath:*firstOrderByField] + matchesInequalityField:[FSTFieldPath fieldPathWithCPPFieldPath:filter.field]]; } } } @@ -522,9 +534,10 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions - (void)validateNewOrderByPath:(FSTFieldPath *)fieldPath { if (![self.query firstSortOrderField]) { // This is the first order by. It must match any inequality. - FSTFieldPath *inequalityField = [self.query inequalityFilterField]; + const FieldPath *inequalityField = [self.query inequalityFilterField]; if (inequalityField) { - [self validateOrderByField:fieldPath matchesInequalityField:inequalityField]; + [self validateOrderByField:fieldPath + matchesInequalityField:[FSTFieldPath fieldPathWithCPPFieldPath:*inequalityField]]; } } } @@ -565,11 +578,12 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions // continues/ends exactly at the provided document. Without the key (by using the explicit sort // orders), multiple documents could match the position, yielding duplicate results. for (FSTSortOrder *sortOrder in self.query.sortOrders) { - if ([sortOrder.field isEqual:[FSTFieldPath keyFieldPath]]) { + if (sortOrder.field == FieldPath::KeyFieldPath()) { [components addObject:[FSTReferenceValue referenceValue:document.key databaseID:self.firestore.databaseID]]; } else { - FSTFieldValue *value = [document fieldForPath:sortOrder.field]; + FSTFieldValue *value = + [document fieldForPath:[FSTFieldPath fieldPathWithCPPFieldPath:sortOrder.field]]; if (value != nil) { [components addObject:value]; } else { @@ -577,7 +591,7 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions @"Invalid query. You are trying to start or end a query using a " "document for which the field '%@' (used as the order by) " "does not exist.", - sortOrder.field.canonicalString); + util::WrapNSStringNoCopy(sortOrder.field.CanonicalString())); } } } @@ -597,7 +611,7 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions NSMutableArray *components = [NSMutableArray array]; [fieldValues enumerateObjectsUsingBlock:^(id rawValue, NSUInteger idx, BOOL *stop) { FSTSortOrder *sortOrder = explicitSortOrders[idx]; - if ([sortOrder.field isEqual:[FSTFieldPath keyFieldPath]]) { + if (sortOrder.field == FieldPath::KeyFieldPath()) { if (![rawValue isKindOfClass:[NSString class]]) { FSTThrowInvalidUsage(@"InvalidQueryException", @"Invalid query. Expected a string for the document ID."); @@ -607,8 +621,10 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions FSTThrowInvalidUsage(@"InvalidQueryException", @"Invalid query. Document ID '%@' contains a slash.", documentID); } - FSTDocumentKey *key = - [FSTDocumentKey keyWithPath:[self.query.path pathByAppendingSegment:documentID]]; + FSTDocumentKey *key = [FSTDocumentKey + keyWithPath:[FSTResourcePath + resourcePathWithCPPResourcePath:self.query.path.Append( + [documentID UTF8String])]]; [components addObject:[FSTReferenceValue referenceValue:key databaseID:self.firestore.databaseID]]; } else { diff --git a/Firestore/Source/Core/FSTQuery.h b/Firestore/Source/Core/FSTQuery.h index 0562ae4..8da0878 100644 --- a/Firestore/Source/Core/FSTQuery.h +++ b/Firestore/Source/Core/FSTQuery.h @@ -16,11 +16,12 @@ #import +#include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" + @class FSTDocument; @class FSTDocumentKey; -@class FSTFieldPath; @class FSTFieldValue; -@class FSTResourcePath; NS_ASSUME_NONNULL_BEGIN @@ -40,7 +41,7 @@ typedef NS_ENUM(NSInteger, FSTRelationFilterOperator) { @protocol FSTFilter /** Returns the field the Filter operates over. */ -- (FSTFieldPath *)field; +- (const firebase::firestore::model::FieldPath &)field; /** Returns true if a document matches the filter. */ - (BOOL)matchesDocument:(FSTDocument *)document; @@ -64,7 +65,7 @@ typedef NS_ENUM(NSInteger, FSTRelationFilterOperator) { * @param value A constant value to compare @a field to. The RHS of the expression. * @return A new instance of FSTRelationFilter. */ -+ (instancetype)filterWithField:(FSTFieldPath *)field ++ (instancetype)filterWithField:(firebase::firestore::model::FieldPath)field filterOperator:(FSTRelationFilterOperator)filterOperator value:(FSTFieldValue *)value; @@ -74,7 +75,7 @@ typedef NS_ENUM(NSInteger, FSTRelationFilterOperator) { - (BOOL)isInequality; /** The left hand side of the relation. A path into a document field. */ -@property(nonatomic, strong, readonly) FSTFieldPath *field; +- (const firebase::firestore::model::FieldPath &)field; /** The type of equality/inequality operator to use in the relation. */ @property(nonatomic, assign, readonly) FSTRelationFilterOperator filterOperator; @@ -87,32 +88,35 @@ typedef NS_ENUM(NSInteger, FSTRelationFilterOperator) { /** Filter that matches NULL values. */ @interface FSTNullFilter : NSObject - (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithField:(FSTFieldPath *)field NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithField:(firebase::firestore::model::FieldPath)field + NS_DESIGNATED_INITIALIZER; @end /** Filter that matches NAN values. */ @interface FSTNanFilter : NSObject - (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithField:(FSTFieldPath *)field NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithField:(firebase::firestore::model::FieldPath)field + NS_DESIGNATED_INITIALIZER; @end /** FSTSortOrder is a field and direction to order query results by. */ @interface FSTSortOrder : NSObject /** Creates a new sort order with the given field and direction. */ -+ (instancetype)sortOrderWithFieldPath:(FSTFieldPath *)fieldPath ascending:(BOOL)ascending; ++ (instancetype)sortOrderWithFieldPath:(firebase::firestore::model::FieldPath)fieldPath + ascending:(BOOL)ascending; - (instancetype)init NS_UNAVAILABLE; /** Compares two documents based on the field and direction of this sort order. */ - (NSComparisonResult)compareDocument:(FSTDocument *)document1 toDocument:(FSTDocument *)document2; +/** The field to sort by. */ +- (const firebase::firestore::model::FieldPath &)field; + /** The direction of the sort. */ @property(nonatomic, assign, readonly, getter=isAscending) BOOL ascending; -/** The field to sort by. */ -@property(nonatomic, strong, readonly) FSTFieldPath *field; - @end /** @@ -157,7 +161,7 @@ typedef NS_ENUM(NSInteger, FSTRelationFilterOperator) { /** * Initializes a query with all of its components directly. */ -- (instancetype)initWithPath:(FSTResourcePath *)path +- (instancetype)initWithPath:(firebase::firestore::model::ResourcePath)path filterBy:(NSArray> *)filters orderBy:(NSArray *)sortOrders limit:(NSInteger)limit @@ -170,7 +174,7 @@ typedef NS_ENUM(NSInteger, FSTRelationFilterOperator) { * @param path The path to the collection to be queried over. * @return A new instance of FSTQuery. */ -+ (instancetype)queryWithPath:(FSTResourcePath *)path; ++ (instancetype)queryWithPath:(firebase::firestore::model::ResourcePath)path; /** * Returns the list of ordering constraints that were explicitly requested on the query by the @@ -237,14 +241,15 @@ typedef NS_ENUM(NSInteger, FSTRelationFilterOperator) { /** Returns a comparator that will sort documents according to the receiver's sort order. */ - (NSComparator)comparator; -/** Returns the field of the first filter on the receiver that's an inequality, or nil if none. */ -- (FSTFieldPath *_Nullable)inequalityFilterField; +/** Returns the field of the first filter on the receiver that's an inequality, or nullptr if none. + */ +- (const firebase::firestore::model::FieldPath *)inequalityFilterField; -/** Returns the first field in an order-by constraint, or nil if none. */ -- (FSTFieldPath *_Nullable)firstSortOrderField; +/** Returns the first field in an order-by constraint, or nullptr if none. */ +- (const firebase::firestore::model::FieldPath *)firstSortOrderField; /** The base path of the query. */ -@property(nonatomic, strong, readonly) FSTResourcePath *path; +- (const firebase::firestore::model::ResourcePath &)path; /** The filters on the documents returned by the query. */ @property(nonatomic, strong, readonly) NSArray> *filters; diff --git a/Firestore/Source/Core/FSTQuery.mm b/Firestore/Source/Core/FSTQuery.mm index 8c98687..7314ce9 100644 --- a/Firestore/Source/Core/FSTQuery.mm +++ b/Firestore/Source/Core/FSTQuery.mm @@ -16,6 +16,9 @@ #import "Firestore/Source/Core/FSTQuery.h" +#include +#include + #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentKey.h" @@ -23,6 +26,14 @@ #import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Source/Util/FSTAssert.h" +#include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::model::FieldPath; +using firebase::firestore::model::ResourcePath; + NS_ASSUME_NONNULL_BEGIN #pragma mark - FSTRelationFilterOperator functions @@ -53,7 +64,10 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe #pragma mark - FSTRelationFilter -@interface FSTRelationFilter () +@interface FSTRelationFilter () { + /** The left hand side of the relation. A path into a document field. */ + firebase::firestore::model::FieldPath _field; +} /** * Initializes the receiver relation filter. @@ -62,7 +76,7 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe * @param filterOperator The binary operator to apply. * @param value A constant value to compare @a field to. The RHS of the expression. */ -- (instancetype)initWithField:(FSTFieldPath *)field +- (instancetype)initWithField:(FieldPath)field filterOperator:(FSTRelationFilterOperator)filterOperator value:(FSTFieldValue *)value NS_DESIGNATED_INITIALIZER; @@ -81,18 +95,20 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe #pragma mark - Constructor methods -+ (instancetype)filterWithField:(FSTFieldPath *)field ++ (instancetype)filterWithField:(FieldPath)field filterOperator:(FSTRelationFilterOperator)filterOperator value:(FSTFieldValue *)value { - return [[FSTRelationFilter alloc] initWithField:field filterOperator:filterOperator value:value]; + return [[FSTRelationFilter alloc] initWithField:std::move(field) + filterOperator:filterOperator + value:value]; } -- (instancetype)initWithField:(FSTFieldPath *)field +- (instancetype)initWithField:(FieldPath)field filterOperator:(FSTRelationFilterOperator)filterOperator value:(FSTFieldValue *)value { self = [super init]; if (self) { - _field = field; + _field = std::move(field); _filterOperator = filterOperator; _value = value; } @@ -105,10 +121,14 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe return self.filterOperator != FSTRelationFilterOperatorEqual; } +- (const firebase::firestore::model::FieldPath &)field { + return _field; +} + #pragma mark - NSObject methods - (NSString *)description { - return [NSString stringWithFormat:@"%@ %@ %@", [self.field canonicalString], + return [NSString stringWithFormat:@"%s %@ %@", _field.CanonicalString().c_str(), FSTStringFromQueryRelationOperator(self.filterOperator), self.value]; } @@ -126,20 +146,21 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe #pragma mark - Private methods - (BOOL)matchesDocument:(FSTDocument *)document { - if ([self.field isKeyFieldPath]) { + if (_field.IsKeyFieldPath()) { FSTAssert([self.value isKindOfClass:[FSTReferenceValue class]], @"Comparing on key, but filter value not a FSTReferenceValue."); FSTReferenceValue *refValue = (FSTReferenceValue *)self.value; NSComparisonResult comparison = FSTDocumentKeyComparator(document.key, refValue.value); return [self matchesComparison:comparison]; } else { - return [self matchesValue:[document fieldForPath:self.field]]; + return [self + matchesValue:[document fieldForPath:[FSTFieldPath fieldPathWithCPPFieldPath:self.field]]]; } } - (NSString *)canonicalID { // TODO(b/37283291): This should be collision robust and avoid relying on |description| methods. - return [NSString stringWithFormat:@"%@%@%@", [self.field canonicalString], + return [NSString stringWithFormat:@"%s%@%@", _field.CanonicalString().c_str(), FSTStringFromQueryRelationOperator(self.filterOperator), [self.value value]]; } @@ -148,7 +169,7 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe if (self.filterOperator != other.filterOperator) { return NO; } - if (![self.field isEqual:other.field]) { + if (_field != other.field) { return NO; } if (![self.value isEqual:other.value]) { @@ -185,25 +206,31 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe #pragma mark - FSTNullFilter -@interface FSTNullFilter () -@property(nonatomic, strong, readonly) FSTFieldPath *field; +@interface FSTNullFilter () { + FieldPath _field; +} @end @implementation FSTNullFilter -- (instancetype)initWithField:(FSTFieldPath *)field { +- (instancetype)initWithField:(FieldPath)field { if (self = [super init]) { - _field = field; + _field = std::move(field); } return self; } - (BOOL)matchesDocument:(FSTDocument *)document { - FSTFieldValue *fieldValue = [document fieldForPath:self.field]; + FSTFieldValue *fieldValue = + [document fieldForPath:[FSTFieldPath fieldPathWithCPPFieldPath:self.field]]; return fieldValue != nil && [fieldValue isEqual:[FSTNullValue nullValue]]; } - (NSString *)canonicalID { - return [NSString stringWithFormat:@"%@ IS NULL", [self.field canonicalString]]; + return [NSString stringWithFormat:@"%s IS NULL", _field.CanonicalString().c_str()]; +} + +- (const firebase::firestore::model::FieldPath &)field { + return _field; } - (NSString *)description { @@ -214,37 +241,43 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe if (other == self) return YES; if (![[other class] isEqual:[self class]]) return NO; - return [self.field isEqual:((FSTNullFilter *)other).field]; + return _field == ((FSTNullFilter *)other)->_field; } - (NSUInteger)hash { - return [self.field hash]; + return _field.Hash(); } @end #pragma mark - FSTNanFilter -@interface FSTNanFilter () -@property(nonatomic, strong, readonly) FSTFieldPath *field; +@interface FSTNanFilter () { + FieldPath _field; +} @end @implementation FSTNanFilter -- (instancetype)initWithField:(FSTFieldPath *)field { +- (instancetype)initWithField:(FieldPath)field { if (self = [super init]) { - _field = field; + _field = std::move(field); } return self; } - (BOOL)matchesDocument:(FSTDocument *)document { - FSTFieldValue *fieldValue = [document fieldForPath:self.field]; + FSTFieldValue *fieldValue = + [document fieldForPath:[FSTFieldPath fieldPathWithCPPFieldPath:self.field]]; return fieldValue != nil && [fieldValue isEqual:[FSTDoubleValue nanValue]]; } - (NSString *)canonicalID { - return [NSString stringWithFormat:@"%@ IS NaN", [self.field canonicalString]]; + return [NSString stringWithFormat:@"%s IS NaN", _field.CanonicalString().c_str()]; +} + +- (const firebase::firestore::model::FieldPath &)field { + return _field; } - (NSString *)description { @@ -255,20 +288,23 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe if (other == self) return YES; if (![[other class] isEqual:[self class]]) return NO; - return [self.field isEqual:((FSTNanFilter *)other).field]; + return _field == ((FSTNanFilter *)other)->_field; } - (NSUInteger)hash { - return [self.field hash]; + return _field.Hash(); } @end #pragma mark - FSTSortOrder -@interface FSTSortOrder () +@interface FSTSortOrder () { + /** The field to sort by. */ + firebase::firestore::model::FieldPath _field; +} /** Creates a new sort order with the given field and direction. */ -- (instancetype)initWithFieldPath:(FSTFieldPath *)fieldPath ascending:(BOOL)ascending; +- (instancetype)initWithFieldPath:(FieldPath)fieldPath ascending:(BOOL)ascending; - (NSString *)canonicalID; @@ -278,28 +314,34 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe #pragma mark - Constructor methods -+ (instancetype)sortOrderWithFieldPath:(FSTFieldPath *)fieldPath ascending:(BOOL)ascending { - return [[FSTSortOrder alloc] initWithFieldPath:fieldPath ascending:ascending]; ++ (instancetype)sortOrderWithFieldPath:(FieldPath)fieldPath ascending:(BOOL)ascending { + return [[FSTSortOrder alloc] initWithFieldPath:std::move(fieldPath) ascending:ascending]; } -- (instancetype)initWithFieldPath:(FSTFieldPath *)fieldPath ascending:(BOOL)ascending { +- (instancetype)initWithFieldPath:(FieldPath)fieldPath ascending:(BOOL)ascending { self = [super init]; if (self) { - _field = fieldPath; + _field = std::move(fieldPath); _ascending = ascending; } return self; } +- (const firebase::firestore::model::FieldPath &)field { + return _field; +} + #pragma mark - Public methods - (NSComparisonResult)compareDocument:(FSTDocument *)document1 toDocument:(FSTDocument *)document2 { NSComparisonResult result; - if ([self.field isEqual:[FSTFieldPath keyFieldPath]]) { + if (_field == FieldPath::KeyFieldPath()) { result = FSTDocumentKeyComparator(document1.key, document2.key); } else { - FSTFieldValue *value1 = [document1 fieldForPath:self.field]; - FSTFieldValue *value2 = [document2 fieldForPath:self.field]; + FSTFieldValue *value1 = + [document1 fieldForPath:[FSTFieldPath fieldPathWithCPPFieldPath:self.field]]; + FSTFieldValue *value2 = + [document2 fieldForPath:[FSTFieldPath fieldPathWithCPPFieldPath:self.field]]; FSTAssert(value1 != nil && value2 != nil, @"Trying to compare documents on fields that don't exist."); result = [value1 compare:value2]; @@ -311,18 +353,19 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe } - (NSString *)canonicalID { - return [NSString - stringWithFormat:@"%@%@", self.field.canonicalString, self.isAscending ? @"asc" : @"desc"]; + return [NSString stringWithFormat:@"%s%@", _field.CanonicalString().c_str(), + self.isAscending ? @"asc" : @"desc"]; } - (BOOL)isEqualToSortOrder:(FSTSortOrder *)other { - return [self.field isEqual:other.field] && self.isAscending == other.isAscending; + return _field == other->_field && self.isAscending == other.isAscending; } #pragma mark - NSObject methods - (NSString *)description { - return [NSString stringWithFormat:@"", self.field, + return [NSString stringWithFormat:@"", + _field.CanonicalString().c_str(), self.ascending ? @"asc" : @"desc"]; } @@ -385,13 +428,14 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe BOOL *stop) { FSTSortOrder *sortOrderComponent = sortOrder[idx]; NSComparisonResult comparison; - if ([sortOrderComponent.field isEqual:[FSTFieldPath keyFieldPath]]) { + if (sortOrderComponent.field == FieldPath::KeyFieldPath()) { FSTAssert([fieldValue isKindOfClass:[FSTReferenceValue class]], @"FSTBound has a non-key value where the key path is being used %@", fieldValue); FSTReferenceValue *refValue = (FSTReferenceValue *)fieldValue; comparison = [refValue.value compare:document.key]; } else { - FSTFieldValue *docValue = [document fieldForPath:sortOrderComponent.field]; + FSTFieldValue *docValue = + [document fieldForPath:[FSTFieldPath fieldPathWithCPPFieldPath:sortOrderComponent.field]]; FSTAssert(docValue != nil, @"Field should exist since document matched the orderBy already."); comparison = [fieldValue compare:docValue]; } @@ -444,6 +488,8 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe @interface FSTQuery () { // Cached value of the canonicalID property. NSString *_canonicalID; + /** The base path of the query. */ + ResourcePath _path; } /** @@ -454,7 +500,7 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe * @param sortOrders The fields and directions to sort the results. * @param limit If not NSNotFound, only this many results will be returned. */ -- (instancetype)initWithPath:(FSTResourcePath *)path +- (instancetype)initWithPath:(ResourcePath)path filterBy:(NSArray> *)filters orderBy:(NSArray *)sortOrders limit:(NSInteger)limit @@ -473,8 +519,8 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe #pragma mark - Constructors -+ (instancetype)queryWithPath:(FSTResourcePath *)path { - return [[FSTQuery alloc] initWithPath:path ++ (instancetype)queryWithPath:(ResourcePath)path { + return [[FSTQuery alloc] initWithPath:std::move(path) filterBy:@[] orderBy:@[] limit:NSNotFound @@ -482,14 +528,14 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe endAt:nil]; } -- (instancetype)initWithPath:(FSTResourcePath *)path +- (instancetype)initWithPath:(ResourcePath)path filterBy:(NSArray> *)filters orderBy:(NSArray *)sortOrders limit:(NSInteger)limit startAt:(nullable FSTBound *)startAtBound endAt:(nullable FSTBound *)endAtBound { if (self = [super init]) { - _path = path; + _path = std::move(path); _filters = filters; _explicitSortOrders = sortOrders; _limit = limit; @@ -527,32 +573,33 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe - (NSArray *)sortOrders { if (self.memoizedSortOrders == nil) { - FSTFieldPath *_Nullable inequalityField = [self inequalityFilterField]; - FSTFieldPath *_Nullable firstSortOrderField = [self firstSortOrderField]; + const FieldPath *inequalityField = [self inequalityFilterField]; + const FieldPath *firstSortOrderField = [self firstSortOrderField]; if (inequalityField && !firstSortOrderField) { // In order to implicitly add key ordering, we must also add the inequality filter field for // it to be a valid query. Note that the default inequality field and key ordering is // ascending. - if ([inequalityField isKeyFieldPath]) { + if (inequalityField->IsKeyFieldPath()) { self.memoizedSortOrders = - @[ [FSTSortOrder sortOrderWithFieldPath:[FSTFieldPath keyFieldPath] ascending:YES] ]; + @[ [FSTSortOrder sortOrderWithFieldPath:FieldPath::KeyFieldPath() ascending:YES] ]; } else { self.memoizedSortOrders = @[ - [FSTSortOrder sortOrderWithFieldPath:inequalityField ascending:YES], - [FSTSortOrder sortOrderWithFieldPath:[FSTFieldPath keyFieldPath] ascending:YES] + [FSTSortOrder sortOrderWithFieldPath:*inequalityField ascending:YES], + [FSTSortOrder sortOrderWithFieldPath:FieldPath::KeyFieldPath() ascending:YES] ]; } } else { - FSTAssert(!inequalityField || [inequalityField isEqual:firstSortOrderField], - @"First orderBy %@ should match inequality field %@.", firstSortOrderField, - inequalityField); + FSTAssert(!inequalityField || *inequalityField == *firstSortOrderField, + @"First orderBy %@ should match inequality field %@.", + util::WrapNSStringNoCopy(firstSortOrderField->CanonicalString()), + util::WrapNSStringNoCopy(inequalityField->CanonicalString())); __block BOOL foundKeyOrder = NO; NSMutableArray *result = [NSMutableArray array]; for (FSTSortOrder *sortOrder in self.explicitSortOrders) { [result addObject:sortOrder]; - if ([sortOrder.field isEqual:[FSTFieldPath keyFieldPath]]) { + if (sortOrder.field == FieldPath::KeyFieldPath()) { foundKeyOrder = YES; } } @@ -562,7 +609,7 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe // explicit sort order BOOL lastIsAscending = self.explicitSortOrders.count > 0 ? self.explicitSortOrders.lastObject.ascending : YES; - [result addObject:[FSTSortOrder sortOrderWithFieldPath:[FSTFieldPath keyFieldPath] + [result addObject:[FSTSortOrder sortOrderWithFieldPath:FieldPath::KeyFieldPath() ascending:lastIsAscending]]; } @@ -573,17 +620,18 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe } - (instancetype)queryByAddingFilter:(id)filter { - FSTAssert(![FSTDocumentKey isDocumentKey:self.path], @"No filtering allowed for document query"); + FSTAssert(![FSTDocumentKey isDocumentKey:[FSTResourcePath resourcePathWithCPPResourcePath:_path]], + @"No filtering allowed for document query"); - FSTFieldPath *_Nullable newInequalityField = nil; + const FieldPath *newInequalityField = nullptr; if ([filter isKindOfClass:[FSTRelationFilter class]] && [((FSTRelationFilter *)filter)isInequality]) { - newInequalityField = filter.field; + newInequalityField = &filter.field; } - FSTFieldPath *_Nullable queryInequalityField = [self inequalityFilterField]; - FSTAssert(!queryInequalityField || !newInequalityField || - [queryInequalityField isEqual:newInequalityField], - @"Query must only have one inequality field."); + const FieldPath *queryInequalityField = [self inequalityFilterField]; + FSTAssert( + !queryInequalityField || !newInequalityField || *queryInequalityField == *newInequalityField, + @"Query must only have one inequality field."); return [[FSTQuery alloc] initWithPath:self.path filterBy:[self.filters arrayByAddingObject:filter] @@ -594,7 +642,7 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe } - (instancetype)queryByAddingSortOrder:(FSTSortOrder *)sortOrder { - FSTAssert(![FSTDocumentKey isDocumentKey:self.path], + FSTAssert(![FSTDocumentKey isDocumentKey:[FSTResourcePath resourcePathWithCPPResourcePath:_path]], @"No ordering is allowed for a document query."); // TODO(klimt): Validate that the same key isn't added twice. @@ -634,7 +682,8 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe } - (BOOL)isDocumentQuery { - return [FSTDocumentKey isDocumentKey:self.path] && self.filters.count == 0; + return [FSTDocumentKey isDocumentKey:[FSTResourcePath resourcePathWithCPPResourcePath:_path]] && + self.filters.count == 0; } - (BOOL)matchesDocument:(FSTDocument *)document { @@ -650,26 +699,33 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe if (comp != NSOrderedSame) { return comp; } - didCompareOnKeyField = - didCompareOnKeyField || [orderBy.field isEqual:[FSTFieldPath keyFieldPath]]; + didCompareOnKeyField = didCompareOnKeyField || orderBy.field == FieldPath::KeyFieldPath(); } FSTAssert(didCompareOnKeyField, @"sortOrder of query did not include key ordering"); return NSOrderedSame; }; } -- (FSTFieldPath *_Nullable)inequalityFilterField { +- (const FieldPath *)inequalityFilterField { for (id filter in self.filters) { if ([filter isKindOfClass:[FSTRelationFilter class]] && ((FSTRelationFilter *)filter).filterOperator != FSTRelationFilterOperatorEqual) { - return filter.field; + return &filter.field; } } - return nil; + return nullptr; +} + +- (const FieldPath *)firstSortOrderField { + if (self.explicitSortOrders.count > 0) { + return &self.explicitSortOrders.firstObject.field; + } + return nullptr; } -- (FSTFieldPath *_Nullable)firstSortOrderField { - return self.explicitSortOrders.firstObject.field; +/** The base path of the query. */ +- (const firebase::firestore::model::ResourcePath &)path { + return _path; } #pragma mark - Private properties @@ -679,7 +735,7 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe return _canonicalID; } - NSMutableString *canonicalID = [[self.path canonicalString] mutableCopy]; + NSMutableString *canonicalID = [util::WrapNSStringNoCopy(_path.CanonicalString()) mutableCopy]; // Add filters. [canonicalID appendString:@"|f:"]; @@ -713,7 +769,7 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe #pragma mark - Private methods - (BOOL)isEqualToQuery:(FSTQuery *)other { - return [self.path isEqual:other.path] && self.limit == other.limit && + return self.path == other.path && self.limit == other.limit && [self.filters isEqual:other.filters] && [self.sortOrders isEqual:other.sortOrders] && (self.startAt == other.startAt || [self.startAt isEqual:other.startAt]) && (self.endAt == other.endAt || [self.endAt isEqual:other.endAt]); @@ -722,12 +778,13 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe /* Returns YES if the document matches the path for the receiver. */ - (BOOL)pathMatchesDocument:(FSTDocument *)document { FSTResourcePath *documentPath = document.key.path; - if ([FSTDocumentKey isDocumentKey:self.path]) { + if ([FSTDocumentKey isDocumentKey:[FSTResourcePath resourcePathWithCPPResourcePath:_path]]) { // Exact match for document queries. - return [self.path isEqual:documentPath]; + return self.path == [documentPath toCPPResourcePath]; } else { // Shallow ancestor queries by default. - return [self.path isPrefixOfPath:documentPath] && self.path.length == documentPath.length - 1; + return self.path.IsPrefixOf([documentPath toCPPResourcePath]) && + _path.size() == documentPath.length - 1; } } @@ -736,10 +793,10 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe */ - (BOOL)orderByMatchesDocument:(FSTDocument *)document { for (FSTSortOrder *orderBy in self.explicitSortOrders) { - FSTFieldPath *fieldPath = orderBy.field; + const FieldPath &fieldPath = orderBy.field; // order by key always matches - if (![fieldPath isEqual:[FSTFieldPath keyFieldPath]] && - [document fieldForPath:fieldPath] == nil) { + if (fieldPath != FieldPath::KeyFieldPath() && + [document fieldForPath:[FSTFieldPath fieldPathWithCPPFieldPath:fieldPath]] == nil) { return NO; } } diff --git a/Firestore/Source/Core/FSTSyncEngine.mm b/Firestore/Source/Core/FSTSyncEngine.mm index 61fac7d..88be23c 100644 --- a/Firestore/Source/Core/FSTSyncEngine.mm +++ b/Firestore/Source/Core/FSTSyncEngine.mm @@ -36,6 +36,7 @@ #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Source/Model/FSTMutationBatch.h" +#import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Source/Remote/FSTRemoteEvent.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTDispatchQueue.h" @@ -496,7 +497,7 @@ static const FSTListenSequenceNumber kIrrelevantSequenceNumber = -1; if (!self.limboTargetsByKey[key]) { FSTLog(@"New document in limbo: %@", key); FSTTargetID limboTargetID = _targetIdGenerator.NextId(); - FSTQuery *query = [FSTQuery queryWithPath:key.path]; + FSTQuery *query = [FSTQuery queryWithPath:[key.path toCPPResourcePath]]; FSTQueryData *queryData = [[FSTQueryData alloc] initWithQuery:query targetID:limboTargetID listenSequenceNumber:kIrrelevantSequenceNumber diff --git a/Firestore/Source/Local/FSTLevelDBMutationQueue.mm b/Firestore/Source/Local/FSTLevelDBMutationQueue.mm index 248ef9a..9041ddc 100644 --- a/Firestore/Source/Local/FSTLevelDBMutationQueue.mm +++ b/Firestore/Source/Local/FSTLevelDBMutationQueue.mm @@ -426,7 +426,7 @@ static ReadOptions StandardReadOptions() { FSTAssert(![query isDocumentQuery], @"Document queries shouldn't go down this path"); NSString *userID = self.userID; - FSTResourcePath *queryPath = query.path; + FSTResourcePath *queryPath = [FSTResourcePath resourcePathWithCPPResourcePath:query.path]; int immediateChildrenPathLength = queryPath.length + 1; // TODO(mcg): Actually implement a single-collection query diff --git a/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.mm b/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.mm index b842cb5..17ecb53 100644 --- a/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.mm +++ b/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.mm @@ -104,7 +104,8 @@ static ReadOptions StandardReadOptions() { // Documents are ordered by key, so we can use a prefix scan to narrow down // the documents we need to match the query against. - std::string startKey = [FSTLevelDBRemoteDocumentKey keyPrefixWithResourcePath:query.path]; + std::string startKey = [FSTLevelDBRemoteDocumentKey + keyPrefixWithResourcePath:[FSTResourcePath resourcePathWithCPPResourcePath:query.path]]; std::unique_ptr it(_db->NewIterator(StandardReadOptions())); it->Seek(startKey); @@ -112,7 +113,7 @@ static ReadOptions StandardReadOptions() { for (; it->Valid() && [currentKey decodeKey:it->key()]; it->Next()) { FSTMaybeDocument *maybeDoc = [self decodedMaybeDocument:it->value() withKey:currentKey.documentKey]; - if (![query.path isPrefixOfPath:maybeDoc.key.path]) { + if (!query.path.IsPrefixOf([maybeDoc.key.path toCPPResourcePath])) { break; } else if ([maybeDoc isKindOfClass:[FSTDocument class]]) { results = [results dictionaryBySettingObject:(FSTDocument *)maybeDoc forKey:maybeDoc.key]; diff --git a/Firestore/Source/Local/FSTLocalDocumentsView.mm b/Firestore/Source/Local/FSTLocalDocumentsView.mm index 0e88958..4bcdf47 100644 --- a/Firestore/Source/Local/FSTLocalDocumentsView.mm +++ b/Firestore/Source/Local/FSTLocalDocumentsView.mm @@ -25,6 +25,7 @@ #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTMutation.h" #import "Firestore/Source/Model/FSTMutationBatch.h" +#import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Source/Util/FSTAssert.h" NS_ASSUME_NONNULL_BEGIN @@ -74,8 +75,9 @@ NS_ASSUME_NONNULL_BEGIN } - (FSTDocumentDictionary *)documentsMatchingQuery:(FSTQuery *)query { - if ([FSTDocumentKey isDocumentKey:query.path]) { - return [self documentsMatchingDocumentQuery:query.path]; + if ([FSTDocumentKey isDocumentKey:[FSTResourcePath resourcePathWithCPPResourcePath:query.path]]) { + return [self documentsMatchingDocumentQuery:[FSTResourcePath + resourcePathWithCPPResourcePath:query.path]]; } else { return [self documentsMatchingCollectionQuery:query]; } diff --git a/Firestore/Source/Local/FSTMemoryMutationQueue.mm b/Firestore/Source/Local/FSTMemoryMutationQueue.mm index 2a6a1cc..bf4f600 100644 --- a/Firestore/Source/Local/FSTMemoryMutationQueue.mm +++ b/Firestore/Source/Local/FSTMemoryMutationQueue.mm @@ -248,13 +248,13 @@ static const NSComparator NumberComparator = ^NSComparisonResult(NSNumber *left, - (NSArray *)allMutationBatchesAffectingQuery:(FSTQuery *)query { // Use the query path as a prefix for testing if a document matches the query. - FSTResourcePath *prefix = query.path; + FSTResourcePath *prefix = [FSTResourcePath resourcePathWithCPPResourcePath:query.path]; int immediateChildrenPathLength = prefix.length + 1; // Construct a document reference for actually scanning the index. Unlike the prefix, the document // key in this reference must have an even number of segments. The empty segment can be used as // a suffix of the query path because it precedes all other segments in an ordered traversal. - FSTResourcePath *startPath = query.path; + FSTResourcePath *startPath = [FSTResourcePath resourcePathWithCPPResourcePath:query.path]; if (![FSTDocumentKey isDocumentKey:startPath]) { startPath = [startPath pathByAppendingSegment:@""]; } diff --git a/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.mm b/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.mm index 9bbc047..7d5e1e2 100644 --- a/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.mm +++ b/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.mm @@ -60,10 +60,11 @@ NS_ASSUME_NONNULL_BEGIN // Documents are ordered by key, so we can use a prefix scan to narrow down the documents // we need to match the query against. - FSTDocumentKey *prefix = [FSTDocumentKey keyWithPath:[query.path pathByAppendingSegment:@""]]; + FSTDocumentKey *prefix = [FSTDocumentKey + keyWithPath:[FSTResourcePath resourcePathWithCPPResourcePath:query.path.Append("")]]; NSEnumerator *enumerator = [self.docs keyEnumeratorFrom:prefix]; for (FSTDocumentKey *key in enumerator) { - if (![query.path isPrefixOfPath:key.path]) { + if (!query.path.IsPrefixOf([key.path toCPPResourcePath])) { break; } FSTMaybeDocument *maybeDoc = self.docs[key]; diff --git a/Firestore/Source/Model/FSTPath.h b/Firestore/Source/Model/FSTPath.h index 1f63f17..f127156 100644 --- a/Firestore/Source/Model/FSTPath.h +++ b/Firestore/Source/Model/FSTPath.h @@ -16,6 +16,9 @@ #import +#include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" + NS_ASSUME_NONNULL_BEGIN /** @@ -113,6 +116,17 @@ NS_ASSUME_NONNULL_BEGIN /** Returns YES if this is the `FSTFieldPath.keyFieldPath` field path. */ - (BOOL)isKeyFieldPath; +/** Creates and returns a new path from C++ FieldPath. + * + * @param fieldPath A C++ FieldPath. + */ ++ (instancetype)fieldPathWithCPPFieldPath:(const firebase::firestore::model::FieldPath &)fieldPath; + +/** + * Creates and returns a new C++ FieldPath. + */ +- (firebase::firestore::model::FieldPath)toCPPFieldPath; + @end /** A slash-separated path for navigating resources (documents and collections) within Firestore. */ @@ -136,6 +150,18 @@ NS_ASSUME_NONNULL_BEGIN * @param resourcePath A slash-separated string representing the path. */ + (instancetype)pathWithString:(NSString *)resourcePath; + +/** Creates and returns a new path from C++ ResourcePath. + * + * @param resourcePath A C++ ResourcePath. + */ ++ (instancetype)resourcePathWithCPPResourcePath: + (const firebase::firestore::model::ResourcePath &)resourcePath; + +/** + * Creates and returns a new C++ ResourcePath. + */ +- (firebase::firestore::model::ResourcePath)toCPPResourcePath; @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Model/FSTPath.mm b/Firestore/Source/Model/FSTPath.mm index 636c322..b91e428 100644 --- a/Firestore/Source/Model/FSTPath.mm +++ b/Firestore/Source/Model/FSTPath.mm @@ -16,11 +16,21 @@ #import "Firestore/Source/Model/FSTPath.h" +#include + #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTClasses.h" #import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::model::FieldPath; +using firebase::firestore::model::ResourcePath; + NS_ASSUME_NONNULL_BEGIN @interface FSTPath () @@ -313,6 +323,22 @@ NS_ASSUME_NONNULL_BEGIN return result; } ++ (instancetype)fieldPathWithCPPFieldPath:(const FieldPath &)fieldPath { + NSMutableArray *segments = [NSMutableArray arrayWithCapacity:fieldPath.size()]; + for (int i = 0; i < fieldPath.size(); i++) { + segments[i] = util::WrapNSString(fieldPath[i]); + } + return [FSTFieldPath pathWithSegments:segments]; +} + +- (FieldPath)toCPPFieldPath { + std::vector segments(self.length); + for (int i = 0; i < self.length; i++) { + segments[i] = [[self segmentAtIndex:i] UTF8String]; + } + return FieldPath(segments.begin(), segments.end()); +} + @end @implementation FSTResourcePath @@ -351,6 +377,23 @@ NS_ASSUME_NONNULL_BEGIN } return result; } + ++ (instancetype)resourcePathWithCPPResourcePath:(const ResourcePath &)resourcePath { + NSMutableArray *segments = [NSMutableArray arrayWithCapacity:resourcePath.size()]; + for (int i = 0; i < resourcePath.size(); i++) { + segments[i] = util::WrapNSString(resourcePath[i]); + } + return [FSTResourcePath pathWithSegments:segments]; +} + +- (ResourcePath)toCPPResourcePath { + std::vector segments(self.length); + for (int i = 0; i < self.length; i++) { + segments[i] = [[self segmentAtIndex:i] UTF8String]; + } + return ResourcePath(segments.begin(), segments.end()); +} + @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Remote/FSTRemoteStore.mm b/Firestore/Source/Remote/FSTRemoteStore.mm index b2e4013..b762722 100644 --- a/Firestore/Source/Remote/FSTRemoteStore.mm +++ b/Firestore/Source/Remote/FSTRemoteStore.mm @@ -27,6 +27,7 @@ #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTMutation.h" #import "Firestore/Source/Model/FSTMutationBatch.h" +#import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Source/Remote/FSTDatastore.h" #import "Firestore/Source/Remote/FSTExistenceFilter.h" #import "Firestore/Source/Remote/FSTRemoteEvent.h" @@ -446,7 +447,8 @@ static const int kOnlineAttemptsBeforeFailure = 2; // updates. Without applying a deleted document there might be another query that will // raise this document as part of a snapshot until it is resolved, essentially exposing // inconsistency between queries - FSTDocumentKey *key = [FSTDocumentKey keyWithPath:query.path]; + FSTDocumentKey *key = [FSTDocumentKey + keyWithPath:[FSTResourcePath resourcePathWithCPPResourcePath:query.path]]; FSTDeletedDocument *deletedDoc = [FSTDeletedDocument documentWithKey:key version:snapshotVersion]; [remoteEvent addDocumentUpdate:deletedDoc]; diff --git a/Firestore/Source/Remote/FSTSerializerBeta.mm b/Firestore/Source/Remote/FSTSerializerBeta.mm index ceb0501..1dc7c79 100644 --- a/Firestore/Source/Remote/FSTSerializerBeta.mm +++ b/Firestore/Source/Remote/FSTSerializerBeta.mm @@ -654,7 +654,8 @@ NS_ASSUME_NONNULL_BEGIN - (GCFSTarget_DocumentsTarget *)encodedDocumentsTarget:(FSTQuery *)query { GCFSTarget_DocumentsTarget *result = [GCFSTarget_DocumentsTarget message]; NSMutableArray *docs = result.documentsArray; - [docs addObject:[self encodedQueryPath:query.path]]; + [docs addObject:[self encodedQueryPath:[FSTResourcePath + resourcePathWithCPPResourcePath:query.path]]]; return result; } @@ -664,16 +665,17 @@ NS_ASSUME_NONNULL_BEGIN (unsigned long)documents.count); NSString *name = documents[0]; - return [FSTQuery queryWithPath:[self decodedQueryPath:name]]; + return [FSTQuery queryWithPath:[[self decodedQueryPath:name] toCPPResourcePath]]; } - (GCFSTarget_QueryTarget *)encodedQueryTarget:(FSTQuery *)query { // Dissect the path into parent, collectionId, and optional key filter. GCFSTarget_QueryTarget *queryTarget = [GCFSTarget_QueryTarget message]; - if (query.path.length == 0) { - queryTarget.parent = [self encodedQueryPath:query.path]; + if (query.path.empty()) { + queryTarget.parent = + [self encodedQueryPath:[FSTResourcePath resourcePathWithCPPResourcePath:query.path]]; } else { - FSTResourcePath *path = query.path; + FSTResourcePath *path = [FSTResourcePath resourcePathWithCPPResourcePath:query.path]; FSTAssert(path.length % 2 != 0, @"Document queries with filters are not supported."); queryTarget.parent = [self encodedQueryPath:[path pathByRemovingLastSegment]]; GCFSStructuredQuery_CollectionSelector *from = [GCFSStructuredQuery_CollectionSelector message]; @@ -749,7 +751,7 @@ NS_ASSUME_NONNULL_BEGIN endAt = [self decodedBound:query.endAt]; } - return [[FSTQuery alloc] initWithPath:path + return [[FSTQuery alloc] initWithPath:[path toCPPResourcePath] filterBy:filterBy orderBy:orderBy limit:limit @@ -818,7 +820,7 @@ NS_ASSUME_NONNULL_BEGIN - (GCFSStructuredQuery_Filter *)encodedRelationFilter:(FSTRelationFilter *)filter { GCFSStructuredQuery_Filter *proto = [GCFSStructuredQuery_Filter message]; GCFSStructuredQuery_FieldFilter *fieldFilter = proto.fieldFilter; - fieldFilter.field = [self encodedFieldPath:filter.field]; + fieldFilter.field = [self encodedFieldPath:[FSTFieldPath fieldPathWithCPPFieldPath:filter.field]]; fieldFilter.op = [self encodedRelationFilterOperator:filter.filterOperator]; fieldFilter.value = [self encodedFieldValue:filter.value]; return proto; @@ -828,12 +830,15 @@ NS_ASSUME_NONNULL_BEGIN FSTFieldPath *fieldPath = [FSTFieldPath pathWithServerFormat:proto.field.fieldPath]; FSTRelationFilterOperator filterOperator = [self decodedRelationFilterOperator:proto.op]; FSTFieldValue *value = [self decodedFieldValue:proto.value]; - return [FSTRelationFilter filterWithField:fieldPath filterOperator:filterOperator value:value]; + return [FSTRelationFilter filterWithField:[fieldPath toCPPFieldPath] + filterOperator:filterOperator + value:value]; } - (GCFSStructuredQuery_Filter *)encodedUnaryFilter:(id)filter { GCFSStructuredQuery_Filter *proto = [GCFSStructuredQuery_Filter message]; - proto.unaryFilter.field = [self encodedFieldPath:filter.field]; + proto.unaryFilter.field = + [self encodedFieldPath:[FSTFieldPath fieldPathWithCPPFieldPath:filter.field]]; if ([filter isKindOfClass:[FSTNanFilter class]]) { proto.unaryFilter.op = GCFSStructuredQuery_UnaryFilter_Operator_IsNan; } else if ([filter isKindOfClass:[FSTNullFilter class]]) { @@ -848,10 +853,10 @@ NS_ASSUME_NONNULL_BEGIN FSTFieldPath *field = [FSTFieldPath pathWithServerFormat:proto.field.fieldPath]; switch (proto.op) { case GCFSStructuredQuery_UnaryFilter_Operator_IsNan: - return [[FSTNanFilter alloc] initWithField:field]; + return [[FSTNanFilter alloc] initWithField:[field toCPPFieldPath]]; case GCFSStructuredQuery_UnaryFilter_Operator_IsNull: - return [[FSTNullFilter alloc] initWithField:field]; + return [[FSTNullFilter alloc] initWithField:[field toCPPFieldPath]]; default: FSTFail(@"Unrecognized UnaryFilter.operator %d", proto.op); @@ -920,7 +925,7 @@ NS_ASSUME_NONNULL_BEGIN - (GCFSStructuredQuery_Order *)encodedSortOrder:(FSTSortOrder *)sortOrder { GCFSStructuredQuery_Order *proto = [GCFSStructuredQuery_Order message]; - proto.field = [self encodedFieldPath:sortOrder.field]; + proto.field = [self encodedFieldPath:[FSTFieldPath fieldPathWithCPPFieldPath:sortOrder.field]]; if (sortOrder.ascending) { proto.direction = GCFSStructuredQuery_Direction_Ascending; } else { @@ -942,7 +947,7 @@ NS_ASSUME_NONNULL_BEGIN default: FSTFail(@"Unrecognized GCFSStructuredQuery_Direction %d", proto.direction); } - return [FSTSortOrder sortOrderWithFieldPath:fieldPath ascending:ascending]; + return [FSTSortOrder sortOrderWithFieldPath:[fieldPath toCPPFieldPath] ascending:ascending]; } #pragma mark - Bounds/Cursors diff --git a/Firestore/core/src/firebase/firestore/model/base_path.h b/Firestore/core/src/firebase/firestore/model/base_path.h index accce27..73ac611 100644 --- a/Firestore/core/src/firebase/firestore/model/base_path.h +++ b/Firestore/core/src/firebase/firestore/model/base_path.h @@ -159,6 +159,19 @@ class BasePath { return segments_ >= rhs.segments_; } +#if defined(__OBJC__) + // For Objective-C++ hash; to be removed after migration. + // Do NOT use in C++ code. + uint64_t Hash() const { + std::hash hash_fn; + uint64_t hash_result = 0; + for (const std::string& segment : segments_) { + hash_result = hash_result * 31u + hash_fn(segment); + } + return hash_result; + } +#endif // defined(__OBJC__) + protected: BasePath() = default; template diff --git a/Firestore/core/test/firebase/firestore/testutil/CMakeLists.txt b/Firestore/core/test/firebase/firestore/testutil/CMakeLists.txt index 14308a5..636acb0 100644 --- a/Firestore/core/test/firebase/firestore/testutil/CMakeLists.txt +++ b/Firestore/core/test/firebase/firestore/testutil/CMakeLists.txt @@ -27,10 +27,12 @@ if(APPLE) list(APPEND TESTUTIL_DEPENDS firebase_firestore_testutil_apple) endif() -add_library( - firebase_firestore_testutil INTERFACE -) -target_link_libraries( - firebase_firestore_testutil INTERFACE - ${TESTUTIL_DEPENDS} +cc_library( + firebase_firestore_testutil + SOURCES + testutil.cc + testutil.h + DEPENDS + ${TESTUTIL_DEPENDS} + firebase_firestore_model ) diff --git a/Firestore/core/test/firebase/firestore/testutil/testutil.cc b/Firestore/core/test/firebase/firestore/testutil/testutil.cc new file mode 100644 index 0000000..9b9228d --- /dev/null +++ b/Firestore/core/test/firebase/firestore/testutil/testutil.cc @@ -0,0 +1,28 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Firestore/core/test/firebase/firestore/testutil/testutil.h" + +namespace firebase { +namespace firestore { +namespace testutil { + +void dummy() { +} + +} // namespace testutil +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/testutil/testutil.h b/Firestore/core/test/firebase/firestore/testutil/testutil.h new file mode 100644 index 0000000..7e4f313 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/testutil/testutil.h @@ -0,0 +1,41 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIRESTORE_CORE_TEST_FIREBASE_FIRESTORE_TESTUTIL_TESTUTIL_H_ +#define FIRESTORE_CORE_TEST_FIREBASE_FIRESTORE_TESTUTIL_TESTUTIL_H_ + +#include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "absl/strings/string_view.h" + +namespace firebase { +namespace firestore { +namespace testutil { + +// Below are convenience methods for creating instances for tests. + +inline model::FieldPath Field(absl::string_view field) { + return model::FieldPath::FromServerFormat(field); +} + +// Add a non-inline function to make this library buildable. +// TODO(zxu123): remove once there is non-inline function. +void dummy(); + +} // namespace testutil +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_TEST_FIREBASE_FIRESTORE_TESTUTIL_TESTUTIL_H_ -- cgit v1.2.3