【问题标题】:How to query subcollection inside transaction in firebase?如何在firebase中查询事务内的子集合?
【发布时间】:2020-09-21 15:13:16
【问题描述】:

我的根集合 tradeRequests 中有一个名为 requests 的子集合

我想使用 firebase 中的事务查询我的 requests 子集合

下面的代码将失败,因为Transaction.get() requires its first argument to be a DocumentReference

const getOne = (itemID) => db.runTransaction(async (transaction) => {
  const tradeRequestItemRef = tradeRequestsRef.doc(itemID);
  const requestsRef = tradeRequestItemRef.collection('requests');

  const rawTradeRequestItem = await transaction.get(tradeRequestItemRef);

  if (!rawTradeRequestItem) return null;

  // something like this ???
  // But this line will break everything
  // Error: Transaction.get() requires its first argument to be a DocumentReference
  const rawRequests = transaction.get(requestsRef);

  return {
    ...normalizeData(rawTradeRequestItem),
    requests: normalizeData(rawRequests),
  };
});

【问题讨论】:

    标签: javascript database firebase google-cloud-firestore


    【解决方案1】:

    在网络和移动客户端 SDK 中,您无法在同一事务中处理查询结果。您只能使用 DocumentReference 单独获取()每个文档。因此,您可以改为先执行查询,然后在事务中 get() 来自该查询的每个单独文档引用,以确保它被原子操作。

    // query for requests before the transaction
    const requests = await db.collection("requests").get()
    
    await db.runTransaction(async transaction => {
        // get each document individually again in the transaction
        const requestsInTransaction = []
        for (const doc of requests.docs) {
            const requestInTransaction = await transction.get(doc.ref)
            requestsInTransaction.push(requestInTransaction)
        }
    
        // Work with each document as needed...
        return whatever_you_want
    })
    

    请注意,如果在事务期间原始查询的结果发生变化,则事务将不会看到任何新的或删除的文档。

    【讨论】:

    • 在上一句中,您说事务运行时原始查询的变化不会被事务看到。那么......甚至在集合中使用交易的全部意义是什么?!
    • @dontdownvoteme 对于客户端应用程序,交易要求您知道在交易时要处理的每个文档。这实际上是一种非常普遍的情况。对整个集合或查询结果(可能很大)进行事务处理将是一个性能问题。
    • 服务器端呢?如果您运行查询事务,是否会确保在事务完成之前集合不会更改?
    • @dontdownvoteme 不,您只能确保最多 500 个单独的文档不会更改。如果您从针对集合的查询中获得这些信息,那很好。但为了防止性能和可扩展性问题,它仍然只有 500 个最大值。
    【解决方案2】:

    正如 doc 关于 Firestore 的“事务可串行化和隔离”中所述,移动/网络 SDK(iOS、Android、Web、C++)使用乐观并发控制

    文档解释说:

    事务会跟踪您在 事务 ... 并完成其写入操作仅当没有 这些文件在交易执行期间发生了变化。如果有的话 文档确实发生了变化,事务处理程序重试事务。 如果事务在重试几次后仍不能得到干净的结果,则 由于数据争用,交易失败。

    因此,在跟踪 Query 返回的所有文档时出现太多数据争用失败的风险被认为太高,因此移动/网络 SDK 不提供在 Transaction 中执行 Query 的可能性。


    另一方面,Admin SDK 使用悲观并发控制,因此您可以在 Transaction 中执行查询,并对 Query 返回的所有文档设置悲观锁定。因此,一种解决方案是使用 Callable Cloud Function(使用 Admin SDK)并从您的客户端调用此 Cloud Function。

    更具体地说,您将在 Cloud Function 中使用来自 Google Cloud Firestore 的 Node.js 服务器 SDK 中的 get() 方法,该方法可以传递一个 Query 并将对所有返回的文档进行悲观锁定.

    【讨论】:

      猜你喜欢
      • 2019-12-20
      • 1970-01-01
      • 2020-10-10
      • 1970-01-01
      • 1970-01-01
      • 2023-03-31
      • 2019-03-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多