Error getting document: clientDetails Error: Cannot modify a WriteBatch that has been committed

Error getting document: clientDetails Error: Cannot modify a WriteBatch that has been committed

我在 Firestore 批量更新中收到此错误的原因是什么?承诺作为批提交返回,批处理在循环内创建。我无法解决这个问题。有什么建议吗?

export const updateUserDetailsTypeform = functions.firestore.
  document('responseClientDetails/{case}').onCreate((snap, context) => {

    const newValue = snap.data();
    const caseReference = snap.id;
    var batch = db.batch();

    var reg = "[^£]*£[^£]*";
    const uid = caseReference.match(reg);
    if (uid && newValue) {

      let document = db.collection("responseClientDetails").doc(caseReference);
      let refDoc = db.collection("clientDetails").doc(uid[0])
      batch.update(refDoc,{ has_seen_setup: "true" })


 document.get().then(function (doc: any) {
        if (doc.exists) {
          let refNo = db.collection("clientDetails").doc(uid[0])
          for (let [key, value] of Object.entries(doc.data())) {
            const keyValue = key;
            const valueValue = value;
            batch.update(refNo, { [keyValue]: valueValue })
           // promises.push(db.collection("clientDetails").doc(uid[0]).update({ [keyValue]: valueValue }))
          }

        }else{
          console.log("document does not exist")
        } 
      }).catch(function (error: any) {
        console.log("Error getting document: clientDetails", error);
      });



return batch.commit().then(function () {
  console.log("updated clientDetails")
  return null

});

    }


  });


您必须确保仅在执行完所有更新后才调用 batch.commit()。您的代码现在正在做的是在调用第二次更新之前提交。

问题是 get() 是异步的,并且在查询完成之前立即返回。如果您添加一些日志语句,您会更好地理解发生了什么。相反,您需要做的是等到从 get() 返回的承诺完全解决,然后再提交批处理。这意味着您对 batch.commit() 的调用可能会出现在 then 回调中。

您对 batch.update(refNo, { [keyValue]: valueValue }) 的调用异步运行,并在您对 batch.commit

的调用之后触发

让我们稍微清理一下您的代码示例并添加一些注释以帮助说明发生了什么。

// 1 - This fires synchronously
batch.update(db.collection("clientDetails").doc(uid[0]), { has_seen_setup: "true" });

// 2 - This fires synchronously
db.collection("responseClientDetails").doc(caseReference).get().then(function (doc: any) {
  if (doc.exists) {
    let refNo = db.collection("clientDetails").doc(uid[0])
    for (let [key, value] of Object.entries(doc.data())) {
      const keyValue = key;
      const valueValue = value;
      // 4 - This fires asynchronously AFTER #3 runs, therefore the batch has been committed
      batch.update(refNo, { [keyValue]: valueValue })
    }
  } else {
    console.log("document does not exist")
  }
}).catch(function (error: any) {
  console.log("Error getting document: clientDetails", error);
});

// 3 - This fires synchronously
return batch.commit().then(function () {
  console.log("updated clientDetails")
  return null
});

因此,您的解决方法可能是将 batch.commit 调用移至 then() 调用:

batch.update(db.collection("clientDetails").doc(uid[0]), { has_seen_setup: "true" });

db.collection("responseClientDetails").doc(caseReference).get().then(function (doc: any) {
  if (doc.exists) {
    let refNo = db.collection("clientDetails").doc(uid[0])
    for (let [key, value] of Object.entries(doc.data())) {
      const keyValue = key;
      const valueValue = value;
      batch.update(refNo, { [keyValue]: valueValue })
    }
  } else {
    console.log("document does not exist")
  }
})
.then(() => {
  return batch.commit().then(function () {
    console.log("updated clientDetails")
    return null
  });
})
.catch(function (error: any) {
  console.log("Error getting document: clientDetails", error);
});