What's the best way to delete many (not all) documents from Cloud Firestore?
The official documentation contains information regarding deleting one document and all documents: https://firebase.google.com/docs/firestore/manage-data/delete-data.
What's the best way to delete many (not all) documents from Cloud Firestore?
The official documentation contains information regarding deleting one document and all documents: https://firebase.google.com/docs/firestore/manage-data/delete-data.
To delete multiple documents, you can do a single batched write. The WriteBatch
class has a delete()
method for this purpose.
The performance to between a single BatchedWrite
and multiple DocumentReference.delete
calls is similar though, see here. As in: I expect both of them to be plenty enough efficient for a case where the user selects documents to be deleted. If you find out that this is not the case, share the code that reproduces the performance problem.
As this is the first result when you search for "how to delete multiple documents on Firestore", I wanted to share a working code snippet that not only does what OP is asking, but also splits your batches into chunks to avoid reaching the limit of 500 commits per batch.
const deleteEmptyMessages = async () => {
const snapshot = await firestore.collection('messages').where('text', '==', '').get();
const MAX_WRITES_PER_BATCH = 500; /** https://cloud.google.com/firestore/quotas#writes_and_transactions */
/**
* `chunk` function splits the array into chunks up to the provided length.
* You can get it from either:
* - [Underscore.js](https://underscorejs.org/#chunk)
* - [lodash](https://lodash.com/docs/4.17.15#chunk)
* - Or one of [these answers](https://stackoverflow.com/questions/8495687/split-array-into-chunks#comment84212474_8495740)
*/
const batches = chunk(snapshot.docs, MAX_WRITES_PER_BATCH);
const commitBatchPromises = [];
batches.forEach(batch => {
const writeBatch = firestore.batch();
batch.forEach(doc => writeBatch.delete(doc.ref));
commitBatchPromises.push(writeBatch.commit());
});
await Promise.all(commitBatchPromises);
};
FirebaseFirestore db = FirebaseFirestore.getInstance();
WriteBatch writeBatch = db.batch();
for (int i=0;i<cartList.size();i++){
DocumentReference documentReference = db.collection("Users").document(cartList.get(i).getProductId());
writeBatch.delete(documentReference);
}
writeBatch.commit().addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
// Do anything here
}
});
I used batched writes for this. For example I deleted all documents, where the field "text" is empty:
const emptyMessages = await firestore.collection('messages').where("text", "==", "").get()
const batch = firestore.batch();
emptyMessages.forEach(doc => {
batch.delete(doc.ref);
});
await batch.commit();
btnDeleteSomeTasks.setOnClickListener{
for (ids in 0 until mRecyclerView.size){
val currentID = entryAdapter.snapshots.getSnapshot(ids).id
val reference = firestore.collection("allTasks")
.document(user.uid)
.collection("userTasks")
.document(currentID)
reference.delete()
notifyItemRemoved(ids)
}
}
The question didn't provide a specific use case. But...
One common scenario where the user may want to delete multiple documents, would be for example: Removing all of the items from a user's shopping cart. (But not from other user's carts.)
To do this efficiently, you must use a batched write. This allows you to perform multiple delete operations simultaneously.
For example, to delete three documents at once:
WriteBatch batch = db.batch();
batch.delete(db.collection("cartItems").document("item1"));
batch.delete(db.collection("cartItems").document("item2"));
batch.delete(db.collection("cartItems").document("item3"));
batch.commit()
.addOnSuccessListener((result) -> {
Log.i(LOG_TAG, "Selected items have been deleted.");
})
.addOnFailureListener((error) -> {
Log.e(LOG_TAG, "Failed to delete selected items.", error);
});
So if you know the document Ids in advance, this is fairly simple:
public void removeSelectedItemsFromShoppingCart(List<String> itemIds) {
WriteBatch batch = db.batch();
CollectionReference collection = db.collection("cartItems");
for (String itemId : itemIds) {
batch.delete(collection.document(itemId));
}
batch.commit()
.addOnSuccessListener((result) -> {
Log.i(LOG_TAG, "Selected items have been removed.");
})
.addOnFailureListener((error) -> {
Log.e(LOG_TAG, "Failed to remove selected items.", error);
});
}
But if you don't know the document Ids in advance, you may have to query the database first:
public void removeAllItemsFromShoppingCart(String userId) {
db.collection("cartItems")
.whereEqualTo("userId", userId)
.get()
.addOnSuccessListener((querySnapshot) -> {
WriteBatch batch = db.batch();
for (DocumentSnapshot doc : querySnapshot) {
batch.delete(doc.getReference());
}
batch.commit()
.addOnSuccessListener((result) -> {
Log.i(LOG_TAG, "All items have been removed.");
})
.addOnFailureListener((error) -> {
Log.e(LOG_TAG, "Failed to remove all items.", error);
});
})
.addOnFailureListener((error) -> {
Log.e(LOG_TAG, "Failed to get your cart items.", error);
});
}
Note that the code above uses a one-time read, instead of a real-time read. Which is critical here, so it doesn't continue deleting documents after the operation is done.
With the march 2023 firebase update here is the method to delete all objects that match multiple conditions:
const q = query(collection(db, 'collectionid'),
and(where('fieldname', '==', surveyObject.uid),
where('array-of-objecst', 'array-contains', object)
)
);
const batch = writeBatch(db);
const objectsToDelete = await getDocs(q);
questionsToDelete.forEach(doc => {
batch.delete(doc.ref);
});
await batch.commit();