/* * Copyright 2021 Google LLC * * 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_MUTABLE_DOCUMENT_H_ #define FIRESTORE_CORE_SRC_MODEL_MUTABLE_DOCUMENT_H_ #include #include #include #include #include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h" #include "Firestore/core/src/model/document_key.h" #include "Firestore/core/src/model/object_value.h" #include "Firestore/core/src/model/snapshot_version.h" namespace firebase { namespace firestore { namespace model { /** * Represents a document in Firestore with a key, version, data and whether it * has local mutations applied to it. * * Documents can transition between states via `ConvertToFoundDocument()`, * `ConvertToNoDocument()` and `ConvertToUnknownDocument()`. If a document does * not transition to one of these states even after all mutations have been * applied, `is_valid_document()` returns false and the document should be * removed from all views. */ class MutableDocument { private: enum class DocumentType { /** * Represents the initial state of a MutableDocument when only the document * key is known. Invalid documents transition to other states as mutations * are applied. If a document remains invalid after applying mutations, it * should be discarded. */ kInvalid, /** * Represents a document in Firestore with a key, version, data and whether * the data has local mutations applied to it. */ kFoundDocument, /** Represents that no documents exists for the key at the given version. */ kNoDocument, /** * Represents an existing document whose data is unknown (e.g. a document * that was updated without a known base document). */ kUnknownDocument }; /** Describes the `hasPendingWrites` state of a document. */ enum class DocumentState { /** * Local mutations applied via the mutation queue. Document is potentially * inconsistent. */ kHasLocalMutations, /** * Mutations applied based on a write acknowledgment. Document is * potentially inconsistent. */ kHasCommittedMutations, /** No mutations applied. Document was sent to us by Watch. */ kSynced }; public: MutableDocument() = default; /** * Creates a document with no known version or data. This document can serve * as a base document for mutations. */ static MutableDocument InvalidDocument(DocumentKey document_key); /** * Creates a new document that is known to exist with the given data at the * given version. */ static MutableDocument FoundDocument(DocumentKey document_key, const SnapshotVersion& version, ObjectValue value); /** Creates a new document that is known to not exisr at the given version. */ static MutableDocument NoDocument(DocumentKey document_key, const SnapshotVersion& version); /** * Creates a new document that is known to exist at the given version but * whose data is not known (e.g. a document that was updated without a known * base document). */ static MutableDocument UnknownDocument(const DocumentKey& document_key, const SnapshotVersion& version); /** * Changes the document type to indicate that it exists and that its version * and data are known. */ MutableDocument& ConvertToFoundDocument(const SnapshotVersion& version, ObjectValue value); /** * Changes the document type to indicate that it exists and that its version * and data are known. */ MutableDocument& ConvertToFoundDocument(const SnapshotVersion& version); /** * Changes the document type to indicate that it doesn't exist at the given * version. */ MutableDocument& ConvertToNoDocument(const SnapshotVersion& version); /** * Changes the document type to indicate that it exists at a given version but * that its data is not known (e.g. a document that was updated without a * known base document). */ MutableDocument& ConvertToUnknownDocument(const SnapshotVersion& version); MutableDocument& SetHasCommittedMutations(); MutableDocument& SetHasLocalMutations(); MutableDocument& WithReadTime(const SnapshotVersion& read_time); /** Creates a new document with a copy of the document's data and state. */ MutableDocument Clone() const; const DocumentKey& key() const { return key_; } const SnapshotVersion& version() const { return version_; } const SnapshotVersion& read_time() const { return read_time_; } bool has_local_mutations() const { return document_state_ == DocumentState::kHasLocalMutations; } bool has_committed_mutations() const { return document_state_ == DocumentState::kHasCommittedMutations; } bool has_pending_writes() const { return has_local_mutations() || has_committed_mutations(); } google_firestore_v1_Value value() const { return value_->Get(); } ObjectValue& data() const { return *value_; } /** * Returns the value at the given path or absl::nullopt. If the path is empty, * an identical copy of the FieldValue is returned. * * @param field_path the path to search. * @return The value at the path or absl::nullopt if it doesn't exist. */ absl::optional field( const FieldPath& field_path) const { return value_->Get(field_path); } bool is_valid_document() const { return document_type_ != DocumentType ::kInvalid; } bool is_found_document() const { return document_type_ == DocumentType ::kFoundDocument; } bool is_no_document() const { return document_type_ == DocumentType ::kNoDocument; } bool is_unknown_document() const { return document_type_ == DocumentType ::kUnknownDocument; } size_t Hash() const; std::string ToString() const; friend bool operator==(const MutableDocument& lhs, const MutableDocument& rhs); friend std::ostream& operator<<(std::ostream& os, DocumentState state); friend std::ostream& operator<<(std::ostream& os, DocumentType type); private: MutableDocument(DocumentKey key, DocumentType document_type, SnapshotVersion version, SnapshotVersion read_time, std::shared_ptr value, DocumentState document_state) : key_{std::move(key)}, document_type_{document_type}, version_{version}, read_time_{read_time}, value_{std::move(value)}, document_state_{document_state} { } DocumentKey key_; DocumentType document_type_ = DocumentType::kInvalid; SnapshotVersion version_; SnapshotVersion read_time_; // Using a shared pointer to ObjectValue makes MutableDocument copy-assignable // without having to manually create a deep clone of its Protobuf contents. std::shared_ptr value_ = std::make_shared(); DocumentState document_state_ = DocumentState::kSynced; }; bool operator==(const MutableDocument& lhs, const MutableDocument& rhs); std::ostream& operator<<(std::ostream& os, const MutableDocument& doc); inline bool operator!=(const MutableDocument& lhs, const MutableDocument& rhs) { return !(lhs == rhs); } } // namespace model } // namespace firestore } // namespace firebase #endif // FIRESTORE_CORE_SRC_MODEL_MUTABLE_DOCUMENT_H_