Firebase 函数返回批量更新错误无法修改已提交的 WriteBatch

时间:2021-06-20 10:14:42

标签: javascript node.js firebase google-cloud-firestore google-cloud-functions

这是我的代码:

exports.updateUserName = functions.firestore
  .document("users/{userId}")
  .onUpdate((change, context) => {
    const newValue = change.after.data().name;
    const previousValue = change.before.data().name;
    const userID = context.params.userId;
    var batch = admin.firestore().batch();
    if (newValue != previousValue) {

      //Update author in sets/{set_id}/author
       admin
        .firestore()
        .collection("sets")
        .where("authorId", "==", userID)
        .get()
        .then((querySnapshot) => {
          if (!querySnapshot.empty) {
             querySnapshot.forEach((doc) => {
              batch.update(doc.ref,{ "author": newValue });
            });
          }
        }),

    //Update author in courses/{course_id}/author
      admin
        .firestore()
        .collection("courses")
        .where("authorId", "==", userID)
        .get()
        .then((querySnapshot) => {
          if (!querySnapshot.empty) {
             querySnapshot.forEach((doc) => {
              batch.update(doc.ref,{ "author": newValue });
            });
          }
        })
    }
   return batch.commit().then(() => {
    console.log("commited")
  });
  });

所以我想更新两个不同的文档。我想我必须用批处理()来做到这一点。 但我收到错误:

Error: Cannot modify a WriteBatch that has been committed.
    at WriteBatch.verifyNotCommitted (/workspace/node_modules/@google-cloud/firestore/build/src/write-batch.js:117:19)
    at WriteBatch.update (/workspace/node_modules/@google-cloud/firestore/build/src/write-batch.js:313:14)
    at /workspace/index.js:84:21
    at QuerySnapshot.forEach (/workspace/node_modules/@google-cloud/firestore/build/src/reference.js:748:22)
    at /workspace/index.js:83:33
    at processTicksAndRejections (internal/process/task_queues.js:97:5)

这是我第一次使用批处理或必须使用一个云功能更新 2 个文档。 我做错了什么?

1 个答案:

答案 0 :(得分:1)

在调用 commit() 之前,您需要等待异步查询完成并填充批处理。为此,您需要 chain the promises 并在 Cloud 函数中返回此链。

所以以下应该可以解决问题(未经测试):

exports.updateUserName = functions.firestore
    .document("users/{userId}")
    .onUpdate((change, context) => {
        const newValue = change.after.data().name;
        const previousValue = change.before.data().name;
        const userID = context.params.userId;
        const db = admin.firestore();
        const batch = db.batch();
        if (newValue != previousValue) {

            //Update author in sets/{set_id}/author
            return db
                .collection("sets")
                .where("authorId", "==", userID)
                .get()
                .then((querySnapshot) => {
                    if (!querySnapshot.empty) {
                        querySnapshot.forEach((doc) => {
                            batch.update(doc.ref, { "author": newValue });
                        });
                    }

                    return db
                        .collection("courses")
                        .where("authorId", "==", userID)
                        .get();
                })
                .then((querySnapshot) => {
                    if (!querySnapshot.empty) {
                        querySnapshot.forEach((doc) => {
                            batch.update(doc.ref, { "author": newValue });
                        });
                    }
                    return batch.commit();
                })
                .then(() => {
                    console.log("commited")
                    return null;
                });
        } else {
            return null;
        }
    });

不要忘记批量写入有 500 个文档的限制。如果您知道自己可能会超过此限制,则可以改用 Promise.all(),如下所示:

exports.updateUserName = functions.firestore
    .document("users/{userId}")
    .onUpdate((change, context) => {
        const newValue = change.after.data().name;
        const previousValue = change.before.data().name;
        const userID = context.params.userId;
        const db = admin.firestore();
        var promises = [];
        
        if (newValue != previousValue) {

            //Update author in sets/{set_id}/author
            return db
                .collection("sets")
                .where("authorId", "==", userID)
                .get()
                .then((querySnapshot) => {
                    if (!querySnapshot.empty) {
                        querySnapshot.forEach((doc) => {
                            promises.push(doc.ref.update({ "author": newValue }));
                        });
                    }
                    return db
                        .collection("courses")
                        .where("authorId", "==", userID)
                        .get();
                })
                .then((querySnapshot) => {
                    if (!querySnapshot.empty) {
                        querySnapshot.forEach((doc) => {
                            promises.push(doc.ref.update({ "author": newValue }));
                        });
                    }
                    return Promise.all(promises);
                })
                .then(() => {
                    console.log("commited")
                    return null;
                });
        } else {
            return null;
        }
    });

最后,注意这个 async/await 版本,它更容易阅读:

exports.updateUserName = functions.firestore
    .document("users/{userId}")
    .onUpdate(async (change, context) => {
        const newValue = change.after.data().name;
        const previousValue = change.before.data().name;
        const userID = context.params.userId;
        const db = admin.firestore();
        const batch = db.batch();
        if (newValue != previousValue) {

            let querySnapshot = await db
                .collection("sets")
                .where("authorId", "==", userID)
                .get();

            if (!querySnapshot.empty) {
                querySnapshot.forEach((doc) => {
                    batch.update(doc.ref, { "author": newValue });
                });
            }

            querySnapshot = await db
                .collection("courses")
                .where("authorId", "==", userID)

            if (!querySnapshot.empty) {
                querySnapshot.forEach((doc) => {
                    batch.update(doc.ref, { "author": newValue });
                });
            }

            await batch.commit();
            console.log("commited")
            return null;

        } else {
            return null;
        }
    });