| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- /*
- * Copyright 2018 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #ifndef FIRESTORE_CORE_SRC_MODEL_MUTATION_H_
- #define FIRESTORE_CORE_SRC_MODEL_MUTATION_H_
- #include <iosfwd>
- #include <map>
- #include <memory>
- #include <string>
- #include <utility>
- #include <vector>
- #include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h"
- #include "Firestore/core/src/model/document_key.h"
- #include "Firestore/core/src/model/field_mask.h"
- #include "Firestore/core/src/model/field_transform.h"
- #include "Firestore/core/src/model/object_value.h"
- #include "Firestore/core/src/model/precondition.h"
- #include "Firestore/core/src/model/snapshot_version.h"
- #include "Firestore/core/src/nanopb/message.h"
- #include "absl/types/optional.h"
- namespace firebase {
- namespace firestore {
- namespace model {
- class Document;
- class MutableDocument;
- /**
- * The result of applying a mutation to the server. This is a model of the
- * WriteResult proto message.
- *
- * Note that MutationResult does not name which document was mutated. The
- * association is implied positionally: for each entry in the array of
- * Mutations, there's a corresponding entry in the array of MutationResults.
- */
- class MutationResult {
- public:
- /** Takes ownership of `transform_results`. */
- MutationResult(
- SnapshotVersion version,
- nanopb::Message<google_firestore_v1_ArrayValue> transform_results)
- : version_(version), transform_results_{std::move(transform_results)} {
- }
- MutationResult(MutationResult&& other) noexcept
- : version_{other.version_},
- transform_results_{std::move(other.transform_results_)} {
- }
- /**
- * The version at which the mutation was committed.
- *
- * - For most operations, this is the update_time in the WriteResult.
- * - For deletes, it is the commit_time of the WriteResponse (because
- * deletes are not stored and have no update_time).
- *
- * Note that these versions can be different: No-op writes will not change
- * the update_time even though the commit_time advances.
- */
- const SnapshotVersion& version() const {
- return version_;
- }
- /**
- * The resulting fields returned from the backend after a TransformMutation
- * has been committed. Contains one FieldValue for each FieldTransform that
- * was in the mutation.
- *
- * Will be nullopt if the mutation was not a TransformMutation.
- */
- const nanopb::Message<google_firestore_v1_ArrayValue>& transform_results()
- const {
- return transform_results_;
- }
- std::string ToString() const;
- friend std::ostream& operator<<(std::ostream& os,
- const MutationResult& result);
- friend bool operator==(const MutationResult& lhs, const MutationResult& rhs);
- private:
- SnapshotVersion version_;
- nanopb::Message<google_firestore_v1_ArrayValue> transform_results_;
- };
- /**
- * Represents a Mutation of a document. Different subclasses of Mutation will
- * perform different kinds of changes to a base document. For example, a
- * SetMutation replaces the value of a document and a DeleteMutation deletes a
- * document.
- *
- * In addition to the value of the document mutations also operate on the
- * version. For local mutations (mutations that haven't been committed yet), we
- * preserve the existing version for Set and Patch mutations. For
- * local deletes, we reset the version to 0.
- *
- * Here's the expected transition table.
- *
- * MUTATION APPLIED TO RESULTS IN
- * SetMutation Document(v3) Document(v3)
- * SetMutation NoDocument(v3) Document(v0)
- * SetMutation null Document(v0)
- * PatchMutation Document(v3) Document(v3)
- * PatchMutation NoDocument(v3) NoDocument(v3)
- * PatchMutation null null
- * DeleteMutation Document(v3) NoDocument(v0)
- * DeleteMutation NoDocument(v3) NoDocument(v0)
- * DeleteMutation null NoDocument(v0)
- *
- * For acknowledged mutations, we use the update_time of the WriteResponse as
- * the resulting version for Set and Patch mutations. As deletes have no
- * explicit update time, we use the commit_time of the WriteResponse for
- * acknowledged deletes.
- *
- * If a mutation is acknowledged by the backend but fails the precondition
- * check locally, we return an `UnknownDocument` and rely on Watch to send us
- * the updated version.
- *
- * Field transforms are used only with Patch and Set Mutations. We use the
- * `updateTransforms` field to store transforms, rather than the `transforms`
- * message.
- *
- * Note: Mutation and its subclasses are specially designed to avoid slicing.
- * You can assign a subclass of Mutation to an instance of Mutation and the
- * full value is preserved, unsliced. Each subclass declares an explicit
- * constructor that can recover the derived type. This means that code like
- * this will work:
- *
- * SetMutation set(...);
- * Mutation mutation = set;
- * SetMutation recovered(mutation);
- *
- * The final line results in an explicit check that will fail if the type of
- * the underlying data is not actually Type::Set.
- */
- class Mutation {
- public:
- /**
- * Represents the mutation type. This is used in place of dynamic_cast.
- */
- enum class Type { Set, Patch, Delete, Verify };
- /** Creates an invalid mutation. */
- Mutation() = default;
- /**
- * Returns true if the given mutation is a valid instance. Default constructed
- * and moved-from Mutations are not valid.
- */
- bool is_valid() const {
- return rep_ != nullptr;
- }
- /** The runtime type of this mutation. */
- Type type() const {
- return rep().type();
- }
- const DocumentKey& key() const {
- return rep().key();
- }
- const Precondition& precondition() const {
- return rep().precondition();
- }
- const std::vector<FieldTransform>& field_transforms() const {
- return rep().field_transforms();
- }
- const absl::optional<FieldMask>& field_mask() const {
- return rep().field_mask();
- }
- /**
- * Applies this mutation to the given Document for the purposes of computing
- * the committed state of the document after the server has acknowledged that
- * this mutation has been successfully committed.
- *
- * If the input document doesn't match the expected state (e.g. it is invalid
- * or outdated), the document state may transition to unknown.
- *
- * @param document The document to mutate.
- * @param mutation_result The backend's response of successfully applying the
- * mutation.
- */
- void ApplyToRemoteDocument(MutableDocument& document,
- const MutationResult& mutation_result) const;
- /**
- * Estimates the latency compensated view of this mutation applied to the
- * given MaybeDocument.
- *
- * Unlike ApplyToRemoteDocument, this method is used before the mutation has
- * been committed and so it's possible that the mutation is operating on a
- * locally non-existent document and may produce a non-existent document.
- *
- * @param document The document to mutate.
- * @param previous_mask The fields that have been updated before applying this
- * mutation.
- * @param local_write_time A timestamp indicating the local write time of the
- * batch this mutation is a part of.
- * @return A `FieldMask` representing the fields that are changed by applying
- * this mutation, or `absl::nullopt` if the entire document is overwritten.
- */
- absl::optional<FieldMask> ApplyToLocalView(
- MutableDocument& document,
- absl::optional<FieldMask> previous_mask,
- const Timestamp& local_write_time) const;
- /**
- * If this mutation is not idempotent, returns the base value to persist with
- * this mutation. If a base value is returned, the mutation is always
- * applied to this base value, even if document has already been updated.
- *
- * The base value is a sparse object that consists of only the document
- * fields for which this mutation contains a non-idempotent transformation
- * (e.g. a numeric increment). The provided value guarantees consistent
- * behavior for non-idempotent transforms and allow us to return the same
- * latency-compensated value even if the backend has already applied the
- * mutation. The base value is empty for idempotent mutations, as they can be
- * re-played even if the backend has already applied them.
- *
- * @return a base value to store along with the mutation, or empty for
- * idempotent mutations.
- */
- absl::optional<ObjectValue> ExtractTransformBaseValue(
- const Document& document) const {
- return rep_->ExtractTransformBaseValue(document);
- }
- /**
- * A utility method to calculate an `Mutation` representing the overlay from
- * the final state of the document, and a `FieldMask` representing the fields
- * that are mutated by the local mutations, or a `absl::nullopt` if all fields
- * should be overwritten.
- *
- * Returns a mutation that can be applied to a base document to get `doc`, or
- * `nullopt` if base document and `doc` is the same.
- */
- static absl::optional<Mutation> CalculateOverlayMutation(
- const MutableDocument& doc, const absl::optional<FieldMask>& mask);
- friend bool operator==(const Mutation& lhs, const Mutation& rhs);
- size_t Hash() const {
- return rep().Hash();
- }
- std::string ToString() const {
- return rep_ ? rep().ToString() : "(invalid)";
- }
- friend std::ostream& operator<<(std::ostream& os, const Mutation& mutation);
- protected:
- class Rep {
- public:
- Rep(DocumentKey&& key, Precondition&& precondition);
- Rep(DocumentKey&& key,
- Precondition&& precondition,
- std::vector<FieldTransform>&& field_transforms);
- Rep(DocumentKey&& key,
- Precondition&& precondition,
- std::vector<FieldTransform>&& field_transforms,
- absl::optional<FieldMask>&& mask);
- virtual ~Rep() = default;
- virtual Type type() const = 0;
- const DocumentKey& key() const {
- return key_;
- }
- const Precondition& precondition() const {
- return precondition_;
- }
- const std::vector<FieldTransform>& field_transforms() const {
- return field_transforms_;
- }
- const absl::optional<FieldMask>& field_mask() const {
- return mask_;
- }
- virtual void ApplyToRemoteDocument(
- MutableDocument& document,
- const MutationResult& mutation_result) const = 0;
- virtual absl::optional<FieldMask> ApplyToLocalView(
- MutableDocument& document,
- absl::optional<FieldMask> previous_mask,
- const Timestamp& local_write_time) const = 0;
- virtual absl::optional<ObjectValue> ExtractTransformBaseValue(
- const Document& document) const;
- /**
- * Creates a map of "transform results" (a transform result is a field
- * value representing the result of applying a transform) for use after a
- * mutation containing transforms has been acknowledged by the server.
- *
- * @param previous_data The state of the data before applying this mutation.
- * @param server_transform_results The transform results received by the
- * server.
- * @return A map of fields to transform results.
- */
- TransformMap ServerTransformResults(
- const ObjectValue& previous_data,
- const nanopb::Message<google_firestore_v1_ArrayValue>&
- server_transform_results) const;
- /**
- * Creates a map of "transform results" (a transform result is a field
- * value representing the result of applying a transform) for use when
- * applying a transform locally.
- *
- * @param previous_data The state of the data before applying this mutation.
- * @param local_write_time The local time of the mutation (used to generate
- * ServerTimestampValues).
- * @return A map of fields to transform results.
- */
- TransformMap LocalTransformResults(const ObjectValue& previous_data,
- const Timestamp& local_write_time) const;
- virtual bool Equals(const Rep& other) const;
- virtual size_t Hash() const;
- virtual std::string ToString() const = 0;
- protected:
- void VerifyKeyMatches(const MutableDocument& document) const;
- static SnapshotVersion GetPostMutationVersion(
- const MutableDocument& document);
- private:
- DocumentKey key_;
- Precondition precondition_;
- std::vector<FieldTransform> field_transforms_;
- absl::optional<FieldMask> mask_;
- };
- explicit Mutation(std::shared_ptr<Rep>&& rep) : rep_(std::move(rep)) {
- }
- const Rep& rep() const {
- return *NOT_NULL(rep_);
- }
- private:
- std::shared_ptr<Rep> rep_;
- };
- inline bool operator!=(const Mutation& lhs, const Mutation& rhs) {
- return !(lhs == rhs);
- }
- } // namespace model
- } // namespace firestore
- } // namespace firebase
- #endif // FIRESTORE_CORE_SRC_MODEL_MUTATION_H_
|