-1

I have implemented the following a function to delete data using a batch. But it's giving me an unhandled exception saying that "Cannot modify a WriteBatch that has been committed. I think it's because of the for loop I'm using.

data.forEach(async (element: any) => {
 const snapshot = await db
  .collection("car")
  .doc(element.brand)
  .collection("model")
  .doc(element.model)
  .collection("year")
  .orderBy("year")
  .get();

snapshot.forEach(async (doc) => {

  if (element.service === "Car Spa") {
    console.log("Cond1")
    const ref1 = db
      .collection(element.service)
      .doc(element.sub_service)
      .collection("Body Type")
      .doc(element.bodyType)
      .collection("model")
      .doc(element.model)
      .collection("mechanic")
      .doc("email@gmail.com")
    batch.delete(ref1);
    operationCounter++;
  } else {
    console.log("Cond:2")
    const ref2 = db
      .collection(element.service)
      .doc(element.sub_service)
      .collection("vehicle")
      .doc(element.brand)
      .collection("model")
      .doc(element.model)
      .collection("year")
      .doc(doc.data().year.toString())
      .collection("mechanic")
      .doc("email@gmail.com")
    batch.delete(ref2)
    operationCounter++;
  }

  let _id =
    element.service +
    element.sub_service +
    element.brand +
    element.model +
    doc.id;

  const ref3 = db
    .collection("mechanics")
    .doc("email@gmail.com")
    .collection("services")
    .doc(_id)
  batch.delete(ref3);
  operationCounter++;

  if (operationCounter === 500) {
    console.log("MORE THAN 500");
    batches.push(batch.commit());
    batch = db.batch();
    operationCounter = 0;
  }
});
});
if (batches.length === 0) {
await batch.commit().then(() => console.log("DONE ONE BAtch"));
} else {
await Promise.all(batches).then(() => console.log("DONE"));
}

Please suggest a way to do the batch deletion inside the loop efficiently. Thanks in advance!

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
BLasan
  • 57
  • 4
  • your error could be here `operationCounter === 500`. Shouldn't this be `operationCounter >= 500` ? Also, can you post a self contained code so I can try to reproduce your issue as I really cannot wrap my head around your data model :) ? – Methkal Khalawi Nov 20 '20 at 10:02
  • acyually even if the operator counter is less than 500 it's giving the above exception. And when i look at the logs I found that the function was jumping to the end before executing the data.foreach. Also in snapshot.foreach it was having the same issue. Can you suggest me a solution for that. I tried with for loop and the deletion was okay for some amount of data. For a huge count it could be crashed – BLasan Nov 20 '20 at 13:23

1 Answers1

1

Not seeing all your code it's difficult to know exactly how its failing or how to make it efficient. I've edited the you code a little, this should work: The issue is with the foreach see this issue, Using async/await with a forEach loop

this is correct the forEach loops

let batch = db.firestore().batch()

for (const element of data.docs) {
  const snapshot = await db
  .collection("car")
  .doc(element.brand)
  .collection("model")
  .doc(element.model)
  .collection("year")
  .orderBy("year")
  .get();

for( const doc of snapshot.docs) {

    if (element.service === "Car Spa") {
      
      const ref1 = db
        .collection(element.service)
        .doc(element.sub_service)
        .collection("Body Type")
        .doc(element.bodyType)
        .collection("model")
        .doc(element.model)
        .collection("mechanic")
        .doc("email@gmail.com")
      
        batch.delete(ref1);
     
        operationCounter++;
    } else {
     
      const ref2 = db
        .collection(element.service)
        .doc(element.sub_service)
        .collection("vehicle")
        .doc(element.brand)
        .collection("model")
        .doc(element.model)
        .collection("year")
        .doc(doc.data().year.toString())
        .collection("mechanic")
        .doc("email@gmail.com")
      batch.delete(ref2)
      operationCounter++;
    }

  let _id =
    element.service +
    element.sub_service +
    element.brand +
    element.model +
    doc.id;

  const ref3 = db
    .collection("mechanics")
    .doc("email@gmail.com")
    .collection("services")
    .doc(_id)
  batch.delete(ref3);
  operationCounter++;

  if (operationCounter === 500) {
    
    await batch.commit();
    batch = db.batch();
    operationCounter = 0;
  }
}
}

if (batch.length === 0) {
  await batch.commit().then(() => console.log("DONE LAST BATCH"));
}
Cleanbeans
  • 655
  • 4
  • 12
  • Your code is the one I have implemented. But data.foreach & snapshot.foreach is not working as expected. Foreach loops are not executing like for-loop. Sometimes it skips some loops. So I'm getting an "Unhandled Error" – BLasan Nov 20 '20 at 17:03
  • Apologies, awaits do not work as expected in a forEach. If you use the for of loop the await will work as expected. See my edited post. Check this post out https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop – Cleanbeans Nov 20 '20 at 18:42
  • Still foreach is giving an error. for-loop executes well but it'll get crashed for a huge amount of data. I got an idea from the link you provided on foreach loops. Thanks for that – BLasan Nov 21 '20 at 02:58
  • yuh. But not works for a huge amount of data (more than 150 records) – BLasan Nov 21 '20 at 07:54
  • what do you mean by "not works for a huge amount of data"? can you post the error message ? again can you please post an [MCVE](https://stackoverflow.com/help/minimal-reproducible-example) – Methkal Khalawi Nov 23 '20 at 10:32