16
16
17
17
import static com .google .firebase .firestore .util .Assert .fail ;
18
18
19
+ import android .database .Cursor ;
19
20
import androidx .annotation .Nullable ;
20
21
import com .google .firebase .firestore .auth .User ;
21
22
import com .google .firebase .firestore .model .DocumentKey ;
22
23
import com .google .firebase .firestore .model .ResourcePath ;
23
24
import com .google .firebase .firestore .model .mutation .Mutation ;
24
25
import com .google .firebase .firestore .model .mutation .Overlay ;
26
+ import com .google .firebase .firestore .util .BackgroundQueue ;
27
+ import com .google .firebase .firestore .util .Executors ;
25
28
import com .google .firestore .v1 .Write ;
26
29
import com .google .protobuf .InvalidProtocolBufferException ;
30
+ import java .util .ArrayList ;
31
+ import java .util .Arrays ;
27
32
import java .util .HashMap ;
33
+ import java .util .List ;
28
34
import java .util .Map ;
35
+ import java .util .SortedSet ;
36
+ import java .util .concurrent .Executor ;
29
37
30
38
public class SQLiteDocumentOverlayCache implements DocumentOverlayCache {
31
39
private final SQLitePersistence db ;
@@ -47,7 +55,49 @@ public Overlay getOverlay(DocumentKey key) {
47
55
"SELECT overlay_mutation, largest_batch_id FROM document_overlays "
48
56
+ "WHERE uid = ? AND collection_path = ? AND document_id = ?" )
49
57
.binding (uid , collectionPath , documentId )
50
- .firstValue (this ::decodeOverlay );
58
+ .firstValue (row -> this .decodeOverlay (row .getBlob (0 ), row .getInt (1 )));
59
+ }
60
+
61
+ @ Override
62
+ public Map <DocumentKey , Overlay > getOverlays (SortedSet <DocumentKey > keys ) {
63
+ Map <DocumentKey , Overlay > result = new HashMap <>();
64
+
65
+ BackgroundQueue backgroundQueue = new BackgroundQueue ();
66
+ ResourcePath currentCollectionPath = ResourcePath .EMPTY ;
67
+ List <Object > currentDocumentIds = new ArrayList <>();
68
+ for (DocumentKey key : keys ) {
69
+ if (!currentCollectionPath .equals (key .getCollectionPath ())) {
70
+ processSingleCollection (result , backgroundQueue , currentCollectionPath , currentDocumentIds );
71
+ currentDocumentIds = new ArrayList <>();
72
+ }
73
+ currentCollectionPath = key .getCollectionPath ();
74
+ currentDocumentIds .add (key .getDocumentId ());
75
+ }
76
+
77
+ processSingleCollection (result , backgroundQueue , currentCollectionPath , currentDocumentIds );
78
+ backgroundQueue .drain ();
79
+ return result ;
80
+ }
81
+
82
+ /** Reads the overlays for the documents in a single collection. */
83
+ private void processSingleCollection (
84
+ Map <DocumentKey , Overlay > result ,
85
+ BackgroundQueue backgroundQueue ,
86
+ ResourcePath collectionPath ,
87
+ List <Object > documentIds ) {
88
+ SQLitePersistence .LongQuery longQuery =
89
+ new SQLitePersistence .LongQuery (
90
+ db ,
91
+ "SELECT overlay_mutation, largest_batch_id FROM document_overlays "
92
+ + "WHERE uid = ? AND collection_path = ? AND document_id IN (" ,
93
+ Arrays .asList (uid , EncodedPath .encode (collectionPath )),
94
+ documentIds ,
95
+ ")" );
96
+ while (longQuery .hasMoreSubqueries ()) {
97
+ longQuery
98
+ .performNextSubquery ()
99
+ .forEach (row -> processOverlaysInBackground (backgroundQueue , result , row ));
100
+ }
51
101
}
52
102
53
103
private void saveOverlay (int largestBatchId , DocumentKey key , @ Nullable Mutation mutation ) {
@@ -83,49 +133,48 @@ public void removeOverlaysForBatchId(int batchId) {
83
133
84
134
@ Override
85
135
public Map <DocumentKey , Overlay > getOverlays (ResourcePath collection , int sinceBatchId ) {
86
- String collectionPath = EncodedPath .encode (collection );
87
-
88
136
Map <DocumentKey , Overlay > result = new HashMap <>();
137
+ BackgroundQueue backgroundQueue = new BackgroundQueue ();
89
138
db .query (
90
139
"SELECT overlay_mutation, largest_batch_id FROM document_overlays "
91
140
+ "WHERE uid = ? AND collection_path = ? AND largest_batch_id > ?" )
92
- .binding (uid , collectionPath , sinceBatchId )
93
- .forEach (
94
- row -> {
95
- Overlay overlay = decodeOverlay (row );
96
- result .put (overlay .getKey (), overlay );
97
- });
98
-
141
+ .binding (uid , EncodedPath .encode (collection ), sinceBatchId )
142
+ .forEach (row -> processOverlaysInBackground (backgroundQueue , result , row ));
143
+ backgroundQueue .drain ();
99
144
return result ;
100
145
}
101
146
102
147
@ Override
103
148
public Map <DocumentKey , Overlay > getOverlays (
104
149
String collectionGroup , int sinceBatchId , int count ) {
105
150
Map <DocumentKey , Overlay > result = new HashMap <>();
106
- Overlay [] lastOverlay = new Overlay [] {null };
151
+ String [] lastCollectionPath = new String [] {null };
152
+ String [] lastDocumentPath = new String [] {null };
153
+ int [] lastLargestBatchId = new int [] {0 };
107
154
155
+ BackgroundQueue backgroundQueue = new BackgroundQueue ();
108
156
db .query (
109
- "SELECT overlay_mutation, largest_batch_id FROM document_overlays "
157
+ "SELECT overlay_mutation, largest_batch_id, collection_path, document_id "
158
+ + " FROM document_overlays "
110
159
+ "WHERE uid = ? AND collection_group = ? AND largest_batch_id > ? "
111
160
+ "ORDER BY largest_batch_id, collection_path, document_id LIMIT ?" )
112
161
.binding (uid , collectionGroup , sinceBatchId , count )
113
162
.forEach (
114
163
row -> {
115
- lastOverlay [0 ] = decodeOverlay (row );
116
- result .put (lastOverlay [0 ].getKey (), lastOverlay [0 ]);
164
+ lastLargestBatchId [0 ] = row .getInt (1 );
165
+ lastCollectionPath [0 ] = row .getString (2 );
166
+ lastDocumentPath [0 ] = row .getString (3 );
167
+ processOverlaysInBackground (backgroundQueue , result , row );
117
168
});
118
169
119
- if (lastOverlay [0 ] == null ) {
170
+ if (lastCollectionPath [0 ] == null ) {
120
171
return result ;
121
172
}
122
173
123
174
// This function should not return partial batch overlays, even if the number of overlays in the
124
175
// result set exceeds the given `count` argument. Since the `LIMIT` in the above query might
125
176
// result in a partial batch, the following query appends any remaining overlays for the last
126
177
// batch.
127
- DocumentKey key = lastOverlay [0 ].getKey ();
128
- String encodedCollectionPath = EncodedPath .encode (key .getCollectionPath ());
129
178
db .query (
130
179
"SELECT overlay_mutation, largest_batch_id FROM document_overlays "
131
180
+ "WHERE uid = ? AND collection_group = ? "
@@ -134,23 +183,35 @@ public Map<DocumentKey, Overlay> getOverlays(
134
183
.binding (
135
184
uid ,
136
185
collectionGroup ,
137
- encodedCollectionPath ,
138
- encodedCollectionPath ,
139
- key .getDocumentId (),
140
- lastOverlay [0 ].getLargestBatchId ())
141
- .forEach (
142
- row -> {
143
- Overlay overlay = decodeOverlay (row );
144
- result .put (overlay .getKey (), overlay );
145
- });
146
-
186
+ lastCollectionPath [0 ],
187
+ lastCollectionPath [0 ],
188
+ lastDocumentPath [0 ],
189
+ lastLargestBatchId [0 ])
190
+ .forEach (row -> processOverlaysInBackground (backgroundQueue , result , row ));
191
+ backgroundQueue .drain ();
147
192
return result ;
148
193
}
149
194
150
- private Overlay decodeOverlay (android .database .Cursor row ) {
195
+ private void processOverlaysInBackground (
196
+ BackgroundQueue backgroundQueue , Map <DocumentKey , Overlay > results , Cursor row ) {
197
+ byte [] rawMutation = row .getBlob (0 );
198
+ int largestBatchId = row .getInt (1 );
199
+
200
+ // Since scheduling background tasks incurs overhead, we only dispatch to a
201
+ // background thread if there are still some documents remaining.
202
+ Executor executor = row .isLast () ? Executors .DIRECT_EXECUTOR : backgroundQueue ;
203
+ executor .execute (
204
+ () -> {
205
+ Overlay document = decodeOverlay (rawMutation , largestBatchId );
206
+ synchronized (results ) {
207
+ results .put (document .getKey (), document );
208
+ }
209
+ });
210
+ }
211
+
212
+ private Overlay decodeOverlay (byte [] rawMutation , int largestBatchId ) {
151
213
try {
152
- Write write = Write .parseFrom (row .getBlob (0 ));
153
- int largestBatchId = row .getInt (1 );
214
+ Write write = Write .parseFrom (rawMutation );
154
215
Mutation mutation = serializer .decodeMutation (write );
155
216
return Overlay .create (largestBatchId , mutation );
156
217
} catch (InvalidProtocolBufferException e ) {
0 commit comments