how to update multiple documents from cloud function in firestore database?

邮差的信 提交于 2020-12-05 12:37:08

问题


I am new to firebase cloud functions and I want to update username field of some documents from posts collection when the users collection changes it username field of a particular document. I use the following code to do that:

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);
  }

This code works fine. usernames in posts collection are changing correctly. But, after some time, the cloud functions log shows this log : Function execution took 60002 ms, finished with status: 'timeout'. How to solve that? And will this function be a problem if i have to update millions of docs in posts collection?


回答1:


The problem comes from the fact that you are not returning the Promise returned by the update() method, therefore the Cloud Function is not informed that the work is done and runs up to the timeout.

What may also happen, if you have to update "millions of docs in posts collection", is that the Cloud Function ends before your updates are all done. This is more annoying!

I would suggest you watch the 3 videos titled "Learn JavaScript Promises" from the Firebase video series which explain this key point of returning Promises for background triggered functions.

The following code should work. Note that I have used a batched write, which is especially dedicated to multiple write operations.

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;
        }
    });

Note that a batched write can contain up to 500 operations. If you plan to update more than 500 documents, you may use Promise.all() instead, as follows:

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;
        }
    });


来源:https://stackoverflow.com/questions/58213619/how-to-update-multiple-documents-from-cloud-function-in-firestore-database

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!