Search in sources :

Example 1 with PatchMutation

use of com.google.firebase.firestore.model.mutation.PatchMutation 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", () -> {
        // 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.
        ImmutableSortedMap<DocumentKey, Document> documents = localDocuments.getDocuments(keys);
        // 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(documents.get(mutation.getKey()));
            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(documents);
        documentOverlayCache.saveOverlays(batch.getBatchId(), overlays);
        return new LocalDocumentsResult(batch.getBatchId(), documents);
    });
}
Also used : ArrayList(java.util.ArrayList) Document(com.google.firebase.firestore.model.Document) MutableDocument(com.google.firebase.firestore.model.MutableDocument) 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) HashSet(java.util.HashSet)

Example 2 with PatchMutation

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

the class TestUtil method patchMutationHelper.

private static PatchMutation patchMutationHelper(String path, Map<String, Object> values, Precondition precondition, @Nullable List<FieldPath> updateMask) {
    // Replace '<DELETE>' from JSON
    for (Entry<String, Object> entry : values.entrySet()) {
        if (entry.getValue().equals(DELETE_SENTINEL)) {
            values.put(entry.getKey(), FieldValue.delete());
        }
    }
    UserDataReader dataReader = new UserDataReader(DatabaseId.forProject("project"));
    ParsedUpdateData parsed = dataReader.parseUpdateData(values);
    // `mergeMutation()` provides an update mask for the merged fields, whereas `patchMutation()`
    // requires the update mask to be parsed from the values.
    Collection<FieldPath> mask = updateMask != null ? updateMask : parsed.getFieldMask().getMask();
    // We sort the fieldMaskPaths to make the order deterministic in tests. (Otherwise, when we
    // flatten a Set to a proto repeated field, we'll end up comparing in iterator order and
    // possibly consider {foo,bar} != {bar,foo}.)
    SortedSet<FieldPath> fieldMaskPaths = new TreeSet<>(mask);
    // The order of the transforms doesn't matter, but we sort them so tests can assume a particular
    // order.
    ArrayList<FieldTransform> fieldTransforms = new ArrayList<>(parsed.getFieldTransforms());
    Collections.sort(fieldTransforms, (ft1, ft2) -> ft1.getFieldPath().compareTo(ft2.getFieldPath()));
    return new PatchMutation(key(path), parsed.getData(), FieldMask.fromSet(fieldMaskPaths), precondition, fieldTransforms);
}
Also used : FieldPath(com.google.firebase.firestore.model.FieldPath) FieldTransform(com.google.firebase.firestore.model.mutation.FieldTransform) ArrayList(java.util.ArrayList) ByteString(com.google.protobuf.ByteString) ParsedUpdateData(com.google.firebase.firestore.core.UserData.ParsedUpdateData) UserDataReader(com.google.firebase.firestore.UserDataReader) TreeSet(java.util.TreeSet) PatchMutation(com.google.firebase.firestore.model.mutation.PatchMutation)

Example 3 with PatchMutation

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

the class RemoteSerializer method decodeMutation.

public Mutation decodeMutation(com.google.firestore.v1.Write mutation) {
    Precondition precondition = mutation.hasCurrentDocument() ? decodePrecondition(mutation.getCurrentDocument()) : Precondition.NONE;
    List<FieldTransform> fieldTransforms = new ArrayList<>();
    for (DocumentTransform.FieldTransform fieldTransform : mutation.getUpdateTransformsList()) {
        fieldTransforms.add(decodeFieldTransform(fieldTransform));
    }
    switch(mutation.getOperationCase()) {
        case UPDATE:
            if (mutation.hasUpdateMask()) {
                return new PatchMutation(decodeKey(mutation.getUpdate().getName()), ObjectValue.fromMap(mutation.getUpdate().getFieldsMap()), decodeDocumentMask(mutation.getUpdateMask()), precondition, fieldTransforms);
            } else {
                return new SetMutation(decodeKey(mutation.getUpdate().getName()), ObjectValue.fromMap(mutation.getUpdate().getFieldsMap()), precondition, fieldTransforms);
            }
        case DELETE:
            return new DeleteMutation(decodeKey(mutation.getDelete()), precondition);
        case VERIFY:
            return new VerifyMutation(decodeKey(mutation.getVerify()), precondition);
        default:
            throw fail("Unknown mutation operation: %d", mutation.getOperationCase());
    }
}
Also used : DocumentTransform(com.google.firestore.v1.DocumentTransform) DeleteMutation(com.google.firebase.firestore.model.mutation.DeleteMutation) Precondition(com.google.firebase.firestore.model.mutation.Precondition) FieldTransform(com.google.firebase.firestore.model.mutation.FieldTransform) ArrayList(java.util.ArrayList) PatchMutation(com.google.firebase.firestore.model.mutation.PatchMutation) VerifyMutation(com.google.firebase.firestore.model.mutation.VerifyMutation) SetMutation(com.google.firebase.firestore.model.mutation.SetMutation)

Example 4 with PatchMutation

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

the class LocalSerializerTest method testMultipleMutationsAreSquashed.

// TODO(b/174608374): Remove these tests once we perform a schema migration.
@Test
public void testMultipleMutationsAreSquashed() {
    // INPUT:
    // SetMutation -> SetMutation -> TransformMutation -> DeleteMutation -> PatchMutation ->
    // TransformMutation -> PatchMutation
    // OUTPUT (squashed):
    // SetMutation -> SetMutation -> DeleteMutation -> PatchMutation -> PatchMutation
    WriteBatch batchProto = com.google.firebase.firestore.proto.WriteBatch.newBuilder().setBatchId(42).addAllWrites(asList(setProto, setProto, transformProto, deleteProto, patchProto, transformProto, patchProto)).setLocalWriteTime(writeTimeProto).build();
    MutationBatch decoded = serializer.decodeMutationBatch(batchProto);
    assertEquals(5, decoded.getMutations().size());
    List<Write> allExpected = asList(new TestWriteBuilder().addSet().build(), new TestWriteBuilder().addSet().addUpdateTransforms().build(), new TestWriteBuilder().addDelete().build(), new TestWriteBuilder().addPatch().addUpdateTransforms().build(), new TestWriteBuilder().addPatch().build());
    for (int i = 0; i < decoded.getMutations().size(); i++) {
        Mutation mutation = decoded.getMutations().get(i);
        Write encoded = remoteSerializer.encodeMutation(mutation);
        assertEquals(allExpected.get(i), encoded);
    }
}
Also used : Write(com.google.firestore.v1.Write) MutationBatch(com.google.firebase.firestore.model.mutation.MutationBatch) PatchMutation(com.google.firebase.firestore.model.mutation.PatchMutation) Mutation(com.google.firebase.firestore.model.mutation.Mutation) SetMutation(com.google.firebase.firestore.model.mutation.SetMutation) TestUtil.deleteMutation(com.google.firebase.firestore.testutil.TestUtil.deleteMutation) TestUtil.setMutation(com.google.firebase.firestore.testutil.TestUtil.setMutation) WriteBatch(com.google.firebase.firestore.proto.WriteBatch) Test(org.junit.Test)

Example 5 with PatchMutation

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

the class LocalDocumentsView method computeViews.

/*Computes the local view for doc */
private ImmutableSortedMap<DocumentKey, Document> computeViews(Map<DocumentKey, MutableDocument> docs, Map<DocumentKey, Overlay> overlays, Set<DocumentKey> existenceStateChanged) {
    ImmutableSortedMap<DocumentKey, Document> results = emptyDocumentMap();
    Map<DocumentKey, MutableDocument> recalculateDocuments = new HashMap<>();
    for (MutableDocument doc : docs.values()) {
        Overlay overlay = overlays.get(doc.getKey());
        // but would now match.
        if (existenceStateChanged.contains(doc.getKey()) && (overlay == null || overlay.getMutation() instanceof PatchMutation)) {
            recalculateDocuments.put(doc.getKey(), doc);
        } else if (overlay != null) {
            overlay.getMutation().applyToLocalView(doc, null, Timestamp.now());
        }
    }
    recalculateAndSaveOverlays(recalculateDocuments);
    for (Map.Entry<DocumentKey, MutableDocument> entry : docs.entrySet()) {
        results = results.insert(entry.getKey(), entry.getValue());
    }
    return results;
}
Also used : HashMap(java.util.HashMap) DocumentKey(com.google.firebase.firestore.model.DocumentKey) MutableDocument(com.google.firebase.firestore.model.MutableDocument) PatchMutation(com.google.firebase.firestore.model.mutation.PatchMutation) Document(com.google.firebase.firestore.model.Document) MutableDocument(com.google.firebase.firestore.model.MutableDocument) Overlay(com.google.firebase.firestore.model.mutation.Overlay) HashMap(java.util.HashMap) Map(java.util.Map) TreeMap(java.util.TreeMap) ImmutableSortedMap(com.google.firebase.database.collection.ImmutableSortedMap) DocumentCollections.emptyDocumentMap(com.google.firebase.firestore.model.DocumentCollections.emptyDocumentMap)

Aggregations

PatchMutation (com.google.firebase.firestore.model.mutation.PatchMutation)7 MutationBatch (com.google.firebase.firestore.model.mutation.MutationBatch)4 Mutation (com.google.firebase.firestore.model.mutation.Mutation)3 SetMutation (com.google.firebase.firestore.model.mutation.SetMutation)3 WriteBatch (com.google.firebase.firestore.proto.WriteBatch)3 Write (com.google.firestore.v1.Write)3 ArrayList (java.util.ArrayList)3 Test (org.junit.Test)3 Document (com.google.firebase.firestore.model.Document)2 DocumentKey (com.google.firebase.firestore.model.DocumentKey)2 MutableDocument (com.google.firebase.firestore.model.MutableDocument)2 FieldTransform (com.google.firebase.firestore.model.mutation.FieldTransform)2 TestUtil.deleteMutation (com.google.firebase.firestore.testutil.TestUtil.deleteMutation)2 TestUtil.setMutation (com.google.firebase.firestore.testutil.TestUtil.setMutation)2 Timestamp (com.google.firebase.Timestamp)1 ImmutableSortedMap (com.google.firebase.database.collection.ImmutableSortedMap)1 UserDataReader (com.google.firebase.firestore.UserDataReader)1 ParsedUpdateData (com.google.firebase.firestore.core.UserData.ParsedUpdateData)1 DocumentCollections.emptyDocumentMap (com.google.firebase.firestore.model.DocumentCollections.emptyDocumentMap)1 FieldPath (com.google.firebase.firestore.model.FieldPath)1