Transactions 具有多个get的Firestore事务

Transactions 具有多个get的Firestore事务,transactions,angularfire2,google-cloud-firestore,Transactions,Angularfire2,Google Cloud Firestore,我正在尝试运行具有可变读取操作数的事务。 我将read()操作放在update()之前 正在读取上的Firestore文档 事务由任意数量的get()操作和任意数量的写入操作组成,如set()、update()或delete() 及 使用事务时,请注意: 读操作必须在写入操作之前进行 如果当前编辑影响到所调用的文档,则调用事务的函数(事务函数)可能会运行多次 事务读取 事务函数不应直接修改应用程序状态 但是没有提供一个实现。 当我尝试运行下面的代码时,我发现事务函数运行的时间更长,然后我得到

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

正在读取上的Firestore文档

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

使用事务时,请注意:

  • 读操作必须在写入操作之前进行
  • 如果当前编辑影响到所调用的文档,则调用事务的函数(事务函数)可能会运行多次 事务读取
  • 事务函数不应直接修改应用程序状态
但是没有提供一个实现。 当我尝试运行下面的代码时,我发现事务函数运行的时间更长,然后我得到一个异常。 但如果我只试一次,一切都会好起来

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

Firestore文档没有这样说,但答案隐藏在API引用中:

您可以使用
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'));

我不知道如何在纯类型脚本中实现这一点,但我能够找到一个使用承诺的方法,所以我调整了它以满足我的需要。它似乎工作正常,但是当我快速运行我的功能时(通过快速连续单击按钮),我会收到控制台错误,显示为
POSThttps://firestore.googleapis.com/v1beta1/projects/myprojectname/databases/(默认)/文档:提交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);

  });

}

我也面临同样的问题,决定使用批处理写入和“正常”读取的组合。 我的决定是基于这样一个事实,即我需要进行许多互不依赖的阅读。起初,我使用了一种类似于上面Derrick提出的方法,但事实证明,这种方法对may reads来说是不可持续的。 代码指示每个循环都阻塞到下一个循环。 我所做的是批处理所有读取以与
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()
                     })
const batch=firestore().batch()
const readPromises=invoiceValues.map(val=>{
returnordercollection(omcId).where(,'',).get()
})
返回Promise.all(readPromises)。然后(orderDocs=>{
//在此处执行批处理操作
返回batch.commit()
})

这已被证明对许多读取更有效,同时保持安全,因为我感兴趣的字段不会更改

您知道这一点吗?我也有同样的问题。在我的例子中,我有一个由未知数量的firestore引用组成的数组,我需要获取每个引用,然后向每个引用添加+1并更新它们。他们肯定需要一个文档中多个get的例子。不,我没有更新它,我使用另一个包含所有信息的结构解决了这个问题。通过这种方式,我可以进行一次读取。从Angular调用此命令时,我得到的
属性“getAll”在类型“Transaction”上不存在。
似乎
getAll
仅在nodejs Firestore API中可用,而在Angular等其他环境的库中不可用。读取时会锁定数据吗?如果没有,它将导致并发问题?您所说的“锁定数据”是什么意思?因为我相信锁定发生在db级别。所有操作都是相互独立的,因此,如果写入时发生冲突,则只有lasp操作将持续。因此,只有在保证查询在每次读取时都会产生唯一的结果并且在我的理解中没有“重复值”的情况下,此代码才是安全的。事务中发生的情况是:首先读取数据,然后写入数据库,通过应用特定的“锁”,事务将保证数据在读取之后和写入之前不会更改。这样,您就有信心在更新完成之前阅读最新信息。但在本例中,这样的数据一致性不能得到保证?如果其他人在您的“get”方法之后立即执行了更新怎么办?您完全正确,不,这不是事务。我不明白那部分。此代码可以应用的唯一场景是每个操作都是独立的。