|
17 | 17 | import static com.google.firebase.firestore.model.Values.isArray;
|
18 | 18 | import static com.google.firebase.firestore.util.Assert.fail;
|
19 | 19 | import static com.google.firebase.firestore.util.Assert.hardAssert;
|
| 20 | +import static com.google.firebase.firestore.util.Util.diffCollections; |
20 | 21 | import static com.google.firebase.firestore.util.Util.repeatSequence;
|
21 | 22 | import static java.lang.Math.max;
|
22 | 23 |
|
|
30 | 31 | import com.google.firebase.firestore.index.DirectionalIndexByteEncoder;
|
31 | 32 | import com.google.firebase.firestore.index.FirestoreIndexValueWriter;
|
32 | 33 | import com.google.firebase.firestore.index.IndexByteEncoder;
|
| 34 | +import com.google.firebase.firestore.index.IndexEntry; |
33 | 35 | import com.google.firebase.firestore.model.Document;
|
34 | 36 | import com.google.firebase.firestore.model.DocumentKey;
|
35 | 37 | import com.google.firebase.firestore.model.FieldIndex;
|
|
52 | 54 | import java.util.PriorityQueue;
|
53 | 55 | import java.util.Queue;
|
54 | 56 | import java.util.Set;
|
| 57 | +import java.util.SortedSet; |
| 58 | +import java.util.TreeSet; |
55 | 59 |
|
56 | 60 | /** A persisted implementation of IndexManager. */
|
57 | 61 | final class SQLiteIndexManager implements IndexManager {
|
@@ -220,11 +224,30 @@ public void updateIndexEntries(Collection<Document> documents) {
|
220 | 224 | for (Document document : documents) {
|
221 | 225 | Collection<FieldIndex> fieldIndexes = getFieldIndexes(document.getKey().getCollectionGroup());
|
222 | 226 | for (FieldIndex fieldIndex : fieldIndexes) {
|
223 |
| - writeEntries(document, fieldIndex); |
| 227 | + SortedSet<IndexEntry> existingEntries = |
| 228 | + getExistingIndexEntries(document.getKey(), fieldIndex); |
| 229 | + SortedSet<IndexEntry> newEntries = computeIndexEntries(document, fieldIndex); |
| 230 | + if (!existingEntries.equals(newEntries)) { |
| 231 | + updateEntries(document, existingEntries, newEntries); |
| 232 | + } |
224 | 233 | }
|
225 | 234 | }
|
226 | 235 | }
|
227 | 236 |
|
| 237 | + /** |
| 238 | + * Updates the index entries for the provided document by deleting entries that are no longer |
| 239 | + * referenced in {@code newEntries} and adding all newly added entries. |
| 240 | + */ |
| 241 | + private void updateEntries( |
| 242 | + Document document, SortedSet<IndexEntry> existingEntries, SortedSet<IndexEntry> newEntries) { |
| 243 | + Logger.debug(TAG, "Updating index entries for document '%s'", document.getKey()); |
| 244 | + diffCollections( |
| 245 | + existingEntries, |
| 246 | + newEntries, |
| 247 | + entry -> addIndexEntry(document, entry), |
| 248 | + entry -> deleteIndexEntry(document, entry)); |
| 249 | + } |
| 250 | + |
228 | 251 | @Override
|
229 | 252 | public Collection<FieldIndex> getFieldIndexes(String collectionGroup) {
|
230 | 253 | hardAssert(started, "IndexManager not started");
|
@@ -264,70 +287,74 @@ private void memoizeIndex(FieldIndex fieldIndex) {
|
264 | 287 | Math.max(memoizedMaxSequenceNumber, fieldIndex.getIndexState().getSequenceNumber());
|
265 | 288 | }
|
266 | 289 |
|
267 |
| - /** Persists the index entries for the given document. */ |
268 |
| - private void writeEntries(Document document, FieldIndex fieldIndex) { |
| 290 | + /** Creates the index entries for the given document. */ |
| 291 | + private SortedSet<IndexEntry> computeIndexEntries(Document document, FieldIndex fieldIndex) { |
| 292 | + SortedSet<IndexEntry> result = new TreeSet<>(); |
| 293 | + |
269 | 294 | @Nullable byte[] directionalValue = encodeDirectionalElements(fieldIndex, document);
|
270 | 295 | if (directionalValue == null) {
|
271 |
| - return; |
| 296 | + return result; |
272 | 297 | }
|
273 | 298 |
|
274 | 299 | @Nullable FieldIndex.Segment arraySegment = fieldIndex.getArraySegment();
|
275 | 300 | if (arraySegment != null) {
|
276 | 301 | Value value = document.getField(arraySegment.getFieldPath());
|
277 |
| - if (!isArray(value)) { |
278 |
| - return; |
279 |
| - } |
280 |
| - |
281 |
| - for (Value arrayValue : value.getArrayValue().getValuesList()) { |
282 |
| - addSingleEntry( |
283 |
| - document, fieldIndex.getIndexId(), encodeSingleElement(arrayValue), directionalValue); |
| 302 | + if (isArray(value)) { |
| 303 | + for (Value arrayValue : value.getArrayValue().getValuesList()) { |
| 304 | + result.add( |
| 305 | + IndexEntry.create( |
| 306 | + fieldIndex.getIndexId(), |
| 307 | + document.getKey(), |
| 308 | + encodeSingleElement(arrayValue), |
| 309 | + directionalValue)); |
| 310 | + } |
284 | 311 | }
|
285 | 312 | } else {
|
286 |
| - addSingleEntry(document, fieldIndex.getIndexId(), /* arrayValue= */ null, directionalValue); |
| 313 | + result.add( |
| 314 | + IndexEntry.create( |
| 315 | + fieldIndex.getIndexId(), document.getKey(), new byte[] {}, directionalValue)); |
287 | 316 | }
|
288 |
| - } |
289 |
| - |
290 |
| - @Override |
291 |
| - public void handleDocumentChange(@Nullable Document oldDocument, @Nullable Document newDocument) { |
292 |
| - hardAssert(started, "IndexManager not started"); |
293 |
| - hardAssert(oldDocument == null, "Support for updating documents is not yet available"); |
294 |
| - hardAssert(newDocument != null, "Support for removing documents is not yet available"); |
295 | 317 |
|
296 |
| - DocumentKey documentKey = newDocument.getKey(); |
297 |
| - Collection<FieldIndex> fieldIndices = getFieldIndexes(documentKey.getCollectionGroup()); |
298 |
| - addIndexEntry(newDocument, fieldIndices); |
299 |
| - } |
300 |
| - |
301 |
| - /** |
302 |
| - * Writes index entries for the field indexes that apply to the provided document. |
303 |
| - * |
304 |
| - * @param document The provided document to index. |
305 |
| - * @param fieldIndexes A list of field indexes to apply. |
306 |
| - */ |
307 |
| - private void addIndexEntry(Document document, Collection<FieldIndex> fieldIndexes) { |
308 |
| - for (FieldIndex fieldIndex : fieldIndexes) { |
309 |
| - writeEntries(document, fieldIndex); |
310 |
| - } |
| 318 | + return result; |
311 | 319 | }
|
312 | 320 |
|
313 |
| - /** Adds a single index entry into the index entries table. */ |
314 |
| - private void addSingleEntry( |
315 |
| - Document document, int indexId, @Nullable Object arrayValue, Object directionalValue) { |
316 |
| - if (Logger.isDebugEnabled()) { |
317 |
| - Logger.debug( |
318 |
| - TAG, "Adding index values for document '%s' to index '%s'", document.getKey(), indexId); |
319 |
| - } |
320 |
| - |
| 321 | + private void addIndexEntry(Document document, IndexEntry indexEntry) { |
321 | 322 | db.execute(
|
322 | 323 | "INSERT INTO index_entries (index_id, uid, array_value, directional_value, document_name) "
|
323 | 324 | + "VALUES(?, ?, ?, ?, ?)",
|
324 |
| - indexId, |
| 325 | + indexEntry.getIndexId(), |
325 | 326 | uid,
|
326 |
| - arrayValue, |
327 |
| - directionalValue, |
| 327 | + indexEntry.getArrayValue(), |
| 328 | + indexEntry.getDirectionalValue(), |
328 | 329 | document.getKey().toString());
|
329 | 330 | }
|
330 | 331 |
|
| 332 | + private void deleteIndexEntry(Document document, IndexEntry indexEntry) { |
| 333 | + db.execute( |
| 334 | + "DELETE FROM index_entries WHERE index_id = ? AND uid = ? AND array_value = ? " |
| 335 | + + "AND directional_value = ? AND document_name = ?", |
| 336 | + indexEntry.getIndexId(), |
| 337 | + uid, |
| 338 | + indexEntry.getArrayValue(), |
| 339 | + indexEntry.getDirectionalValue(), |
| 340 | + document.getKey().toString()); |
| 341 | + } |
| 342 | + |
| 343 | + private SortedSet<IndexEntry> getExistingIndexEntries( |
| 344 | + DocumentKey documentKey, FieldIndex fieldIndex) { |
| 345 | + SortedSet<IndexEntry> results = new TreeSet<>(); |
| 346 | + db.query( |
| 347 | + "SELECT array_value, directional_value FROM index_entries " |
| 348 | + + "WHERE index_id = ? AND document_name = ? AND uid = ?") |
| 349 | + .binding(fieldIndex.getIndexId(), documentKey.toString(), uid) |
| 350 | + .forEach( |
| 351 | + row -> |
| 352 | + results.add( |
| 353 | + IndexEntry.create( |
| 354 | + fieldIndex.getIndexId(), documentKey, row.getBlob(0), row.getBlob(1)))); |
| 355 | + return results; |
| 356 | + } |
| 357 | + |
331 | 358 | @Override
|
332 | 359 | public Set<DocumentKey> getDocumentsMatchingTarget(FieldIndex fieldIndex, Target target) {
|
333 | 360 | hardAssert(started, "IndexManager not started");
|
|
0 commit comments