17
17
import static com .google .firebase .firestore .model .DocumentCollections .emptyDocumentMap ;
18
18
import static com .google .firebase .firestore .util .Assert .hardAssert ;
19
19
20
+ import androidx .annotation .Nullable ;
20
21
import androidx .annotation .VisibleForTesting ;
21
22
import com .google .firebase .Timestamp ;
22
23
import com .google .firebase .database .collection .ImmutableSortedMap ;
23
24
import com .google .firebase .firestore .core .Query ;
24
25
import com .google .firebase .firestore .model .Document ;
25
26
import com .google .firebase .firestore .model .DocumentKey ;
27
+ import com .google .firebase .firestore .model .FieldIndex ;
26
28
import com .google .firebase .firestore .model .FieldIndex .IndexOffset ;
27
29
import com .google .firebase .firestore .model .MutableDocument ;
28
30
import com .google .firebase .firestore .model .ResourcePath ;
31
33
import com .google .firebase .firestore .model .mutation .MutationBatch ;
32
34
import com .google .firebase .firestore .model .mutation .Overlay ;
33
35
import com .google .firebase .firestore .model .mutation .PatchMutation ;
36
+ import java .util .Collections ;
34
37
import java .util .HashMap ;
35
38
import java .util .HashSet ;
36
39
import java .util .List ;
@@ -84,15 +87,10 @@ DocumentOverlayCache getDocumentOverlayCache() {
84
87
*/
85
88
Document getDocument (DocumentKey key ) {
86
89
Overlay overlay = documentOverlayCache .getOverlay (key );
87
- // Only read from remote document cache if overlay is a patch.
88
- MutableDocument document =
89
- (overlay == null || overlay .getMutation () instanceof PatchMutation )
90
- ? remoteDocumentCache .get (key )
91
- : MutableDocument .newInvalidDocument (key );
90
+ MutableDocument document = getBaseDocument (key , overlay );
92
91
if (overlay != null ) {
93
92
overlay .getMutation ().applyToLocalView (document , null , Timestamp .now ());
94
93
}
95
-
96
94
return document ;
97
95
}
98
96
@@ -117,21 +115,35 @@ ImmutableSortedMap<DocumentKey, Document> getDocuments(Iterable<DocumentKey> key
117
115
*/
118
116
ImmutableSortedMap <DocumentKey , Document > getLocalViewOfDocuments (
119
117
Map <DocumentKey , MutableDocument > docs , Set <DocumentKey > existenceStateChanged ) {
118
+ return computeViews (docs , Collections .emptyMap (), existenceStateChanged );
119
+ }
120
+
121
+ /**
122
+ * Computes the local view for doc, applying overlays from both {@code memoizedOverlays} and the
123
+ * overlay cache.
124
+ */
125
+ private ImmutableSortedMap <DocumentKey , Document > computeViews (
126
+ Map <DocumentKey , MutableDocument > docs ,
127
+ Map <DocumentKey , Overlay > memoizedOverlays ,
128
+ Set <DocumentKey > existenceStateChanged ) {
120
129
ImmutableSortedMap <DocumentKey , Document > results = emptyDocumentMap ();
121
130
Map <DocumentKey , MutableDocument > recalculateDocuments = new HashMap <>();
122
- for (Map .Entry <DocumentKey , MutableDocument > entry : docs .entrySet ()) {
123
- Overlay overlay = documentOverlayCache .getOverlay (entry .getKey ());
131
+ for (MutableDocument doc : docs .values ()) {
132
+ Overlay overlay =
133
+ memoizedOverlays .containsKey (doc .getKey ())
134
+ ? memoizedOverlays .get (doc .getKey ())
135
+ : documentOverlayCache .getOverlay (doc .getKey ());
124
136
// Recalculate an overlay if the document's existence state is changed due to a remote
125
137
// event *and* the overlay is a PatchMutation. This is because document existence state
126
138
// can change if some patch mutation's preconditions are met.
127
139
// NOTE: we recalculate when `overlay` is null as well, because there might be a patch
128
140
// mutation whose precondition does not match before the change (hence overlay==null),
129
141
// but would now match.
130
- if (existenceStateChanged .contains (entry .getKey ())
142
+ if (existenceStateChanged .contains (doc .getKey ())
131
143
&& (overlay == null || overlay .getMutation () instanceof PatchMutation )) {
132
- recalculateDocuments .put (entry .getKey (), docs . get ( entry . getKey ()) );
144
+ recalculateDocuments .put (doc .getKey (), doc );
133
145
} else if (overlay != null ) {
134
- overlay .getMutation ().applyToLocalView (entry . getValue () , null , Timestamp .now ());
146
+ overlay .getMutation ().applyToLocalView (doc , null , Timestamp .now ());
135
147
}
136
148
}
137
149
@@ -190,14 +202,6 @@ void recalculateAndSaveOverlays(Set<DocumentKey> documentKeys) {
190
202
recalculateAndSaveOverlays (docs );
191
203
}
192
204
193
- /** Gets the local view of the next {@code count} documents based on their read time. */
194
- ImmutableSortedMap <DocumentKey , Document > getDocuments (
195
- String collectionGroup , IndexOffset offset , int count ) {
196
- Map <DocumentKey , MutableDocument > docs =
197
- remoteDocumentCache .getAll (collectionGroup , offset , count );
198
- return getLocalViewOfDocuments (docs , new HashSet <>());
199
- }
200
-
201
205
/**
202
206
* Performs a query against the local view of all documents.
203
207
*
@@ -250,7 +254,47 @@ private ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingCollection
250
254
return results ;
251
255
}
252
256
253
- /** Queries the remote documents and overlays by doing a full collection scan. */
257
+ /**
258
+ * Given a collection group, returns the next documents that follow the provided offset, along
259
+ * with an updated batch ID.
260
+ *
261
+ * <p>The documents returned by this method are ordered by remote version from the provided
262
+ * offset. If there are no more remote documents after the provided offset, documents with
263
+ * mutations in order of batch id from the offset are returned. Since all documents in a batch are
264
+ * returned together, the total number of documents returned can exceed {@code count}.
265
+ *
266
+ * @param collectionGroup The collection group for the documents.
267
+ * @param offset The offset to index into.
268
+ * @param count The number of documents to return
269
+ * @return A LocalDocumentsResult with the documents that follow the provided offset and the last
270
+ * processed batch id.
271
+ */
272
+ LocalDocumentsResult getNextDocuments (String collectionGroup , IndexOffset offset , int count ) {
273
+ Map <DocumentKey , MutableDocument > docs =
274
+ remoteDocumentCache .getAll (collectionGroup , offset , count );
275
+ Map <DocumentKey , Overlay > overlays =
276
+ count - docs .size () > 0
277
+ ? documentOverlayCache .getOverlays (
278
+ collectionGroup , offset .getLargestBatchId (), count - docs .size ())
279
+ : Collections .emptyMap ();
280
+
281
+ int largestBatchId = FieldIndex .INITIAL_LARGEST_BATCH_ID ;
282
+ for (Overlay overlay : overlays .values ()) {
283
+ if (!docs .containsKey (overlay .getKey ())) {
284
+ docs .put (overlay .getKey (), getBaseDocument (overlay .getKey (), overlay ));
285
+ }
286
+ // The callsite will use the largest batch ID together with the latest read time to create
287
+ // a new index offset. Since we only process batch IDs if all remote documents have been read,
288
+ // no overlay will increase the overall read time. This is why we only need to special case
289
+ // the batch id.
290
+ largestBatchId = Math .max (largestBatchId , overlay .getLargestBatchId ());
291
+ }
292
+
293
+ ImmutableSortedMap <DocumentKey , Document > localDocs =
294
+ computeViews (docs , overlays , Collections .emptySet ());
295
+ return new LocalDocumentsResult (largestBatchId , localDocs );
296
+ }
297
+
254
298
private ImmutableSortedMap <DocumentKey , Document > getDocumentsMatchingCollectionQuery (
255
299
Query query , IndexOffset offset ) {
256
300
Map <DocumentKey , MutableDocument > remoteDocuments =
@@ -281,4 +325,11 @@ private ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingCollection
281
325
282
326
return results ;
283
327
}
328
+
329
+ /** Returns a base document that can be used to apply `overlay`. */
330
+ private MutableDocument getBaseDocument (DocumentKey key , @ Nullable Overlay overlay ) {
331
+ return (overlay == null || overlay .getMutation () instanceof PatchMutation )
332
+ ? remoteDocumentCache .get (key )
333
+ : MutableDocument .newInvalidDocument (key );
334
+ }
284
335
}
0 commit comments