@@ -23,6 +23,44 @@ import { ObjectValue } from './object_value';
23
23
import { FieldPath } from './path' ;
24
24
import { valueCompare } from './values' ;
25
25
26
+ const enum DocumentType {
27
+ /**
28
+ * Represents the initial state of a MutableDocument when only the document
29
+ * key is known. Invalid documents transition to other states as mutations are
30
+ * applied. If a document remains invalid after applying mutations, it should
31
+ * be discarded.
32
+ */
33
+ INVALID ,
34
+ /**
35
+ * Represents a document in Firestore with a key, version, data and whether
36
+ * the data has local mutations applied to it.
37
+ */
38
+ FOUND_DOCUMENT ,
39
+ /** Represents that no documents exists for the key at the given version. */
40
+ NO_DOCUMENT ,
41
+ /**
42
+ * Represents an existing document whose data is unknown (e.g. a document that
43
+ * was updated without a known base document).
44
+ */
45
+ UNKNOWN_DOCUMENT
46
+ }
47
+
48
+ /** Describes the `hasPendingWrites` state of a document. */
49
+ const enum DocumentState {
50
+ /** No mutations applied. Document was sent to us by Watch. */
51
+ SYNCED ,
52
+ /**
53
+ * Local mutations applied via the mutation queue. Document is potentially
54
+ * inconsistent.
55
+ */
56
+ HAS_LOCAL_MUTATIONS ,
57
+ /**
58
+ * Mutations applied based on a write acknowledgment. Document is potentially
59
+ * inconsistent.
60
+ */
61
+ HAS_COMMITTED_MUTATIONS
62
+ }
63
+
26
64
/**
27
65
* Represents a document in Firestore with a key, version, data and whether the
28
66
* data has local mutations applied to it.
@@ -80,6 +118,199 @@ export interface Document {
80
118
toString ( ) : string ;
81
119
}
82
120
121
+ /**
122
+ * Represents a document in Firestore with a key, version, data and whether it
123
+ * has local mutations applied to it.
124
+ *
125
+ * Documents can transition between states via `convertToFoundDocument()`,
126
+ * `convertToNoDocument()` and `convertToUnknownDocument()`. If a document does
127
+ * not transition to one of these states even after all mutations have been
128
+ * applied, `isValidDocument()` returns false and the document should be removed
129
+ * from all views.
130
+ */
131
+ export class MutableDocument implements Document {
132
+ private constructor (
133
+ readonly key : DocumentKey ,
134
+ private documentType : DocumentType ,
135
+ public version : SnapshotVersion ,
136
+ public data : ObjectValue ,
137
+ private documentState : DocumentState
138
+ ) { }
139
+
140
+ /**
141
+ * Creates a document with no known version or data, but which can serve as
142
+ * base document for mutations.
143
+ */
144
+ static newInvalidDocument ( documentKey : DocumentKey ) : MutableDocument {
145
+ return new MutableDocument (
146
+ documentKey ,
147
+ DocumentType . INVALID ,
148
+ SnapshotVersion . min ( ) ,
149
+ ObjectValue . empty ( ) ,
150
+ DocumentState . SYNCED
151
+ ) ;
152
+ }
153
+
154
+ /**
155
+ * Creates a new document that is known to exist with the given data at the
156
+ * given version.
157
+ */
158
+ static newFoundDocument (
159
+ documentKey : DocumentKey ,
160
+ version : SnapshotVersion ,
161
+ value : ObjectValue
162
+ ) : MutableDocument {
163
+ return new MutableDocument (
164
+ documentKey ,
165
+ DocumentType . FOUND_DOCUMENT ,
166
+ version ,
167
+ value ,
168
+ DocumentState . SYNCED
169
+ ) ;
170
+ }
171
+
172
+ /** Creates a new document that is known to not exist at the given version. */
173
+ static newNoDocument (
174
+ documentKey : DocumentKey ,
175
+ version : SnapshotVersion
176
+ ) : MutableDocument {
177
+ return new MutableDocument (
178
+ documentKey ,
179
+ DocumentType . NO_DOCUMENT ,
180
+ version ,
181
+ ObjectValue . empty ( ) ,
182
+ DocumentState . SYNCED
183
+ ) ;
184
+ }
185
+
186
+ /**
187
+ * Creates a new document that is known to exist at the given version but
188
+ * whose data is not known (e.g. a document that was updated without a known
189
+ * base document).
190
+ */
191
+ static newUnknownDocument (
192
+ documentKey : DocumentKey ,
193
+ version : SnapshotVersion
194
+ ) : MutableDocument {
195
+ return new MutableDocument (
196
+ documentKey ,
197
+ DocumentType . UNKNOWN_DOCUMENT ,
198
+ version ,
199
+ ObjectValue . empty ( ) ,
200
+ DocumentState . HAS_COMMITTED_MUTATIONS
201
+ ) ;
202
+ }
203
+
204
+ /**
205
+ * Changes the document type to indicate that it exists and that its version
206
+ * and data are known.
207
+ */
208
+ convertToFoundDocument (
209
+ version : SnapshotVersion ,
210
+ value : ObjectValue
211
+ ) : MutableDocument {
212
+ this . version = version ;
213
+ this . documentType = DocumentType . FOUND_DOCUMENT ;
214
+ this . data = value ;
215
+ this . documentState = DocumentState . SYNCED ;
216
+ return this ;
217
+ }
218
+
219
+ /**
220
+ * Changes the document type to indicate that it doesn't exist at the given
221
+ * version.
222
+ */
223
+ convertToNoDocument ( version : SnapshotVersion ) : MutableDocument {
224
+ this . version = version ;
225
+ this . documentType = DocumentType . NO_DOCUMENT ;
226
+ this . data = ObjectValue . empty ( ) ;
227
+ this . documentState = DocumentState . SYNCED ;
228
+ return this ;
229
+ }
230
+
231
+ /**
232
+ * Changes the document type to indicate that it exists at a given version but
233
+ * that is data is not known (e.g. a document that was updated without a known
234
+ * base document).
235
+ */
236
+ convertToUnknownDocument ( version : SnapshotVersion ) : MutableDocument {
237
+ this . version = version ;
238
+ this . documentType = DocumentType . UNKNOWN_DOCUMENT ;
239
+ this . data = ObjectValue . empty ( ) ;
240
+ this . documentState = DocumentState . HAS_COMMITTED_MUTATIONS ;
241
+ return this ;
242
+ }
243
+
244
+ setHasCommittedMutations ( ) : MutableDocument {
245
+ this . documentState = DocumentState . HAS_COMMITTED_MUTATIONS ;
246
+ return this ;
247
+ }
248
+
249
+ setHasLocalMutations ( ) : MutableDocument {
250
+ this . documentState = DocumentState . HAS_LOCAL_MUTATIONS ;
251
+ return this ;
252
+ }
253
+
254
+ get hasLocalMutations ( ) : boolean {
255
+ return this . documentState === DocumentState . HAS_LOCAL_MUTATIONS ;
256
+ }
257
+
258
+ get hasCommittedMutations ( ) : boolean {
259
+ return this . documentState === DocumentState . HAS_COMMITTED_MUTATIONS ;
260
+ }
261
+
262
+ get hasPendingWrites ( ) : boolean {
263
+ return this . hasLocalMutations || this . hasCommittedMutations ;
264
+ }
265
+
266
+ isValidDocument ( ) : boolean {
267
+ return this . documentType !== DocumentType . INVALID ;
268
+ }
269
+
270
+ isFoundDocument ( ) : boolean {
271
+ return this . documentType === DocumentType . FOUND_DOCUMENT ;
272
+ }
273
+
274
+ isNoDocument ( ) : boolean {
275
+ return this . documentType === DocumentType . NO_DOCUMENT ;
276
+ }
277
+
278
+ isUnknownDocument ( ) : boolean {
279
+ return this . documentType === DocumentType . UNKNOWN_DOCUMENT ;
280
+ }
281
+
282
+ isEqual ( other : Document | null | undefined ) : boolean {
283
+ return (
284
+ other instanceof MutableDocument &&
285
+ this . key . isEqual ( other . key ) &&
286
+ this . version . isEqual ( other . version ) &&
287
+ this . documentType === other . documentType &&
288
+ this . documentState === other . documentState &&
289
+ this . data . isEqual ( other . data )
290
+ ) ;
291
+ }
292
+
293
+ clone ( ) : MutableDocument {
294
+ return new MutableDocument (
295
+ this . key ,
296
+ this . documentType ,
297
+ this . version ,
298
+ this . data . clone ( ) ,
299
+ this . documentState
300
+ ) ;
301
+ }
302
+
303
+ toString ( ) : string {
304
+ return (
305
+ `Document(${ this . key } , ${ this . version } , ${ JSON . stringify (
306
+ this . data . toProto ( )
307
+ ) } , ` +
308
+ `{documentType: ${ this . documentType } }), ` +
309
+ `{documentState: ${ this . documentState } })`
310
+ ) ;
311
+ }
312
+ }
313
+
83
314
/**
84
315
* Compares the value for field `field` in the provided documents. Throws if
85
316
* the field does not exist in both documents.
0 commit comments