Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 16 additions & 42 deletions Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,6 @@

NS_ASSUME_NONNULL_BEGIN

@interface FIRDocumentChange ()

// Expose initializer for testing.
- (instancetype)initWithType:(FIRDocumentChangeType)type
document:(FIRQueryDocumentSnapshot *)document
oldIndex:(NSUInteger)oldIndex
newIndex:(NSUInteger)newIndex;

@end

@interface FIRQuerySnapshotTests : XCTestCase
@end

Expand Down Expand Up @@ -91,56 +81,40 @@ - (void)testIncludeMetadataChanges {
DocumentSet oldDocuments = FSTTestDocSet(FSTDocumentComparatorByKey, @[ doc1Old, doc2Old ]);
DocumentSet newDocuments = FSTTestDocSet(FSTDocumentComparatorByKey, @[ doc2New, doc2New ]);
std::vector<DocumentViewChange> documentChanges{
DocumentViewChange{doc1New, DocumentViewChange::Type::kMetadata},
DocumentViewChange{doc2New, DocumentViewChange::Type::kModified},
DocumentViewChange(doc1New, DocumentViewChange::Type::kMetadata),
DocumentViewChange(doc2New, DocumentViewChange::Type::kModified),
};

Firestore *firestore = FSTTestFirestore().wrapped;
FSTQuery *query = FSTTestQuery("foo");
ViewSnapshot viewSnapshot{query,
newDocuments,
oldDocuments,
std::move(documentChanges),
/*mutated_keys=*/DocumentKeySet{},
ViewSnapshot viewSnapshot(query, newDocuments, oldDocuments, std::move(documentChanges),
/*mutated_keys=*/DocumentKeySet(),
/*from_cache=*/false,
/*sync_state_changed=*/true,
/*excludes_metadata_changes=*/false};
/*excludes_metadata_changes=*/false);
SnapshotMetadata metadata(/*pending_writes=*/false, /*from_cache=*/false);
FIRQuerySnapshot *snapshot = [[FIRQuerySnapshot alloc] initWithFirestore:firestore
originalQuery:query
snapshot:std::move(viewSnapshot)
metadata:std::move(metadata)];

FIRQueryDocumentSnapshot *doc1Snap =
[[FIRQueryDocumentSnapshot alloc] initWithFirestore:firestore
documentKey:doc1New.key
document:doc1New
fromCache:false
hasPendingWrites:false];
FIRQueryDocumentSnapshot *doc2Snap =
[[FIRQueryDocumentSnapshot alloc] initWithFirestore:firestore
documentKey:doc2New.key
document:doc2New
fromCache:false
hasPendingWrites:false];
DocumentSnapshot doc1Snap(firestore, doc1New.key, doc1New, SnapshotMetadata());
DocumentSnapshot doc2Snap(firestore, doc2New.key, doc2New, SnapshotMetadata());

NSArray<FIRDocumentChange *> *changesWithoutMetadata = @[
[[FIRDocumentChange alloc] initWithType:FIRDocumentChangeTypeModified
document:doc2Snap
oldIndex:1
newIndex:1],
[[FIRDocumentChange alloc]
initWithDocumentChange:DocumentChange(DocumentChange::Type::Modified, doc2Snap,
/*old_index=*/1, /*new_index=*/1)],
];
XCTAssertEqualObjects(snapshot.documentChanges, changesWithoutMetadata);

NSArray<FIRDocumentChange *> *changesWithMetadata = @[
[[FIRDocumentChange alloc] initWithType:FIRDocumentChangeTypeModified
document:doc1Snap
oldIndex:0
newIndex:0],
[[FIRDocumentChange alloc] initWithType:FIRDocumentChangeTypeModified
document:doc2Snap
oldIndex:1
newIndex:1],
[[FIRDocumentChange alloc]
initWithDocumentChange:DocumentChange(DocumentChange::Type::Modified, doc1Snap,
/*old_index=*/0, /*new_index=*/0)],
[[FIRDocumentChange alloc]
initWithDocumentChange:DocumentChange(DocumentChange::Type::Modified, doc2Snap,
/*old_index=*/1, /*new_index=*/1)],
];
XCTAssertEqualObjects([snapshot documentChangesWithIncludeMetadataChanges:YES],
changesWithMetadata);
Expand Down
16 changes: 6 additions & 10 deletions Firestore/Source/API/FIRDocumentChange+Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,17 @@

#import "FIRDocumentChange.h"

#include "Firestore/core/src/firebase/firestore/api/firestore.h"
#include "Firestore/core/src/firebase/firestore/core/view_snapshot.h"
#import <Foundation/Foundation.h>

@class FIRFirestore;
#include "Firestore/core/src/firebase/firestore/api/document_change.h"

using firebase::firestore::api::DocumentChange;

NS_ASSUME_NONNULL_BEGIN

/** Internal FIRDocumentChange API we don't want exposed in our public header files. */
@interface FIRDocumentChange (Internal)
@interface FIRDocumentChange (/* Init */)

/** Calculates the array of FIRDocumentChange's based on the given FSTViewSnapshot. */
+ (NSArray<FIRDocumentChange *> *)
documentChangesForSnapshot:(const firebase::firestore::core::ViewSnapshot &)snapshot
includeMetadataChanges:(bool)includeMetadataChanges
firestore:(firebase::firestore::api::Firestore *)firestore;
- (instancetype)initWithDocumentChange:(DocumentChange &&)documentChange NS_DESIGNATED_INITIALIZER;

@end

Expand Down
152 changes: 34 additions & 118 deletions Firestore/Source/API/FIRDocumentChange.mm
Original file line number Diff line number Diff line change
Expand Up @@ -14,128 +14,24 @@
* limitations under the License.
*/

#import "FIRDocumentChange.h"
#import "Firestore/Source/API/FIRDocumentChange+Internal.h"

#import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h"
#import "Firestore/Source/API/FIRFirestore+Internal.h"
#import "Firestore/Source/Core/FSTQuery.h"
#import "Firestore/Source/Model/FSTDocument.h"

#include "Firestore/core/src/firebase/firestore/core/view_snapshot.h"
#include "Firestore/core/src/firebase/firestore/model/document_set.h"
#include "Firestore/core/src/firebase/firestore/api/document_change.h"
#include "Firestore/core/src/firebase/firestore/util/hard_assert.h"

using firebase::firestore::api::Firestore;
using firebase::firestore::core::DocumentViewChange;
using firebase::firestore::core::ViewSnapshot;
using firebase::firestore::model::DocumentSet;
using firebase::firestore::api::DocumentChange;

NS_ASSUME_NONNULL_BEGIN

@interface FIRDocumentChange ()

- (instancetype)initWithType:(FIRDocumentChangeType)type
document:(FIRDocumentSnapshot *)document
oldIndex:(NSUInteger)oldIndex
newIndex:(NSUInteger)newIndex NS_DESIGNATED_INITIALIZER;

@end

@implementation FIRDocumentChange (Internal)

+ (FIRDocumentChangeType)documentChangeTypeForChange:(const DocumentViewChange &)change {
switch (change.type()) {
case DocumentViewChange::Type::kAdded:
return FIRDocumentChangeTypeAdded;
case DocumentViewChange::Type::kModified:
case DocumentViewChange::Type::kMetadata:
return FIRDocumentChangeTypeModified;
case DocumentViewChange::Type::kRemoved:
return FIRDocumentChangeTypeRemoved;
}

HARD_FAIL("Unknown DocumentViewChange::Type: %s", change.type());
@implementation FIRDocumentChange {
DocumentChange _documentChange;
}

+ (NSArray<FIRDocumentChange *> *)documentChangesForSnapshot:(const ViewSnapshot &)snapshot
includeMetadataChanges:(bool)includeMetadataChanges
firestore:(Firestore *)firestore {
if (snapshot.old_documents().empty()) {
// Special case the first snapshot because index calculation is easy and fast. Also all changes
// on the first snapshot are adds so there are also no metadata-only changes to filter out.
FSTDocument *_Nullable lastDocument = nil;
NSUInteger index = 0;
NSMutableArray<FIRDocumentChange *> *changes = [NSMutableArray array];
for (const DocumentViewChange &change : snapshot.document_changes()) {
FIRQueryDocumentSnapshot *document = [[FIRQueryDocumentSnapshot alloc]
initWithFirestore:firestore
documentKey:change.document().key
document:change.document()
fromCache:snapshot.from_cache()
hasPendingWrites:snapshot.mutated_keys().contains(change.document().key)];
HARD_ASSERT(change.type() == DocumentViewChange::Type::kAdded,
"Invalid event type for first snapshot");
HARD_ASSERT(!lastDocument || snapshot.query().comparator(lastDocument, change.document()) ==
NSOrderedAscending,
"Got added events in wrong order");
[changes addObject:[[FIRDocumentChange alloc] initWithType:FIRDocumentChangeTypeAdded
document:document
oldIndex:NSNotFound
newIndex:index++]];
}
return changes;
} else {
// A DocumentSet that is updated incrementally as changes are applied to use to lookup the index
// of a document.
DocumentSet indexTracker = snapshot.old_documents();
NSMutableArray<FIRDocumentChange *> *changes = [NSMutableArray array];
for (const DocumentViewChange &change : snapshot.document_changes()) {
if (!includeMetadataChanges && change.type() == DocumentViewChange::Type::kMetadata) {
continue;
}

FIRQueryDocumentSnapshot *document = [[FIRQueryDocumentSnapshot alloc]
initWithFirestore:firestore
documentKey:change.document().key
document:change.document()
fromCache:snapshot.from_cache()
hasPendingWrites:snapshot.mutated_keys().contains(change.document().key)];

size_t oldIndex = DocumentSet::npos;
size_t newIndex = DocumentSet::npos;
if (change.type() != DocumentViewChange::Type::kAdded) {
oldIndex = indexTracker.IndexOf(change.document().key);
HARD_ASSERT(oldIndex != DocumentSet::npos, "Index for document not found");
indexTracker = indexTracker.erase(change.document().key);
}
if (change.type() != DocumentViewChange::Type::kRemoved) {
indexTracker = indexTracker.insert(change.document());
newIndex = indexTracker.IndexOf(change.document().key);
}
[FIRDocumentChange documentChangeTypeForChange:change];
FIRDocumentChangeType type = [FIRDocumentChange documentChangeTypeForChange:change];
[changes addObject:[[FIRDocumentChange alloc] initWithType:type
document:document
oldIndex:oldIndex
newIndex:newIndex]];
}
return changes;
}
}

@end

@implementation FIRDocumentChange

- (instancetype)initWithType:(FIRDocumentChangeType)type
document:(FIRQueryDocumentSnapshot *)document
oldIndex:(NSUInteger)oldIndex
newIndex:(NSUInteger)newIndex {
- (instancetype)initWithDocumentChange:(DocumentChange &&)documentChange {
if (self = [super init]) {
_type = type;
_document = document;
_oldIndex = oldIndex;
_newIndex = newIndex;
_documentChange = std::move(documentChange);
}
return self;
}
Expand All @@ -145,16 +41,36 @@ - (BOOL)isEqual:(nullable id)other {
if (![other isKindOfClass:[FIRDocumentChange class]]) return NO;

FIRDocumentChange *change = (FIRDocumentChange *)other;
return self.type == change.type && [self.document isEqual:change.document] &&
self.oldIndex == change.oldIndex && self.newIndex == change.newIndex;
return _documentChange == change->_documentChange;
}

- (NSUInteger)hash {
NSUInteger result = (NSUInteger)self.type;
result = result * 31u + [self.document hash];
result = result * 31u + (NSUInteger)self.oldIndex;
result = result * 31u + (NSUInteger)self.newIndex;
return result;
return _documentChange.Hash();
}

- (FIRDocumentChangeType)type {
switch (_documentChange.type()) {
case DocumentChange::Type::Added:
return FIRDocumentChangeTypeAdded;
case DocumentChange::Type::Modified:
return FIRDocumentChangeTypeModified;
case DocumentChange::Type::Removed:
return FIRDocumentChangeTypeRemoved;
}

HARD_FAIL("Unknown DocumentChange::Type: %s", _documentChange.type());
}

- (FIRQueryDocumentSnapshot *)document {
return [[FIRQueryDocumentSnapshot alloc] initWithSnapshot:_documentChange.document()];
}

- (NSUInteger)oldIndex {
return _documentChange.old_index();
}

- (NSUInteger)newIndex {
return _documentChange.new_index();
}

@end
Expand Down
16 changes: 8 additions & 8 deletions Firestore/Source/API/FIRQuerySnapshot.mm
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,15 @@ - (NSInteger)count {

- (NSArray<FIRDocumentChange *> *)documentChangesWithIncludeMetadataChanges:
(BOOL)includeMetadataChanges {
if (includeMetadataChanges && _snapshot->view_snapshot().excludes_metadata_changes()) {
ThrowInvalidArgument("To include metadata changes with your document changes, you must call "
"addSnapshotListener(includeMetadataChanges: true).");
}

if (!_documentChanges || _documentChangesIncludeMetadataChanges != includeMetadataChanges) {
_documentChanges = [FIRDocumentChange documentChangesForSnapshot:_snapshot->view_snapshot()
includeMetadataChanges:includeMetadataChanges
firestore:_snapshot->firestore()];
NSMutableArray *documentChanges = [NSMutableArray array];
_snapshot->ForEachChange(
static_cast<bool>(includeMetadataChanges), [&documentChanges](DocumentChange change) {
[documentChanges
addObject:[[FIRDocumentChange alloc] initWithDocumentChange:std::move(change)]];
});

_documentChanges = documentChanges;
_documentChangesIncludeMetadataChanges = includeMetadataChanges;
}
return _documentChanges;
Expand Down
Loading