как обновить несколько документов из облачной функции в базе данных firestore?

Я новичок в облачных функциях 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 коллекции?


person eegooDeveloper    schedule 03.10.2019    source источник


Ответы (1)


Проблема возникает из-за того, что вы не возвращаете обещание, возвращаемое методом update(), поэтому облачная функция не получает информацию о том, что работа выполнена и работает до истечения времени ожидания.

Что также может случиться, если вам нужно обновить «миллионы документов в posts коллекции», так это то, что облачная функция завершится до того, как будут выполнены все ваши обновления. Это раздражает больше!

Я предлагаю вам посмотреть 3 видео под названием «Изучите обещания JavaScript» из серии видео, которые объясняют этот ключевой момент возврата обещаний для функций, запускаемых в фоновом режиме.

Следующий код должен работать. Обратите внимание, что я использовал пакетную запись, т. Е. особенно предназначен для нескольких операций записи.

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;
        }
    });
person Renaud Tarnec    schedule 03.10.2019
comment
спасибо за твой ответ. я не использовал пакетную запись из-за этого ответа stackoverflow.com/a/52170284/2923116 - person eegooDeveloper; 03.10.2019
comment
Хорошо, это имеет смысл, если вы знаете, что вам нужно обновить более 500 документов. Смотрите обновление моего ответа внизу. - person Renaud Tarnec; 03.10.2019