如何从Firestore数据库中的云功能更新多个文档?

时间:2019-10-03 06:44:02

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

我是Firebase云功能的新手,我想在username集合更改特定文档的posts字段时,从users集合更新某些文档的username字段文献。 我使用以下代码来做到这一点:

exports.updateProfileUsername = functions.firestore
  .document('users/{userId}')
  .onUpdate((change, context) => 
  {
    const {userId} = context.params;

    var newUsername = change.after.data().username;
    var previousUsername = change.before.data().username;

    if (newUsername.localeCompare(previousUsername) !== 0)
    {
      let postCollectionRef = db.collection('posts');
      let postQuery = postCollectionRef.where('userId', '==', `${userId}`);

      return new Promise((resolve, reject) => 
      {
        updateUsernameDocuments(postQuery, reject, newUsername);
      });
    }
  });

function updateUsernameDocuments(query, reject, newValue) 
  {
    query.get()
      .then((snapshot) => 
      {
        if (snapshot.size === 0) 
        {
          return 0;
        }

        return snapshot.docs.forEach((doc) =>
        {
          doc.ref.update({username : `${newValue}`});
        });
      }).catch(reject);
  }

此代码可以正常工作。 posts集合中的用户名正确更改。但是,一段时间后,云功能日志将显示以下日志:Function execution took 60002 ms, finished with status: 'timeout'。怎么解决呢? 如果我必须更新posts集合中的数百万个文档,此功能是否会有问题?

1 个答案:

答案 0 :(得分:4)

问题源于您没有返回update()方法返回的Promise的事实,因此没有通知Cloud Function工作已完成并超时。

如果您必须更新“ posts集合中的数百万个文档”,则云功能也会在完成之前结束。更烦人了!

我建议您观看Firebase video series上名为“学习JavaScript承诺”的3个视频,这些视频解释了为后台触发的函数返回承诺的关键点。

以下代码应该可以工作。请注意,我使用了batched write,它专门用于多重写入操作。

exports.updateProfileUsername = functions.firestore
    .document('users/{userId}')
    .onUpdate((change, context) => {
        const { userId } = context.params;

        var newUsername = change.after.data().username;
        var previousUsername = change.before.data().username;

        if (newUsername.localeCompare(previousUsername) !== 0) {
            const postCollectionRef = db.collection('posts');
            const postQuery = postCollectionRef.where('userId', '==', `${userId}`);

            return postQuery.get()
                .then(querySnapshot => {

                    if (querySnapshot.empty) {
                        return null;
                    } else {
                        let batch = db.batch();

                        querySnapshot.forEach(doc => {
                            batch.update(doc.ref, { username: `${newUsername}` });
                        });

                        return batch.commit();

                    }
                });
        } else {
            return null;
        }
    });

请注意,批量写入最多可以包含500个操作。如果您打算更新500多个文档,则可以改为使用Promise.all(),如下所示:

exports.updateProfileUsername = functions.firestore
    .document('users/{userId}')
    .onUpdate((change, context) => {
        const { userId } = context.params;

        var newUsername = change.after.data().username;
        var previousUsername = change.before.data().username;

        if (newUsername.localeCompare(previousUsername) !== 0) {
            const postCollectionRef = db.collection('posts');
            const postQuery = postCollectionRef.where('userId', '==', `${userId}`);

            return postQuery.get()
                .then(querySnapshot => {

                    if (querySnapshot.empty) {
                        return null;
                    } else {
                        const promises = []

                        querySnapshot.forEach(doc => {
                            promises.push(doc.ref.update({ username: `${newUsername}` }));
                        });

                        return Promise.all(promises);
                    }
                });
        } else {
            return null;
        }
    });