具有多个 get 的 Firestore 事务

问题描述 投票:0回答:3

我正在尝试运行具有可变数量的读取操作的事务。 我把 read() 操作放在 update() 之前。

阅读 Firestore 文档 https://cloud.google.com/firestore/docs/manage-data/transactions

“事务由任意数量的 get() 操作组成,后跟任意数量的写入操作,例如 set()、update() 或 delete()”

还有

使用交易时请注意:

  • 读操作必须先于写操作。
  • 如果当前编辑影响了文档,则调用事务(事务函数)的函数可能会运行多次 事务读取。
  • 事务函数不应直接修改应用程序状态。

但它没有提供实现。 当我尝试运行下面的代码时,我发现事务函数运行了更多次,然后出现异常。 但如果我只尝试使用一个

get
一切都会OK。

const reservationCol = this.db.firestore.collection('reservations');
return this.db.firestore.runTransaction(t => {
    return Promise.all([
        t.get(reservationCol.doc('id1')),
        t.get(reservationCol.doc('id2'))
    ]).then((responses) => {          
        let found = false;
        responses.forEach(resp => {
            if (resp.exists)
            {
                found = true;
            }
        });
        if (!found)
        {
            entity.id = 'id1';
            t.set(reservationCol.doc(entity.id), entity);
            return Promise.resolve('ok');
        }
        else
        {
            return Promise.reject('exist');
        }
    });
});
transactions angularfire2 google-cloud-firestore
3个回答
11
投票

Firestore 文档没有这么说,但答案隐藏在 API 参考中:https://cloud.google.com/nodejs/docs/reference/firestore/0.13.x/Transaction?authuser=0#getAll

您可以使用

Transaction.getAll()
代替
Transaction.get()
来获取多个文档。你的例子是:

const reservationCol = this.db.firestore.collection('reservations');
return this.db.firestore.runTransaction(t => {
  return t.getAll(reservationCol.doc('id1'), reservationCol.doc('id2'))
    .then(docs => {
      const id1 = docs[0];
      const id2 = docs[1];
      if (!(id1.exists && id2.exists)) {
        // do stuff
      } else {
        // throw error
      }
    })
}).then(() => console.log('Transaction succeeded'));

1
投票

我无法弄清楚如何在纯 Typescript 中执行此操作,但我能够找到一个使用 Promise 的 JavaScript 示例,因此我对其进行了调整以满足我的需求。它似乎工作正常,但是当我快速运行我的函数(通过快速连续单击按钮)时,我收到控制台错误,内容为

POST https://firestore.googleapis.com/v1beta1/projects/myprojectname/databases/(default)/documents:commit 400 ()
。我不清楚这些是否是我应该担心的错误,或者它们是否只是事务重试的结果。我发布了我自己的问题,并希望得到一些答案。与此同时,这是我想出的代码:

async vote(username, recipeId, direction) {

  let value;

  if ( direction == 'up' ) {
    value = 1;
  }

  if ( direction == 'down' ) {
    value = -1;
  }

  // assemble vote object to be recorded in votes collection
  const voteObj: Vote = { username: username, recipeId: recipeId , value: value };

  // get references to both vote and recipe documents
  const voteDocRef = this.afs.doc(`votes/${username}_${recipeId}`).ref;
  const recipeDocRef = this.afs.doc('recipes/' + recipeId).ref;

  await this.afs.firestore.runTransaction( async t => {

    const voteDoc = await t.get(voteDocRef);
    const recipeDoc = await t.get(recipeDocRef);
    const currentRecipeScore = await recipeDoc.get('score');

    if (!voteDoc.exists) {

      // This is a new vote, so add it to the votes collection
      // and apply its value to the recipe's score
      t.set(voteDocRef, voteObj);
      t.update(recipeDocRef, { score: (currentRecipeScore + value) });

    } else {

      const voteData = voteDoc.data();

      if ( voteData.value == value ) {

        // existing vote is the same as the button that was pressed, so delete
        // the vote document and revert the vote from the recipe's score
        t.delete(voteDocRef);
        t.update(recipeDocRef, { score: (currentRecipeScore - value) });

      } else {

        // existing vote is the opposite of the one pressed, so update the
        // vote doc, then apply it to the recipe's score by doubling it.
        // For example, if the current score is 1 and the user reverses their
        // +1 vote by pressing -1, we apply -2 so the score will become -1.
        t.set(voteDocRef, voteObj);
        t.update(recipeDocRef, { score: (currentRecipeScore + (value*2))});
      }

    }

    return Promise.resolve(true);

  });

}

0
投票

我面临着同样的问题,并决定使用批量写入和“正常”读取的组合。 我做出这个决定的原因是我需要进行许多互不依赖的阅读。起初我使用了类似于上面 Derrick 提出的方法,但事实证明它对于可能的读取来说是不可持续的。 该代码规定每个循环都会阻塞到下一个循环。 我所做的是将所有读取批处理以与

Promise.all
并行运行 这样做的缺点是你没有利用交易功能,但由于我感兴趣的领域没有改变,所以这是有道理的 这是我的示例代码

const batch = firestore().batch()
 const readPromises = invoiceValues.map(val => {
                        return orderCollection(omcId).where(<query field>, '<query operation>', <query>).get()
                    })

                    return Promise.all(readPromises).then(orderDocs => {
                  //Perform batch operations here
                        return batch.commit()
                     })

事实证明,这对于许多读取来说更有效,同时保持安全,因为我感兴趣的字段不会改变

© www.soinside.com 2019 - 2024. All rights reserved.