31
31
import com .google .firebase .firestore .model .mutation .MutationBatch ;
32
32
import com .google .firebase .firestore .model .mutation .Overlay ;
33
33
import com .google .firebase .firestore .model .mutation .PatchMutation ;
34
+ import java .util .ArrayList ;
35
+ import java .util .Collections ;
34
36
import java .util .HashMap ;
35
37
import java .util .HashSet ;
36
38
import java .util .List ;
@@ -112,31 +114,28 @@ ImmutableSortedMap<DocumentKey, Document> getDocuments(Iterable<DocumentKey> key
112
114
return getLocalViewOfDocuments (docs , new HashSet <>());
113
115
}
114
116
115
- /**
116
- * Similar to {@link #getDocuments}, but creates the local view from the given {@code baseDocs}
117
- * without retrieving documents from the local store.
118
- *
119
- * @param docs The documents to apply local mutations to get the local views.
120
- * @param existenceStateChanged The set of document keys whose existence state is changed. This is
121
- * useful to determine if some documents overlay needs to be recalculated.
122
- */
123
- ImmutableSortedMap <DocumentKey , Document > getLocalViewOfDocuments (
124
- Map <DocumentKey , MutableDocument > docs , Set <DocumentKey > existenceStateChanged ) {
117
+ private ImmutableSortedMap <DocumentKey , Document > computeView (
118
+ Map <DocumentKey , MutableDocument > docs ,
119
+ Map <DocumentKey , Overlay > memoizedOverlays ,
120
+ Set <DocumentKey > existenceStateChanged ) {
125
121
ImmutableSortedMap <DocumentKey , Document > results = emptyDocumentMap ();
126
122
Map <DocumentKey , MutableDocument > recalculateDocuments = new HashMap <>();
127
- for (Map .Entry <DocumentKey , MutableDocument > entry : docs .entrySet ()) {
128
- Overlay overlay = documentOverlayCache .getOverlay (entry .getKey ());
123
+ for (MutableDocument doc : docs .values ()) {
124
+ Overlay overlay =
125
+ memoizedOverlays .containsKey (doc .getKey ())
126
+ ? memoizedOverlays .get (doc .getKey ())
127
+ : documentOverlayCache .getOverlay (doc .getKey ());
129
128
// Recalculate an overlay if the document's existence state is changed due to a remote
130
129
// event *and* the overlay is a PatchMutation. This is because document existence state
131
130
// can change if some patch mutation's preconditions are met.
132
131
// NOTE: we recalculate when `overlay` is null as well, because there might be a patch
133
132
// mutation whose precondition does not match before the change (hence overlay==null),
134
133
// but would now match.
135
- if (existenceStateChanged .contains (entry .getKey ())
134
+ if (existenceStateChanged .contains (doc .getKey ())
136
135
&& (overlay == null || overlay .getMutation () instanceof PatchMutation )) {
137
- recalculateDocuments .put (entry .getKey (), docs . get ( entry . getKey ()) );
136
+ recalculateDocuments .put (doc .getKey (), doc );
138
137
} else if (overlay != null ) {
139
- overlay .getMutation ().applyToLocalView (entry . getValue () , null , Timestamp .now ());
138
+ overlay .getMutation ().applyToLocalView (doc , null , Timestamp .now ());
140
139
}
141
140
}
142
141
@@ -148,6 +147,19 @@ ImmutableSortedMap<DocumentKey, Document> getLocalViewOfDocuments(
148
147
return results ;
149
148
}
150
149
150
+ /**
151
+ * Similar to {@link #getDocuments}, but creates the local view from the given {@code baseDocs}
152
+ * without retrieving documents from the local store.
153
+ *
154
+ * @param docs The documents to apply local mutations to get the local views.
155
+ * @param existenceStateChanged The set of document keys whose existence state is changed. This is
156
+ * useful to determine if some documents overlay needs to be recalculated.
157
+ */
158
+ ImmutableSortedMap <DocumentKey , Document > getLocalViewOfDocuments (
159
+ Map <DocumentKey , MutableDocument > docs , Set <DocumentKey > existenceStateChanged ) {
160
+ return computeView (docs , Collections .emptyMap (), existenceStateChanged );
161
+ }
162
+
151
163
private void recalculateAndSaveOverlays (Map <DocumentKey , MutableDocument > docs ) {
152
164
List <MutationBatch > batches =
153
165
mutationQueue .getAllMutationBatchesAffectingDocumentKeys (docs .keySet ());
@@ -195,14 +207,6 @@ void recalculateAndSaveOverlays(Set<DocumentKey> documentKeys) {
195
207
recalculateAndSaveOverlays (docs );
196
208
}
197
209
198
- /** Gets the local view of the next {@code count} documents based on their read time. */
199
- ImmutableSortedMap <DocumentKey , Document > getDocuments (
200
- String collectionGroup , IndexOffset offset , int count ) {
201
- Map <DocumentKey , MutableDocument > docs =
202
- remoteDocumentCache .getAll (collectionGroup , offset , count );
203
- return getLocalViewOfDocuments (docs , new HashSet <>());
204
- }
205
-
206
210
/**
207
211
* Performs a query against the local view of all documents.
208
212
*
@@ -255,12 +259,54 @@ private ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingCollection
255
259
return results ;
256
260
}
257
261
258
- /** Queries the remote documents and overlays by doing a full collection scan. */
262
+ /**
263
+ * Given a collection group, returns the next documents that follow the provided offset, along
264
+ * with an updated offset.
265
+ *
266
+ * <p>The documents returned by this method are ordered by remote version from the provided
267
+ * offset. If there are no more remote documents after the provided offset, documents with
268
+ * mutations in order of batch id from the offset are returned. Since all documents in a batch are
269
+ * returned together, the total number of documents returned can exceed {@code count}.
270
+ *
271
+ * @param collectionGroup The collection group for the documents.
272
+ * @param offset The offset to index into.
273
+ * @param count The number of documents to return
274
+ * @return A LocalDocumentsRResult with the documents that follow the provided offset and the last
275
+ * processed batch id.
276
+ */
277
+ LocalDocumentsResult getNextDocuments (String collectionGroup , IndexOffset offset , int count ) {
278
+ Map <DocumentKey , MutableDocument > docs =
279
+ remoteDocumentCache .getAll (collectionGroup , offset , count );
280
+ Map <DocumentKey , Overlay > overlays =
281
+ count - docs .size () > 0
282
+ ? documentOverlayCache .getOverlays (
283
+ collectionGroup , offset .getLargestBatchId (), count - docs .size ())
284
+ : Collections .emptyMap ();
285
+
286
+ int largestBatchId = -1 ;
287
+ List <DocumentKey > mutatedKeys = new ArrayList <>();
288
+ for (Overlay overlay : overlays .values ()) {
289
+ if (!docs .containsKey (overlay .getKey ())) {
290
+ mutatedKeys .add (overlay .getKey ());
291
+ }
292
+ largestBatchId = Math .max (largestBatchId , overlay .getLargestBatchId ());
293
+ }
294
+
295
+ if (!mutatedKeys .isEmpty ()) {
296
+ docs .putAll (remoteDocumentCache .getAll (mutatedKeys ));
297
+ }
298
+
299
+ ImmutableSortedMap <DocumentKey , Document > localDocs =
300
+ computeView (docs , overlays , Collections .emptySet ());
301
+ return new LocalDocumentsResult (largestBatchId , localDocs );
302
+ }
303
+
259
304
private ImmutableSortedMap <DocumentKey , Document > getDocumentsMatchingCollectionQuery (
260
305
Query query , IndexOffset offset ) {
261
306
Map <DocumentKey , MutableDocument > remoteDocuments =
262
307
remoteDocumentCache .getAll (query .getPath (), offset );
263
- Map <DocumentKey , Overlay > overlays = documentOverlayCache .getOverlays (query .getPath (), -1 );
308
+ Map <DocumentKey , Overlay > overlays =
309
+ documentOverlayCache .getOverlays (query .getPath (), offset .getLargestBatchId ());
264
310
265
311
// As documents might match the query because of their overlay we need to include documents
266
312
// for all overlays in the initial document set.
@@ -282,7 +328,6 @@ private ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingCollection
282
328
results = results .insert (docEntry .getKey (), docEntry .getValue ());
283
329
}
284
330
}
285
-
286
331
return results ;
287
332
}
288
333
}
0 commit comments