24
24
import com .google .firebase .firestore .model .DocumentKey ;
25
25
import com .google .firebase .firestore .model .FieldIndex ;
26
26
import com .google .firebase .firestore .model .MutableDocument ;
27
- import com .google .firebase .firestore .model .ResourcePath ;
28
27
import com .google .firebase .firestore .model .SnapshotVersion ;
29
28
import com .google .firebase .firestore .util .BackgroundQueue ;
30
29
import com .google .firebase .firestore .util .Executors ;
@@ -58,52 +57,50 @@ public void add(MutableDocument document, SnapshotVersion readTime) {
58
57
!readTime .equals (SnapshotVersion .NONE ),
59
58
"Cannot add document to the RemoteDocumentCache with a read time of zero" );
60
59
61
- String path = pathForKey ( document .getKey () );
60
+ DocumentKey documentKey = document .getKey ();
62
61
Timestamp timestamp = readTime .getTimestamp ();
63
62
MessageLite message = serializer .encodeMaybeDocument (document );
64
63
65
64
db .execute (
66
65
"INSERT OR REPLACE INTO remote_documents "
67
- + "(path, read_time_seconds, read_time_nanos, contents) "
68
- + "VALUES (?, ?, ?, ?)" ,
69
- path ,
66
+ + "(path, path_length, read_time_seconds, read_time_nanos, contents) "
67
+ + "VALUES (?, ?, ?, ?, ?)" ,
68
+ EncodedPath .encode (documentKey .getPath ()),
69
+ documentKey .getPath ().length (),
70
70
timestamp .getSeconds (),
71
71
timestamp .getNanoseconds (),
72
72
message .toByteArray ());
73
73
74
- indexManager .addToCollectionParentIndex (document .getKey ().getPath (). popLast ());
74
+ indexManager .addToCollectionParentIndex (document .getKey ().getCollectionPath ());
75
75
}
76
76
77
77
@ Override
78
78
public void remove (DocumentKey documentKey ) {
79
- String path = pathForKey (documentKey );
79
+ String path = EncodedPath . encode (documentKey . getPath () );
80
80
81
81
db .execute ("DELETE FROM remote_documents WHERE path = ?" , path );
82
82
}
83
83
84
84
@ Override
85
85
public MutableDocument get (DocumentKey documentKey ) {
86
- String path = pathForKey (documentKey );
86
+ String path = EncodedPath . encode (documentKey . getPath () );
87
87
88
88
MutableDocument document =
89
89
db .query (
90
90
"SELECT contents, read_time_seconds, read_time_nanos "
91
- + "FROM remote_documents "
92
- + "WHERE path = ?" )
91
+ + "FROM remote_documents WHERE path = ?" )
93
92
.binding (path )
94
93
.firstValue (row -> decodeMaybeDocument (row .getBlob (0 ), row .getInt (1 ), row .getInt (2 )));
95
94
return document != null ? document : MutableDocument .newInvalidDocument (documentKey );
96
95
}
97
96
98
97
@ Override
99
98
public Map <DocumentKey , MutableDocument > getAll (Iterable <DocumentKey > documentKeys ) {
100
- List <Object > args = new ArrayList <>();
101
- for (DocumentKey key : documentKeys ) {
102
- args .add (EncodedPath .encode (key .getPath ()));
103
- }
104
-
105
99
Map <DocumentKey , MutableDocument > results = new HashMap <>();
100
+ List <Object > bindVars = new ArrayList <>();
106
101
for (DocumentKey key : documentKeys ) {
102
+ bindVars .add (EncodedPath .encode (key .getPath ()));
103
+
107
104
// Make sure each key has a corresponding entry, which is null in case the document is not
108
105
// found.
109
106
results .put (key , MutableDocument .newInvalidDocument (key ));
@@ -114,7 +111,7 @@ public Map<DocumentKey, MutableDocument> getAll(Iterable<DocumentKey> documentKe
114
111
db ,
115
112
"SELECT contents, read_time_seconds, read_time_nanos FROM remote_documents "
116
113
+ "WHERE path IN (" ,
117
- args ,
114
+ bindVars ,
118
115
") ORDER BY path" );
119
116
120
117
while (longQuery .hasMoreSubqueries ()) {
@@ -138,88 +135,74 @@ public ImmutableSortedMap<DocumentKey, MutableDocument> getAllDocumentsMatchingQ
138
135
!query .isCollectionGroupQuery (),
139
136
"CollectionGroup queries should be handled in LocalDocumentsView" );
140
137
141
- // Use the query path as a prefix for testing if a document matches the query.
142
- ResourcePath prefix = query .getPath ();
143
- int immediateChildrenPathLength = prefix .length () + 1 ;
138
+ StringBuilder sql =
139
+ new StringBuilder (
140
+ "SELECT contents, read_time_seconds, read_time_nanos "
141
+ + "FROM remote_documents WHERE path >= ? AND path < ? AND path_length = ?" );
144
142
145
- String prefixPath = EncodedPath . encode ( prefix );
146
- String prefixSuccessorPath = EncodedPath . prefixSuccessor ( prefixPath ) ;
143
+ boolean hasOffset = ! FieldIndex . IndexOffset . NONE . equals ( offset );
144
+ Object [] bindVars = new Object [ 3 + ( hasOffset ? 6 : 0 )] ;
147
145
148
- BackgroundQueue backgroundQueue = new BackgroundQueue ( );
146
+ String prefix = EncodedPath . encode ( query . getPath () );
149
147
150
- ImmutableSortedMap <DocumentKey , MutableDocument >[] matchingDocuments =
151
- (ImmutableSortedMap <DocumentKey , MutableDocument >[])
152
- new ImmutableSortedMap [] {DocumentCollections .emptyMutableDocumentMap ()};
148
+ int i = 0 ;
149
+ bindVars [i ++] = prefix ;
150
+ bindVars [i ++] = EncodedPath .prefixSuccessor (prefix );
151
+ bindVars [i ++] = query .getPath ().length () + 1 ;
153
152
154
- SQLitePersistence .Query sqlQuery ;
155
- if (FieldIndex .IndexOffset .NONE .equals (offset )) {
156
- sqlQuery =
157
- db .query (
158
- "SELECT path, contents, read_time_seconds, read_time_nanos "
159
- + "FROM remote_documents WHERE path >= ? AND path < ?" )
160
- .binding (prefixPath , prefixSuccessorPath );
161
- } else {
153
+ if (hasOffset ) {
162
154
Timestamp readTime = offset .getReadTime ().getTimestamp ();
163
155
DocumentKey documentKey = offset .getDocumentKey ();
164
156
165
- sqlQuery =
166
- db .query (
167
- "SELECT path, contents, read_time_seconds, read_time_nanos "
168
- + "FROM remote_documents WHERE path >= ? AND path < ? AND ("
169
- + "read_time_seconds > ? OR ("
170
- + "read_time_seconds = ? AND read_time_nanos > ?) OR ("
171
- + "read_time_seconds = ? AND read_time_nanos = ? and path > ?))" )
172
- .binding (
173
- prefixPath ,
174
- prefixSuccessorPath ,
175
- readTime .getSeconds (),
176
- readTime .getSeconds (),
177
- readTime .getNanoseconds (),
178
- readTime .getSeconds (),
179
- readTime .getNanoseconds (),
180
- EncodedPath .encode (documentKey .getPath ()));
157
+ sql .append (
158
+ " AND (read_time_seconds > ? OR ("
159
+ + "read_time_seconds = ? AND read_time_nanos > ?) OR ("
160
+ + "read_time_seconds = ? AND read_time_nanos = ? and path > ?))" );
161
+ bindVars [i ++] = readTime .getSeconds ();
162
+ bindVars [i ++] = readTime .getSeconds ();
163
+ bindVars [i ++] = readTime .getNanoseconds ();
164
+ bindVars [i ++] = readTime .getSeconds ();
165
+ bindVars [i ++] = readTime .getNanoseconds ();
166
+ bindVars [i ] = EncodedPath .encode (documentKey .getPath ());
181
167
}
182
- sqlQuery .forEach (
183
- row -> {
184
- // TODO: Actually implement a single-collection query
185
- //
186
- // The query is actually returning any path that starts with the query path prefix
187
- // which may include documents in subcollections. For example, a query on 'rooms'
188
- // will return rooms/abc/messages/xyx but we shouldn't match it. Fix this by
189
- // discarding rows with document keys more than one segment longer than the query
190
- // path.
191
- ResourcePath path = EncodedPath .decodeResourcePath (row .getString (0 ));
192
- if (path .length () != immediateChildrenPathLength ) {
193
- return ;
194
- }
195
-
196
- // Store row values in array entries to provide the correct context inside the executor.
197
- final byte [] rawDocument = row .getBlob (1 );
198
- final int [] readTimeSeconds = {row .getInt (2 )};
199
- final int [] readTimeNanos = {row .getInt (3 )};
200
-
201
- // Since scheduling background tasks incurs overhead, we only dispatch to a
202
- // background thread if there are still some documents remaining.
203
- Executor executor = row .isLast () ? Executors .DIRECT_EXECUTOR : backgroundQueue ;
204
- executor .execute (
205
- () -> {
206
- MutableDocument document =
207
- decodeMaybeDocument (rawDocument , readTimeSeconds [0 ], readTimeNanos [0 ]);
208
- if (document .isFoundDocument () && query .matches (document )) {
209
- synchronized (SQLiteRemoteDocumentCache .this ) {
210
- matchingDocuments [0 ] = matchingDocuments [0 ].insert (document .getKey (), document );
211
- }
212
- }
213
- });
214
- });
168
+
169
+ ImmutableSortedMap <DocumentKey , MutableDocument >[] results =
170
+ (ImmutableSortedMap <DocumentKey , MutableDocument >[])
171
+ new ImmutableSortedMap [] {DocumentCollections .emptyMutableDocumentMap ()};
172
+ BackgroundQueue backgroundQueue = new BackgroundQueue ();
173
+
174
+ db .query (sql .toString ())
175
+ .binding (bindVars )
176
+ .forEach (
177
+ row -> {
178
+ // Store row values in array entries to provide the correct context inside the
179
+ // executor.
180
+ final byte [] rawDocument = row .getBlob (0 );
181
+ final int [] readTimeSeconds = {row .getInt (1 )};
182
+ final int [] readTimeNanos = {row .getInt (2 )};
183
+
184
+ // Since scheduling background tasks incurs overhead, we only dispatch to a
185
+ // background thread if there are still some documents remaining.
186
+ Executor executor = row .isLast () ? Executors .DIRECT_EXECUTOR : backgroundQueue ;
187
+ executor .execute (
188
+ () -> {
189
+ MutableDocument document =
190
+ decodeMaybeDocument (rawDocument , readTimeSeconds [0 ], readTimeNanos [0 ]);
191
+ if (document .isFoundDocument () && query .matches (document )) {
192
+ synchronized (SQLiteRemoteDocumentCache .this ) {
193
+ results [0 ] = results [0 ].insert (document .getKey (), document );
194
+ }
195
+ }
196
+ });
197
+ });
215
198
216
199
try {
217
200
backgroundQueue .drain ();
218
201
} catch (InterruptedException e ) {
219
202
fail ("Interrupted while deserializing documents" , e );
220
203
}
221
204
222
- return matchingDocuments [0 ];
205
+ return results [0 ];
223
206
}
224
207
225
208
@ Override
@@ -233,10 +216,6 @@ public SnapshotVersion getLatestReadTime() {
233
216
return latestReadTime != null ? latestReadTime : SnapshotVersion .NONE ;
234
217
}
235
218
236
- private String pathForKey (DocumentKey key ) {
237
- return EncodedPath .encode (key .getPath ());
238
- }
239
-
240
219
private MutableDocument decodeMaybeDocument (
241
220
byte [] bytes , int readTimeSeconds , int readTimeNanos ) {
242
221
try {
0 commit comments