use of com.google.firebase.firestore.remote.TargetChange in project firebase-android-sdk by firebase.
the class LocalStore method applyRemoteEvent.
/**
* Updates the "ground-state" (remote) documents. We assume that the remote event reflects any
* write batches that have been acknowledged or rejected (i.e. we do not re-apply local mutations
* to updates from this event).
*
* <p>LocalDocuments are re-calculated if there are remaining mutations in the queue.
*/
public ImmutableSortedMap<DocumentKey, Document> applyRemoteEvent(RemoteEvent remoteEvent) {
SnapshotVersion remoteVersion = remoteEvent.getSnapshotVersion();
// TODO: Call queryEngine.handleDocumentChange() appropriately.
return persistence.runTransaction("Apply remote event", () -> {
Map<Integer, TargetChange> targetChanges = remoteEvent.getTargetChanges();
long sequenceNumber = persistence.getReferenceDelegate().getCurrentSequenceNumber();
for (Map.Entry<Integer, TargetChange> entry : targetChanges.entrySet()) {
Integer boxedTargetId = entry.getKey();
int targetId = boxedTargetId;
TargetChange change = entry.getValue();
TargetData oldTargetData = queryDataByTarget.get(targetId);
if (oldTargetData == null) {
// we persist the updated query data along with the updated assignment.
continue;
}
targetCache.removeMatchingKeys(change.getRemovedDocuments(), targetId);
targetCache.addMatchingKeys(change.getAddedDocuments(), targetId);
TargetData newTargetData = oldTargetData.withSequenceNumber(sequenceNumber);
if (remoteEvent.getTargetMismatches().contains(targetId)) {
newTargetData = newTargetData.withResumeToken(ByteString.EMPTY, SnapshotVersion.NONE).withLastLimboFreeSnapshotVersion(SnapshotVersion.NONE);
} else if (!change.getResumeToken().isEmpty()) {
newTargetData = newTargetData.withResumeToken(change.getResumeToken(), remoteEvent.getSnapshotVersion());
}
queryDataByTarget.put(targetId, newTargetData);
// since the last update).
if (shouldPersistTargetData(oldTargetData, newTargetData, change)) {
targetCache.updateTargetData(newTargetData);
}
}
Map<DocumentKey, MutableDocument> documentUpdates = remoteEvent.getDocumentUpdates();
Set<DocumentKey> limboDocuments = remoteEvent.getResolvedLimboDocuments();
for (DocumentKey key : documentUpdates.keySet()) {
if (limboDocuments.contains(key)) {
persistence.getReferenceDelegate().updateLimboDocument(key);
}
}
DocumentChangeResult result = populateDocumentChanges(documentUpdates);
Map<DocumentKey, MutableDocument> changedDocs = result.changedDocuments;
// HACK: The only reason we allow snapshot version NONE is so that we can synthesize
// remote events when we get permission denied errors while trying to resolve the
// state of a locally cached document that is in limbo.
SnapshotVersion lastRemoteVersion = targetCache.getLastRemoteSnapshotVersion();
if (!remoteVersion.equals(SnapshotVersion.NONE)) {
hardAssert(remoteVersion.compareTo(lastRemoteVersion) >= 0, "Watch stream reverted to previous snapshot?? (%s < %s)", remoteVersion, lastRemoteVersion);
targetCache.setLastRemoteSnapshotVersion(remoteVersion);
}
return localDocuments.getLocalViewOfDocuments(changedDocs, result.existenceChangedKeys);
});
}
use of com.google.firebase.firestore.remote.TargetChange in project firebase-android-sdk by firebase.
the class DocumentChangeTest method validatePositions.
private static void validatePositions(com.google.firebase.firestore.core.Query query, Collection<MutableDocument> initialDocsList, Collection<MutableDocument> addedList, Collection<MutableDocument> modifiedList, Collection<MutableDocument> removedList) {
ImmutableSortedMap<DocumentKey, Document> initialDocs = docUpdates(initialDocsList.toArray(new MutableDocument[] {}));
ImmutableSortedMap<DocumentKey, Document> updates = emptyDocumentMap();
for (MutableDocument doc : addedList) {
updates = updates.insert(doc.getKey(), doc);
}
for (MutableDocument doc : modifiedList) {
updates = updates.insert(doc.getKey(), doc);
}
for (MutableDocument doc : removedList) {
updates = updates.insert(doc.getKey(), doc);
}
View view = new View(query, DocumentKey.emptyKeySet());
View.DocumentChanges initialChanges = view.computeDocChanges(initialDocs);
TargetChange initialTargetChange = ackTarget(initialDocsList.toArray(new MutableDocument[] {}));
ViewSnapshot initialSnapshot = view.applyChanges(initialChanges, initialTargetChange).getSnapshot();
View.DocumentChanges updateChanges = view.computeDocChanges(updates);
TargetChange updateTargetChange = targetChange(ByteString.EMPTY, true, addedList, modifiedList, removedList);
ViewSnapshot updatedSnapshot = view.applyChanges(updateChanges, updateTargetChange).getSnapshot();
if (updatedSnapshot == null) {
// Nothing changed, no positions to verify
return;
}
List<Document> expected = new ArrayList<>(updatedSnapshot.getDocuments().toList());
List<Document> actual = new ArrayList<>(initialSnapshot.getDocuments().toList());
FirebaseFirestore firestore = mock(FirebaseFirestore.class);
List<DocumentChange> changes = DocumentChange.changesFromSnapshot(firestore, MetadataChanges.EXCLUDE, updatedSnapshot);
for (DocumentChange change : changes) {
if (change.getType() != Type.ADDED) {
actual.remove(change.getOldIndex());
}
if (change.getType() != Type.REMOVED) {
actual.add(change.getNewIndex(), change.getDocument().getDocument());
}
}
assertEquals(expected, actual);
}
use of com.google.firebase.firestore.remote.TargetChange in project firebase-android-sdk by firebase.
the class QueryListenerTest method testDoesNotRaiseEventsForMetadataChangesUnlessSpecified.
@Test
public void testDoesNotRaiseEventsForMetadataChangesUnlessSpecified() {
List<ViewSnapshot> filteredAccum = new ArrayList<>();
List<ViewSnapshot> fullAccum = new ArrayList<>();
Query query = Query.atPath(path("rooms"));
MutableDocument doc1 = doc("rooms/eros", 1, map("name", "eros"));
MutableDocument doc2 = doc("rooms/hades", 2, map("name", "hades"));
ListenOptions options1 = new ListenOptions();
ListenOptions options2 = new ListenOptions();
options2.includeQueryMetadataChanges = true;
options2.includeDocumentMetadataChanges = true;
QueryListener filteredListener = queryListener(query, options1, filteredAccum);
QueryListener fullListener = queryListener(query, options2, fullAccum);
View view = new View(query, DocumentKey.emptyKeySet());
ViewSnapshot snap1 = applyChanges(view, doc1);
TargetChange ackTarget = ackTarget(doc1);
ViewSnapshot snap2 = view.applyChanges(view.computeDocChanges(docUpdates()), ackTarget).getSnapshot();
ViewSnapshot snap3 = applyChanges(view, doc2);
// local event
filteredListener.onViewSnapshot(snap1);
// no event
filteredListener.onViewSnapshot(snap2);
// doc2 update
filteredListener.onViewSnapshot(snap3);
// local event
fullListener.onViewSnapshot(snap1);
// no event
fullListener.onViewSnapshot(snap2);
// doc2 update
fullListener.onViewSnapshot(snap3);
assertEquals(asList(applyExpectedMetadata(snap1, MetadataChanges.EXCLUDE), applyExpectedMetadata(snap3, MetadataChanges.EXCLUDE)), filteredAccum);
assertEquals(asList(snap1, snap2, snap3), fullAccum);
}
use of com.google.firebase.firestore.remote.TargetChange in project firebase-android-sdk by firebase.
the class SyncEngine method handleRemoteEvent.
/**
* Called by FirestoreClient to notify us of a new remote event.
*/
@Override
public void handleRemoteEvent(RemoteEvent event) {
assertCallback("handleRemoteEvent");
// Update `receivedDocument` as appropriate for any limbo targets.
for (Map.Entry<Integer, TargetChange> entry : event.getTargetChanges().entrySet()) {
Integer targetId = entry.getKey();
TargetChange targetChange = entry.getValue();
LimboResolution limboResolution = activeLimboResolutionsByTarget.get(targetId);
if (limboResolution != null) {
// Since this is a limbo resolution lookup, it's for a single document and it could be
// added, modified, or removed, but not a combination.
hardAssert(targetChange.getAddedDocuments().size() + targetChange.getModifiedDocuments().size() + targetChange.getRemovedDocuments().size() <= 1, "Limbo resolution for single document contains multiple changes.");
if (targetChange.getAddedDocuments().size() > 0) {
limboResolution.receivedDocument = true;
} else if (targetChange.getModifiedDocuments().size() > 0) {
hardAssert(limboResolution.receivedDocument, "Received change for limbo target document without add.");
} else if (targetChange.getRemovedDocuments().size() > 0) {
hardAssert(limboResolution.receivedDocument, "Received remove for limbo target document without add.");
limboResolution.receivedDocument = false;
} else {
// This was probably just a CURRENT targetChange or similar.
}
}
}
ImmutableSortedMap<DocumentKey, Document> changes = localStore.applyRemoteEvent(event);
emitNewSnapsAndNotifyLocalStore(changes, event);
}
use of com.google.firebase.firestore.remote.TargetChange in project firebase-android-sdk by firebase.
the class ViewTest method testAddsDocumentsBasedOnQuery.
@Test
public void testAddsDocumentsBasedOnQuery() {
Query query = messageQuery();
View view = new View(query, DocumentKey.emptyKeySet());
MutableDocument doc1 = doc("rooms/eros/messages/1", 0, map("text", "msg1"));
MutableDocument doc2 = doc("rooms/eros/messages/2", 0, map("text", "msg2"));
MutableDocument doc3 = doc("rooms/other/messages/1", 0, map("text", "msg3"));
ImmutableSortedMap<DocumentKey, Document> updates = docUpdates(doc1, doc2, doc3);
View.DocumentChanges docViewChanges = view.computeDocChanges(updates);
TargetChange targetChange = ackTarget(doc1, doc2, doc3);
ViewSnapshot snapshot = view.applyChanges(docViewChanges, targetChange).getSnapshot();
assertEquals(query, snapshot.getQuery());
assertEquals(asList(doc1, doc2), snapshot.getDocuments().toList());
assertEquals(asList(DocumentViewChange.create(Type.ADDED, doc1), DocumentViewChange.create(Type.ADDED, doc2)), snapshot.getChanges());
assertFalse(snapshot.isFromCache());
assertTrue(snapshot.didSyncStateChange());
assertFalse(snapshot.hasPendingWrites());
}
Aggregations