Облачные функции Firebase: функция транзакций не возвращает обещание? - PullRequest
0 голосов
/ 21 апреля 2020

Вот что я пытаюсь сделать с помощью облачной функции Firebase:

-Следить за любым изменением в одном из документов в коллекции 'user'.

-Обновление копий копий userinfo в соответствующих документах в коллекциях 'comment' и 'post'.

Поскольку мне нужно будет запрашивать соответствующие документы и обновлять их сразу, я пишу коды для операций транзакции.

Вот код, который я написал. Он возвращает сообщение об ошибке «Функция вернула неопределенное, ожидаемое обещание или значение».

exports.useInfoUpdate = functions.firestore.document('user/{userid}').onUpdate((change,context) => {
   const olduserinfo=change.before.data();
   const newuserinfo=change.after.data();
       db.runTransaction(t=>{
         return t.get(db.collection('comment').where('userinfo','==',olduserinfo))
        .then((querysnapshot)=>{
          querysnapshot.forEach((doc)=>{
             doc.ref.update({userinfo:newuserinfo})
          })
        })    
      })
  .then(()=>{
    db.runTransaction(t=>{
         return t.get(db.collection('post').where('userinfo','==',olduserinfo))
        .then((querysnapshot)=>{
          querysnapshot.forEach((doc)=>{
             doc.ref.update({userinfo:newuserinfo})
          })
        })    
      })
  })
});

Я немного запутался, потому что, насколько я знаю, метод update возвращает обещание? Возможно, я что-то упустил, но я занялся программированием только в ноябре прошлого года, так что не переживайте sh. :)

Любой совет, как решить эту проблему? Спасибо!

РЕДАКТИРОВАТЬ: Опираясь на Renaud отличный ответ, я создал следующий код на случай, если кому-то может понадобиться. Сложность транзакции заключается в том, что одни и те же данные могут храниться под разными индексами или в разных форматах. Например, одна и та же переменная 'map' может храниться под индексом в одной коллекции и как часть массива в другой. В этом случае для каждого документа, возвращаемого по запросу, требуются разные методы обновления.

Я решил эту проблему с помощью методов do c .ref.path, split и switch. Это позволяет применять различные методы обновления в зависимости от имени коллекции. В двух словах, примерно так:

 return db.runTransaction(t => {
        return t.getAll(...refs)
            .then(docs => {
                docs.forEach(doc => {
                    switch (doc.ref.path.split('/')[0]) { //This returns the collection name and switch method assigns a relevant operation to be done.
                      case 'A':
                        t = t.update(doc.ref, **do whatever is needed for this collection**)
                        break;
                      case 'B':
                        t = t.update(doc.ref, **do whatever is needed for this collection**)
                        break;
                      default:
                        t = t.update(doc.ref, **do whatever is needed for this collection**)
                    }
                })
            })
    })

Надеюсь, это поможет!

1 Ответ

2 голосов
/ 21 апреля 2020

Преамбула: это очень интересный вариант использования !!

Проблема, указанная в сообщении об ошибке, связана с тем, что вы не возвращаете обещание, возвращенное runTransaction() метод. Однако в вашем коде есть несколько других проблем.

С помощью Node.js Server SDK вы действительно можете передать запрос в метод транзакции get() (вы невозможно с JavaScript SDK). Однако в вашем случае вы хотите обновить документы, возвращенные двумя запросами. Вы не можете дважды вызвать db.runTransaction(), потому что тогда это уже не уникальная транзакция.

Так что вам нужно использовать метод getAll(), передавая распакованный массив DocumentReferences. (Опять же, обратите внимание, что этот метод getAll() доступен только в Node.js Server SDK, а не в JavaScript SDK)

Следующий код поможет вам.

Мы выполните два запроса и преобразуйте результат в один массив DocumentReferences. Затем мы вызываем метод runTransaction() и используем оператор распространения, чтобы распаковать массив DocumentReferences и передать его методу getAll().

Затем мы l oop поверх документов и объединяем вызывает метод транзакции update(), поскольку она возвращает транзакцию.

exports.useInfoUpdate = functions.firestore.document('user/{userid}').onUpdate((change, context) => {
    const olduserinfo = change.before.data();
    const newuserinfo = change.after.data();

    const db = admin.firestore();

    const q1 = db.collection('comment').where('userinfo', '==', olduserinfo);  // See the remark below: you probably need to use a document field here (e.g. olduserinfo.userinfo)
    const q2 = db.collection('post').where('userinfo', '==', olduserinfo);


    return Promise.all([q1.get(), q2.get()])
        .then(results => {
            refs = [];
            results.forEach(querySnapshot => {
                querySnapshot.forEach(documentSnapshot => {
                    refs.push(documentSnapshot.ref);
                })
            });


            return db.runTransaction(t => {
                return t.getAll(...refs)
                    .then(docs => {
                        docs.forEach(doc => {
                            t = t.update(doc.ref, { userinfo: newuserinfo })
                        })
                    })
            })

        })
});

Два последних замечания:

  1. Я не уверен, что db.collection('comment').where('userinfo', '==', olduserinfo); будет действительным, поскольку olduserinfo получено через change.before.data(). Вам, вероятно, нужно указать одно поле. Вероятно, это то же самое для newuserinfo.
  2. Обратите внимание, что вы не можете сделать doc.ref.update() в транзакции, вам нужно вызвать метод update() транзакции, а не метод DocumentReference.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...