Search in sources :

Example 6 with MutableDocument

use of com.google.firebase.firestore.model.MutableDocument in project firebase-android-sdk by firebase.

the class LocalStore method writeLocally.

/**
 * Accepts locally generated Mutations and commits them to storage.
 */
public LocalDocumentsResult writeLocally(List<Mutation> mutations) {
    Timestamp localWriteTime = Timestamp.now();
    // TODO: Call queryEngine.handleDocumentChange() appropriately.
    Set<DocumentKey> keys = new HashSet<>();
    for (Mutation mutation : mutations) {
        keys.add(mutation.getKey());
    }
    return persistence.runTransaction("Locally write mutations", () -> {
        // Figure out which keys do not have a remote version in the cache, this is needed to
        // create the right overlay mutation: if no remote version presents, we do not need to
        // create overlays as patch mutations.
        // TODO(Overlay): Is there a better way to determine this? Document version does not work
        // because local mutations set them back to 0.
        Map<DocumentKey, MutableDocument> remoteDocs = remoteDocuments.getAll(keys);
        Set<DocumentKey> docsWithoutRemoteVersion = new HashSet<>();
        for (Map.Entry<DocumentKey, MutableDocument> entry : remoteDocs.entrySet()) {
            if (!entry.getValue().isValidDocument()) {
                docsWithoutRemoteVersion.add(entry.getKey());
            }
        }
        // Load and apply all existing mutations. This lets us compute the current base state for
        // all non-idempotent transforms before applying any additional user-provided writes.
        Map<DocumentKey, OverlayedDocument> overlayedDocuments = localDocuments.getOverlayedDocuments(remoteDocs);
        // For non-idempotent mutations (such as `FieldValue.increment()`), we record the base
        // state in a separate patch mutation. This is later used to guarantee consistent values
        // and prevents flicker even if the backend sends us an update that already includes our
        // transform.
        List<Mutation> baseMutations = new ArrayList<>();
        for (Mutation mutation : mutations) {
            ObjectValue baseValue = mutation.extractTransformBaseValue(overlayedDocuments.get(mutation.getKey()).getDocument());
            if (baseValue != null) {
                // NOTE: The base state should only be applied if there's some existing
                // document to override, so use a Precondition of exists=true
                baseMutations.add(new PatchMutation(mutation.getKey(), baseValue, baseValue.getFieldMask(), Precondition.exists(true)));
            }
        }
        MutationBatch batch = mutationQueue.addMutationBatch(localWriteTime, baseMutations, mutations);
        Map<DocumentKey, Mutation> overlays = batch.applyToLocalDocumentSet(overlayedDocuments, docsWithoutRemoteVersion);
        documentOverlayCache.saveOverlays(batch.getBatchId(), overlays);
        return LocalDocumentsResult.fromOverlayedDocuments(batch.getBatchId(), overlayedDocuments);
    });
}
Also used : MutableDocument(com.google.firebase.firestore.model.MutableDocument) ArrayList(java.util.ArrayList) Timestamp(com.google.firebase.Timestamp) ObjectValue(com.google.firebase.firestore.model.ObjectValue) MutationBatch(com.google.firebase.firestore.model.mutation.MutationBatch) DocumentKey(com.google.firebase.firestore.model.DocumentKey) PatchMutation(com.google.firebase.firestore.model.mutation.PatchMutation) PatchMutation(com.google.firebase.firestore.model.mutation.PatchMutation) Mutation(com.google.firebase.firestore.model.mutation.Mutation) HashMap(java.util.HashMap) Map(java.util.Map) ImmutableSortedMap(com.google.firebase.database.collection.ImmutableSortedMap) HashSet(java.util.HashSet)

Example 7 with MutableDocument

use of com.google.firebase.firestore.model.MutableDocument in project firebase-android-sdk by firebase.

the class LocalStore method populateDocumentChanges.

/**
 * Populates the remote document cache with documents from backend or a bundle. Returns the
 * document changes resulting from applying those documents, and also a set of documents whose
 * existence state are changed as a result.
 *
 * <p>Note: this function will use `documentVersions` if it is defined. When it is not defined, it
 * resorts to `globalVersion`.
 *
 * @param documents Documents to be applied.
 */
private DocumentChangeResult populateDocumentChanges(Map<DocumentKey, MutableDocument> documents) {
    Map<DocumentKey, MutableDocument> changedDocs = new HashMap<>();
    List<DocumentKey> removedDocs = new ArrayList<>();
    Set<DocumentKey> conditionChanged = new HashSet<>();
    // Each loop iteration only affects its "own" doc, so it's safe to get all the remote
    // documents in advance in a single call.
    Map<DocumentKey, MutableDocument> existingDocs = remoteDocuments.getAll(documents.keySet());
    for (Entry<DocumentKey, MutableDocument> entry : documents.entrySet()) {
        DocumentKey key = entry.getKey();
        MutableDocument doc = entry.getValue();
        MutableDocument existingDoc = existingDocs.get(key);
        // Check if see if there is a existence state change for this document.
        if (doc.isFoundDocument() != existingDoc.isFoundDocument()) {
            conditionChanged.add(key);
        }
        // never add documents to cache.
        if (doc.isNoDocument() && doc.getVersion().equals(SnapshotVersion.NONE)) {
            // NoDocuments with SnapshotVersion.NONE are used in manufactured events. We remove
            // these documents from cache since we lost access.
            removedDocs.add(doc.getKey());
            changedDocs.put(key, doc);
        } else if (!existingDoc.isValidDocument() || doc.getVersion().compareTo(existingDoc.getVersion()) > 0 || (doc.getVersion().compareTo(existingDoc.getVersion()) == 0 && existingDoc.hasPendingWrites())) {
            hardAssert(!SnapshotVersion.NONE.equals(doc.getReadTime()), "Cannot add a document when the remote version is zero");
            remoteDocuments.add(doc, doc.getReadTime());
            changedDocs.put(key, doc);
        } else {
            Logger.debug("LocalStore", "Ignoring outdated watch update for %s." + "Current version: %s  Watch version: %s", key, existingDoc.getVersion(), doc.getVersion());
        }
    }
    remoteDocuments.removeAll(removedDocs);
    return new DocumentChangeResult(changedDocs, conditionChanged);
}
Also used : HashMap(java.util.HashMap) DocumentKey(com.google.firebase.firestore.model.DocumentKey) MutableDocument(com.google.firebase.firestore.model.MutableDocument) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet)

Example 8 with MutableDocument

use of com.google.firebase.firestore.model.MutableDocument in project firebase-android-sdk by firebase.

the class LocalStore method applyBundledDocuments.

@Override
public ImmutableSortedMap<DocumentKey, Document> applyBundledDocuments(ImmutableSortedMap<DocumentKey, MutableDocument> documents, String bundleId) {
    // Allocates a target to hold all document keys from the bundle, such that
    // they will not get garbage collected right away.
    TargetData umbrellaTargetData = allocateTarget(newUmbrellaTarget(bundleId));
    return persistence.runTransaction("Apply bundle documents", () -> {
        ImmutableSortedSet<DocumentKey> documentKeys = DocumentKey.emptyKeySet();
        Map<DocumentKey, MutableDocument> documentMap = new HashMap<>();
        for (Entry<DocumentKey, MutableDocument> entry : documents) {
            DocumentKey documentKey = entry.getKey();
            MutableDocument document = entry.getValue();
            if (document.isFoundDocument()) {
                documentKeys = documentKeys.insert(documentKey);
            }
            documentMap.put(documentKey, document);
        }
        targetCache.removeMatchingKeysForTargetId(umbrellaTargetData.getTargetId());
        targetCache.addMatchingKeys(documentKeys, umbrellaTargetData.getTargetId());
        DocumentChangeResult result = populateDocumentChanges(documentMap);
        Map<DocumentKey, MutableDocument> changedDocs = result.changedDocuments;
        return localDocuments.getLocalViewOfDocuments(changedDocs, result.existenceChangedKeys);
    });
}
Also used : HashMap(java.util.HashMap) DocumentKey(com.google.firebase.firestore.model.DocumentKey) MutableDocument(com.google.firebase.firestore.model.MutableDocument)

Example 9 with MutableDocument

use of com.google.firebase.firestore.model.MutableDocument 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 10 with MutableDocument

use of com.google.firebase.firestore.model.MutableDocument in project firebase-android-sdk by firebase.

the class SQLiteRemoteDocumentCache method processRowInBackground.

private void processRowInBackground(BackgroundQueue backgroundQueue, Map<DocumentKey, MutableDocument> results, Cursor row) {
    byte[] rawDocument = row.getBlob(0);
    int readTimeSeconds = row.getInt(1);
    int readTimeNanos = row.getInt(2);
    // Since scheduling background tasks incurs overhead, we only dispatch to a
    // background thread if there are still some documents remaining.
    Executor executor = row.isLast() ? Executors.DIRECT_EXECUTOR : backgroundQueue;
    executor.execute(() -> {
        MutableDocument document = decodeMaybeDocument(rawDocument, readTimeSeconds, readTimeNanos);
        synchronized (results) {
            results.put(document.getKey(), document);
        }
    });
}
Also used : Executor(java.util.concurrent.Executor) MutableDocument(com.google.firebase.firestore.model.MutableDocument)

Aggregations

MutableDocument (com.google.firebase.firestore.model.MutableDocument)166 Test (org.junit.Test)125 DocumentKey (com.google.firebase.firestore.model.DocumentKey)43 Mutation.calculateOverlayMutation (com.google.firebase.firestore.model.mutation.Mutation.calculateOverlayMutation)30 TestUtil.deleteMutation (com.google.firebase.firestore.testutil.TestUtil.deleteMutation)30 TestUtil.mergeMutation (com.google.firebase.firestore.testutil.TestUtil.mergeMutation)30 TestUtil.patchMutation (com.google.firebase.firestore.testutil.TestUtil.patchMutation)30 TestUtil.setMutation (com.google.firebase.firestore.testutil.TestUtil.setMutation)30 HashMap (java.util.HashMap)22 TestUtil.wrapObject (com.google.firebase.firestore.testutil.TestUtil.wrapObject)18 ArrayList (java.util.ArrayList)18 TargetData (com.google.firebase.firestore.local.TargetData)15 WatchTargetChange (com.google.firebase.firestore.remote.WatchChange.WatchTargetChange)14 DocumentChange (com.google.firebase.firestore.remote.WatchChange.DocumentChange)13 ResourcePath (com.google.firebase.firestore.model.ResourcePath)12 Query (com.google.firebase.firestore.core.Query)10 Map (java.util.Map)10 SnapshotVersion (com.google.firebase.firestore.model.SnapshotVersion)8 Document (com.google.firebase.firestore.model.Document)7 Timestamp (com.google.firebase.Timestamp)6