Search in sources :

Example 1 with TargetChange

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);
    });
}
Also used : MutableDocument(com.google.firebase.firestore.model.MutableDocument) TargetChange(com.google.firebase.firestore.remote.TargetChange) SnapshotVersion(com.google.firebase.firestore.model.SnapshotVersion) DocumentKey(com.google.firebase.firestore.model.DocumentKey) HashMap(java.util.HashMap) Map(java.util.Map) ImmutableSortedMap(com.google.firebase.database.collection.ImmutableSortedMap)

Example 2 with TargetChange

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);
}
Also used : MutableDocument(com.google.firebase.firestore.model.MutableDocument) ArrayList(java.util.ArrayList) Document(com.google.firebase.firestore.model.Document) MutableDocument(com.google.firebase.firestore.model.MutableDocument) View(com.google.firebase.firestore.core.View) ViewSnapshot(com.google.firebase.firestore.core.ViewSnapshot) TargetChange(com.google.firebase.firestore.remote.TargetChange) DocumentKey(com.google.firebase.firestore.model.DocumentKey)

Example 3 with TargetChange

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);
}
Also used : ListenOptions(com.google.firebase.firestore.core.EventManager.ListenOptions) TargetChange(com.google.firebase.firestore.remote.TargetChange) ArrayList(java.util.ArrayList) MutableDocument(com.google.firebase.firestore.model.MutableDocument) Test(org.junit.Test)

Example 4 with TargetChange

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);
}
Also used : TargetChange(com.google.firebase.firestore.remote.TargetChange) DocumentKey(com.google.firebase.firestore.model.DocumentKey) Document(com.google.firebase.firestore.model.Document) MutableDocument(com.google.firebase.firestore.model.MutableDocument) HashMap(java.util.HashMap) Map(java.util.Map) ImmutableSortedMap(com.google.firebase.database.collection.ImmutableSortedMap)

Example 5 with TargetChange

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());
}
Also used : TargetChange(com.google.firebase.firestore.remote.TargetChange) MutableDocument(com.google.firebase.firestore.model.MutableDocument) DocumentKey(com.google.firebase.firestore.model.DocumentKey) Document(com.google.firebase.firestore.model.Document) MutableDocument(com.google.firebase.firestore.model.MutableDocument) Test(org.junit.Test)

Aggregations

TargetChange (com.google.firebase.firestore.remote.TargetChange)10 MutableDocument (com.google.firebase.firestore.model.MutableDocument)7 DocumentKey (com.google.firebase.firestore.model.DocumentKey)5 ArrayList (java.util.ArrayList)4 Test (org.junit.Test)4 ImmutableSortedMap (com.google.firebase.database.collection.ImmutableSortedMap)3 Document (com.google.firebase.firestore.model.Document)3 HashMap (java.util.HashMap)3 Map (java.util.Map)3 QueryResult (com.google.firebase.firestore.local.QueryResult)2 ListenOptions (com.google.firebase.firestore.core.EventManager.ListenOptions)1 View (com.google.firebase.firestore.core.View)1 ViewSnapshot (com.google.firebase.firestore.core.ViewSnapshot)1 SyncState (com.google.firebase.firestore.core.ViewSnapshot.SyncState)1 LocalViewChanges (com.google.firebase.firestore.local.LocalViewChanges)1 SnapshotVersion (com.google.firebase.firestore.model.SnapshotVersion)1