Skip to content

Commit 07372a7

Browse files
Mutable Documents (#7722)
1 parent 4f537e7 commit 07372a7

File tree

2 files changed

+380
-0
lines changed

2 files changed

+380
-0
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Copyright 2021 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "Firestore/core/src/model/mutable_document.h"
18+
19+
#include <sstream>
20+
21+
namespace firebase {
22+
namespace firestore {
23+
namespace model {
24+
25+
/* static */
26+
MutableDocument MutableDocument::InvalidDocument(
27+
const DocumentKey& document_key) {
28+
return {document_key, DocumentType::kInvalid, SnapshotVersion::None(),
29+
ObjectValue{}, DocumentState::kSynced};
30+
}
31+
32+
/* static */
33+
MutableDocument MutableDocument::FoundDocument(const DocumentKey& document_key,
34+
const SnapshotVersion& version,
35+
ObjectValue value) {
36+
return std::move(InvalidDocument(document_key)
37+
.ConvertToFoundDocument(version, std::move(value)));
38+
}
39+
40+
/* static */
41+
MutableDocument MutableDocument::NoDocument(const DocumentKey& document_key,
42+
const SnapshotVersion& version) {
43+
return std::move(InvalidDocument(document_key).ConvertToNoDocument(version));
44+
}
45+
46+
/* static */
47+
MutableDocument MutableDocument::UnknownDocument(
48+
const DocumentKey& document_key, const SnapshotVersion& version) {
49+
return std::move(
50+
InvalidDocument(document_key).ConvertToUnknownDocument(version));
51+
}
52+
53+
MutableDocument& MutableDocument::ConvertToFoundDocument(
54+
const SnapshotVersion& version, ObjectValue value) {
55+
version_ = version;
56+
document_type_ = DocumentType::kFoundDocument;
57+
value_ = std::move(value);
58+
document_state_ = DocumentState::kSynced;
59+
return *this;
60+
}
61+
62+
MutableDocument& MutableDocument::ConvertToNoDocument(
63+
const SnapshotVersion& version) {
64+
version_ = version;
65+
document_type_ = DocumentType::kNoDocument;
66+
value_ = {};
67+
document_state_ = DocumentState::kSynced;
68+
return *this;
69+
}
70+
71+
MutableDocument& MutableDocument::ConvertToUnknownDocument(
72+
const SnapshotVersion& version) {
73+
version_ = version;
74+
document_type_ = DocumentType::kUnknownDocument;
75+
value_ = {};
76+
document_state_ = DocumentState::kHasCommittedMutations;
77+
return *this;
78+
}
79+
80+
MutableDocument& MutableDocument::SetHasCommittedMutations() {
81+
document_state_ = DocumentState::kHasCommittedMutations;
82+
return *this;
83+
}
84+
85+
MutableDocument& MutableDocument::SetHasLocalMutations() {
86+
document_state_ = DocumentState::kHasLocalMutations;
87+
return *this;
88+
}
89+
90+
std::string MutableDocument::ToString() const {
91+
std::stringstream stream;
92+
stream << "MutableDocument(key=" << key_ << ", type=" << document_type_
93+
<< ", version=" << version_ << ", value=" << value_
94+
<< ", state=" << document_state_;
95+
return stream.str();
96+
}
97+
98+
bool operator==(const MutableDocument& lhs, const MutableDocument& rhs) {
99+
return lhs.key_ == rhs.key_ && lhs.document_type_ == rhs.document_type_ &&
100+
lhs.version_ == rhs.version_ &&
101+
lhs.document_state_ == rhs.document_state_ && lhs.value_ == rhs.value_;
102+
}
103+
104+
std::ostream& operator<<(std::ostream& os,
105+
MutableDocument::DocumentState state) {
106+
switch (state) {
107+
case MutableDocument::DocumentState::kHasCommittedMutations:
108+
return os << "kHasCommittedMutations";
109+
case MutableDocument::DocumentState::kHasLocalMutations:
110+
return os << "kHasLocalMutations";
111+
case MutableDocument::DocumentState::kSynced:
112+
return os << "kSynced";
113+
}
114+
115+
UNREACHABLE();
116+
}
117+
118+
std::ostream& operator<<(std::ostream& os,
119+
MutableDocument::DocumentType state) {
120+
switch (state) {
121+
case MutableDocument::DocumentType::kInvalid:
122+
return os << "kInvalid";
123+
case MutableDocument::DocumentType::kFoundDocument:
124+
return os << "kFoundDocument";
125+
case MutableDocument::DocumentType::kNoDocument:
126+
return os << "kNoDocument";
127+
case MutableDocument::DocumentType::kUnknownDocument:
128+
return os << "kUnknownDocument";
129+
}
130+
131+
UNREACHABLE();
132+
}
133+
134+
} // namespace model
135+
} // namespace firestore
136+
} // namespace firebase
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
/*
2+
* Copyright 2021 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef FIRESTORE_CORE_SRC_MODEL_MUTABLE_DOCUMENT_H_
18+
#define FIRESTORE_CORE_SRC_MODEL_MUTABLE_DOCUMENT_H_
19+
20+
#include <ostream>
21+
#include <string>
22+
#include <utility>
23+
24+
#include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h"
25+
#include "Firestore/core/src/model/document_key.h"
26+
#include "Firestore/core/src/model/field_value.h"
27+
#include "Firestore/core/src/model/snapshot_version.h"
28+
29+
// TODO(mutabledocuments): We might want to call this class Document and replace
30+
// the existing class.
31+
32+
/**
33+
* Represents a document in Firestore with a key, version, data and whether it
34+
* has local mutations applied to it.
35+
*
36+
* Documents can transition between states via `ConvertToFoundDocument()`,
37+
* `ConvertToNoDocument()` and `ConvertToUnknownDocument()`. If a document does
38+
* not transition to one of these states even after all mutations have been
39+
* applied, `is_valid_document()` returns false and the document should be
40+
* removed from all views.
41+
*/
42+
namespace firebase {
43+
namespace firestore {
44+
namespace model {
45+
46+
class MutableDocument {
47+
private:
48+
enum class DocumentType {
49+
/**
50+
* Represents the initial state of a MutableDocument when only the document
51+
* key is known. Invalid documents transition to other states as mutations
52+
* are applied. If a document remains invalid after applying mutations, it
53+
* should be discarded.
54+
*/
55+
kInvalid,
56+
/**
57+
* Represents a document in Firestore with a key, version, data and whether
58+
* the data has local mutations applied to it.
59+
*/
60+
kFoundDocument,
61+
/** Represents that no documents exists for the key at the given version. */
62+
kNoDocument,
63+
/**
64+
* Represents an existing document whose data is unknown (e.g. a document
65+
* that was updated without a known base document).
66+
*/
67+
kUnknownDocument
68+
};
69+
70+
/** Describes the `hasPendingWrites` state of a document. */
71+
enum class DocumentState {
72+
/**
73+
* Local mutations applied via the mutation queue. Document is potentially
74+
* inconsistent.
75+
*/
76+
kHasLocalMutations,
77+
/**
78+
* Mutations applied based on a write acknowledgment. Document is
79+
* potentially inconsistent.
80+
*/
81+
kHasCommittedMutations,
82+
/** No mutations applied. Document was sent to us by Watch. */
83+
kSynced
84+
};
85+
86+
public:
87+
// MutableDocument contain Proto data that cannot be implicitly created or
88+
// copied.
89+
MutableDocument() = delete;
90+
MutableDocument(const MutableDocument&) = delete;
91+
MutableDocument& operator=(const MutableDocument&) = delete;
92+
93+
MutableDocument(MutableDocument&& other) noexcept
94+
: key_{std::move(other.key_)},
95+
document_type_{other.document_type_},
96+
version_{std::move(other.version_)},
97+
value_{std::move(other.value_)},
98+
document_state_{other.document_state_} {
99+
}
100+
101+
/**
102+
* Creates a document with no known version or data. This document can serve
103+
* as a base document for mutations.
104+
*/
105+
static MutableDocument InvalidDocument(const DocumentKey& document_key);
106+
107+
/**
108+
* Creates a new document that is known to exist with the given data at the
109+
* given version.
110+
*/
111+
static MutableDocument FoundDocument(const DocumentKey& document_key,
112+
const SnapshotVersion& version,
113+
ObjectValue value);
114+
115+
/** Creates a new document that is known to not exisr at the given version. */
116+
static MutableDocument NoDocument(const DocumentKey& document_key,
117+
const SnapshotVersion& version);
118+
119+
/**
120+
* Creates a new document that is known to exist at the given version but
121+
* whose data is not known (e.g. a document that was updated without a known
122+
* base document).
123+
*/
124+
static MutableDocument UnknownDocument(const DocumentKey& document_key,
125+
const SnapshotVersion& version);
126+
127+
/**
128+
* Changes the document type to indicate that it exists and that its version
129+
* and data are known.
130+
*/
131+
MutableDocument& ConvertToFoundDocument(const SnapshotVersion& version,
132+
ObjectValue value);
133+
134+
/**
135+
* Changes the document type to indicate that it doesn't exist at the given
136+
* version.
137+
*/
138+
MutableDocument& ConvertToNoDocument(const SnapshotVersion& version);
139+
140+
/**
141+
* Changes the document type to indicate that it exists at a given version but
142+
* that its data is not known (e.g. a document that was updated without a
143+
* known base document).
144+
*/
145+
MutableDocument& ConvertToUnknownDocument(const SnapshotVersion& version);
146+
147+
MutableDocument& SetHasCommittedMutations();
148+
149+
MutableDocument& SetHasLocalMutations();
150+
151+
const DocumentKey& key() const {
152+
return key_;
153+
}
154+
155+
const SnapshotVersion& version() const {
156+
return version_;
157+
}
158+
159+
bool has_local_mutations() const {
160+
return document_state_ == DocumentState::kHasLocalMutations;
161+
}
162+
163+
bool has_committed_mutations() const {
164+
return document_state_ == DocumentState::kHasCommittedMutations;
165+
}
166+
167+
bool has_pending_writes() const {
168+
return has_local_mutations() || has_committed_mutations();
169+
}
170+
171+
const ObjectValue& data() const {
172+
return value_;
173+
}
174+
175+
/**
176+
* Returns the value at the given path or absl::nullopt. If the path is empty,
177+
* an identical copy of the FieldValue is returned.
178+
*
179+
* @param field_path the path to search.
180+
* @return The value at the path or absl::nullopt if it doesn't exist.
181+
*/
182+
absl::optional<FieldValue> field(const FieldPath& field_path) const {
183+
return value_.Get(field_path);
184+
}
185+
186+
bool is_valid_document() const {
187+
return document_type_ != DocumentType ::kInvalid;
188+
}
189+
190+
bool is_found_document() const {
191+
return document_type_ == DocumentType ::kFoundDocument;
192+
}
193+
194+
bool is_no_document() const {
195+
return document_type_ == DocumentType ::kNoDocument;
196+
}
197+
198+
bool is_unknown_document() const {
199+
return document_type_ == DocumentType ::kUnknownDocument;
200+
}
201+
202+
std::string ToString() const;
203+
204+
friend bool operator==(const MutableDocument& lhs,
205+
const MutableDocument& rhs);
206+
207+
friend std::ostream& operator<<(std::ostream& os, DocumentState state);
208+
friend std::ostream& operator<<(std::ostream& os, DocumentType type);
209+
210+
private:
211+
MutableDocument(DocumentKey key,
212+
DocumentType document_type,
213+
SnapshotVersion version,
214+
ObjectValue value,
215+
DocumentState document_state)
216+
: key_{std::move(key)},
217+
document_type_{document_type},
218+
version_{std::move(version)},
219+
value_{std::move(value)},
220+
document_state_{document_state} {
221+
}
222+
223+
DocumentKey key_;
224+
DocumentType document_type_ = DocumentType::kInvalid;
225+
SnapshotVersion version_;
226+
ObjectValue value_;
227+
DocumentState document_state_ = DocumentState::kSynced;
228+
};
229+
230+
bool operator==(const MutableDocument& lhs, const MutableDocument& rhs);
231+
232+
std::ostream& operator<<(std::ostream& os, const MutableDocument& doc) {
233+
return os << doc.ToString();
234+
}
235+
236+
inline bool operator!=(const MutableDocument& lhs, const MutableDocument& rhs) {
237+
return !(lhs == rhs);
238+
}
239+
240+
} // namespace model
241+
} // namespace firestore
242+
} // namespace firebase
243+
244+
#endif // FIRESTORE_CORE_SRC_MODEL_MUTABLE_DOCUMENT_H_

0 commit comments

Comments
 (0)