diff --git a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/IndexingTest.java b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/IndexingTest.java index ea36090853e..f04bd00fbec 100644 --- a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/IndexingTest.java +++ b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/IndexingTest.java @@ -136,6 +136,9 @@ public void testAutoIndexCreationSetSuccessfully() { results = waitFor(collection.whereEqualTo("match", true).get()); assertEquals(1, results.size()); + + assertDoesNotThrow(() -> db.getPersistentCacheIndexManager().deleteAllIndexes()); + assertEquals(1, results.size()); } @Test @@ -161,6 +164,9 @@ public void testAutoIndexCreationSetSuccessfullyUsingDefault() { results = waitFor(collection.whereEqualTo("match", true).get()); assertEquals(1, results.size()); + + assertDoesNotThrow(() -> db.getPersistentCacheIndexManager().deleteAllIndexes()); + assertEquals(1, results.size()); } @Test @@ -175,5 +181,9 @@ public void testAutoIndexCreationAfterFailsTermination() { expectError( () -> db.getPersistentCacheIndexManager().disableIndexAutoCreation(), "The client has already been terminated"); + + expectError( + () -> db.getPersistentCacheIndexManager().deleteAllIndexes(), + "The client has already been terminated"); } } diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/PersistentCacheIndexManager.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/PersistentCacheIndexManager.java index d9251ba45a3..f73b2332049 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/PersistentCacheIndexManager.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/PersistentCacheIndexManager.java @@ -51,4 +51,12 @@ public void enableIndexAutoCreation() { public void disableIndexAutoCreation() { client.setIndexAutoCreationEnabled(false); } + + /** + * Removes all persistent cache indexes. Please note this function will also deletes indexes + * generated by {@link FirebaseFirestore#setIndexConfiguration(String)}, which is deprecated. + */ + public void deleteAllIndexes() { + client.deleteAllFieldIndexes(); + } } diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/core/FirestoreClient.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/core/FirestoreClient.java index 9367712d991..182b7d6ef35 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/core/FirestoreClient.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/core/FirestoreClient.java @@ -358,6 +358,11 @@ public void setIndexAutoCreationEnabled(boolean enabled) { asyncQueue.enqueueAndForget(() -> localStore.setIndexAutoCreationEnabled(enabled)); } + public void deleteAllFieldIndexes() { + verifyNotTerminated(); + asyncQueue.enqueueAndForget(() -> localStore.deleteAllFieldIndexes()); + } + public void removeSnapshotsInSyncListener(EventListener listener) { // Checks for shutdown but does not raise error, allowing remove after shutdown to be a no-op. if (isTerminated()) { diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/local/IndexManager.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/local/IndexManager.java index 8ba45cd6efd..4751ae6e208 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/local/IndexManager.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/local/IndexManager.java @@ -78,6 +78,9 @@ enum IndexType { /** Removes the given field index and deletes all index values. */ void deleteFieldIndex(FieldIndex index); + /** Removes all field indexes and deletes all index values. */ + void deleteAllFieldIndexes(); + /** Creates a full matched field index which serves the given target. */ void createTargetIndexes(Target target); diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/local/LocalStore.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/local/LocalStore.java index c0be00e8111..a5e79c76c1d 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/local/LocalStore.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/local/LocalStore.java @@ -802,6 +802,10 @@ public void configureFieldIndexes(List newFieldIndexes) { }); } + public void deleteAllFieldIndexes() { + persistence.runTransaction("Delete All Indexes", () -> indexManager.deleteAllFieldIndexes()); + } + public void setIndexAutoCreationEnabled(boolean enabled) { queryEngine.setIndexAutoCreationEnabled(enabled); } diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/local/MemoryIndexManager.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/local/MemoryIndexManager.java index 0cb81deae8b..a55734b3859 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/local/MemoryIndexManager.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/local/MemoryIndexManager.java @@ -60,6 +60,11 @@ public void deleteFieldIndex(FieldIndex index) { // Field indices are not supported with memory persistence. } + @Override + public void deleteAllFieldIndexes() { + // Field indices are not supported with memory persistence. + } + @Override public void createTargetIndexes(Target target) {} diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/local/SQLiteIndexManager.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/local/SQLiteIndexManager.java index 5c3e4a844a4..8f4edc2125b 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/local/SQLiteIndexManager.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/local/SQLiteIndexManager.java @@ -233,6 +233,16 @@ public void deleteFieldIndex(FieldIndex index) { } } + @Override + public void deleteAllFieldIndexes() { + db.execute("DELETE FROM index_configuration"); + db.execute("DELETE FROM index_entries"); + db.execute("DELETE FROM index_state"); + + nextIndexToUpdate.clear(); + memoizedIndexes.clear(); + } + @Override public void createTargetIndexes(Target target) { hardAssert(started, "IndexManager not started"); diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/local/LocalStoreTestCase.java b/firebase-firestore/src/test/java/com/google/firebase/firestore/local/LocalStoreTestCase.java index 4f8645add10..4c93fca4a63 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/local/LocalStoreTestCase.java +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/local/LocalStoreTestCase.java @@ -234,6 +234,10 @@ protected void setRelativeIndexReadCostPerDocument(double newCost) { queryEngine.setRelativeIndexReadCostPerDocument(newCost); } + protected void deleteAllIndexes() { + localStore.deleteAllFieldIndexes(); + } + private void releaseTarget(int targetId) { localStore.releaseTarget(targetId); } diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/local/SQLiteLocalStoreTest.java b/firebase-firestore/src/test/java/com/google/firebase/firestore/local/SQLiteLocalStoreTest.java index 2885b8aa412..5fe08a2cb21 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/local/SQLiteLocalStoreTest.java +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/local/SQLiteLocalStoreTest.java @@ -575,6 +575,68 @@ public void testDisableIndexAutoCreationWorks() { assertRemoteDocumentsRead(/* byKey= */ 0, /* byCollection= */ 2); } + @Test + public void testDeleteAllIndexesWorksWithIndexAutoCreation() { + Query query = query("coll").filter(filter("value", "==", "match")); + int targetId = allocateQuery(query); + + setIndexAutoCreationEnabled(true); + setMinCollectionSizeToAutoCreateIndex(0); + setRelativeIndexReadCostPerDocument(2); + + applyRemoteEvent(addedRemoteEvent(doc("coll/a", 10, map("value", "match")), targetId)); + applyRemoteEvent(addedRemoteEvent(doc("coll/b", 10, map("value", Double.NaN)), targetId)); + applyRemoteEvent(addedRemoteEvent(doc("coll/c", 10, map("value", null)), targetId)); + applyRemoteEvent(addedRemoteEvent(doc("coll/d", 10, map("value", "mismatch")), targetId)); + applyRemoteEvent(addedRemoteEvent(doc("coll/e", 10, map("value", "match")), targetId)); + + // First time query is running without indexes. + // Based on current heuristic, collection document counts (5) > 2 * resultSize (2). + // Full matched index should be created. + executeQuery(query); + assertRemoteDocumentsRead(/* byKey= */ 0, /* byCollection= */ 2); + assertQueryReturned("coll/a", "coll/e"); + + setIndexAutoCreationEnabled(false); + + backfillIndexes(); + + executeQuery(query); + assertRemoteDocumentsRead(/* byKey= */ 2, /* byCollection= */ 0); + assertQueryReturned("coll/a", "coll/e"); + + deleteAllIndexes(); + + executeQuery(query); + assertRemoteDocumentsRead(/* byKey= */ 0, /* byCollection= */ 2); + assertQueryReturned("coll/a", "coll/e"); + } + + @Test + public void testDeleteAllIndexesWorksWithManualAddedIndexes() { + FieldIndex index = + fieldIndex( + "coll", 0, FieldIndex.INITIAL_STATE, "matches", FieldIndex.Segment.Kind.ASCENDING); + configureFieldIndexes(singletonList(index)); + + Query query = query("coll").filter(filter("matches", "==", true)); + int targetId = allocateQuery(query); + + applyRemoteEvent(addedRemoteEvent(doc("coll/a", 10, map("matches", true)), targetId)); + + backfillIndexes(); + + executeQuery(query); + assertRemoteDocumentsRead(/* byKey= */ 1, /* byCollection= */ 0); + assertQueryReturned("coll/a"); + + deleteAllIndexes(); + + executeQuery(query); + assertRemoteDocumentsRead(/* byKey= */ 0, /* byCollection= */ 1); + assertQueryReturned("coll/a"); + } + @Test public void testIndexAutoCreationWorksWithMutation() { Query query =