I have two collections: users and ads in Firestore.
When I delete a user file the users collection I also need to delete all his ads for the ads collection.
Is the bulk version correct?
Future<void> storeMarkAsDeleted() async {
final fs = FirebaseFirestore.instance;
final writeBatch = fs.batch();
// = Open a transaction to perform both operations
final user = await fs.collection(collectionName).doc(id));
/// Reads the ad document
final ads = await fs
.collection(collectionName)
.where("ownerId", isEqualTo: id)
.get();
// Mark user as deleted
writeBatch.update(user.reference, {
"deleted": true,
});
// Update all docs
for (final doc in ads.docs) {
writeBatch.update(doc.reference, {
"clearedForSale": false,
"deleted": true,
});
}
// unless commit is called, nothing happens.
writeBatch.commit();
}
2
Answers
If it works the way you expect, then it’s likely correct. However, you should be aware that there is a short period of time between the query for
ads
docs and their deletion where a new doc could be added to the collection and will not be marked as deleted when the batch is committed.(I also saw your other question Does it makes sense to batch update within a transaction in Flutter with Firebase? about atomically executing the deletions within a Transaction).
One possibility is to use a Transaction in a Cloud Function. While you cannot execute a Query in a Transaction executed via a Client SDK (included Firebase Flutter plugins) you can do it with the server client libraries (C#, Go, Java, Node.js, PHP, Python, Ruby) and therefore within a Cloud Function.
This page in the documentation explains the reasons for this difference.
In your Cloud Function, you first delete the user document, then you execute a query that returns the ads documents corresponding to the user and you delete all of them by looping on the QuerySnapshot. The best is to use a Callable Cloud Function that you call from your Flutter app by passing the user uid.
Something along the following lines for Node.js Cloud Functions 2nd Generation :
I let you add
try/catch
block and manage the potential errors as explained in the doc.