如何使用事务更新子节点下的多个子节点?

时间:2019-03-10 03:41:45

标签: typescript firebase firebase-realtime-database google-cloud-functions

这是我的数据结构,如何组织(firebase数据库):

 -database
  -productsList
    -item1: 0
    -item2: 0
    -item3: 0
    -item4: 0
  -orders
    -order1:
      -...
    -order2:
      -...
    -...

我正在构建一个包含产品列表的购物应用程序。客户可以选择要购买的商品并下订单。当他们这样做时,产品的数量将根据他们选择的数量而增加。 假设某客户选择购买2单位的item2和5单位的item4,当一个客户发送请求时,它将在不同的位置写入以下数据:

-orders
 -orderKey
  -items
   -item2: 2
   -item4: 5

然后,列表将更新为(基于onCreate方法):

-productsList
 -item1: 0
 -item2: 2
 -item3: 0
 -item4: 5

但是,让我们假设另一个客户同时提出了一个请求,产品列表必须承认这一点。

我很了解firebase交易,因此必须执行 作为交易而不是更新。 但是,在此处进行彻底搜索的所有示例仅显示了如何更新单个节点而不是多个子节点。

我正在使用云函数来完成此操作,这意味着我正在使用打字稿。

使用事务更新多个子级的最佳方法是什么?

怎么可能只获得并增加所需的节点?

请,我们将不胜感激。


编辑:Frank van Puffelen回答了我的问题后,我决定更清楚地说明我需要做的事情。

我只希望更新名为 productsList 一个节点,在此示例中,其子节点 item2 child4

这里有一些代码可以更好地说明它:

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp(functions.config().firebase)

export const onOrderCreate = functions.database
.ref('/orders/{oid}')
.onCreate(async (snapshot, context) => {
    const orderId = context.params.oid
    console.log(`new order made: ${oid}`)

    const orderData = snapshot.val()
    const items = getProductsAndQuantities(orderData)
    const path = 'productsList'

    return admin.database().ref(path).transaction(items => {
        // I would like to know if there's a way to update only the bought items, like
        const updatedList = {
            'item2': item2oldValue + 2,
            'item4': item2oldValue + 5 
        }
        return updatedList
    })

})

// Get the order data and return only the items and its quantities
function getProductsAndQuantities(data: any): Object {
    // Return items, exemple
    const products = {
        'item2': 2,
        'item4': 5 
    }

    return products
}

1 个答案:

答案 0 :(得分:0)

事务在JSON树中的单个节点上运行,但是它们可以在树中的任何节点上运行。

这意味着,如果需要以事务方式更新多个节点,则需要在这些节点的共同祖先处运行事务。因此,如果您需要同时更新ordersproductsList下的数据,则需要在它们的共同祖先运行事务。

请记住:

  • 事务首先读取该位置的值,然后将其返回给客户。这意味着您的事务在树中运行的越高,您将使用的带宽就越多。
  • 如果另一个客户端在读取和写入之间更改了数据,则会自动重试事务。在更高级别的位置上执行此操作,使交易更有可能受到竞争,即多个客户端同时在该位置下更新数据。

一种选择是使用多位置更新。与事务不同,多位置更新不会读取整个公共祖先的值,并且不会自动重试。

这种方法要考虑的一些缺点/事情:

  • 如果新值取决于现有值,则仍然必须自己读取这些值。但是,与事务相比,优点是您可以读取要更新的精确值,而不用读取整个共同祖先。
  • 多位置更新只是更新,这意味着它们没有检测冲突更新的机制。您可以在安全规则中实施此操作,但绝对不容易。
  • 多位置更新不会自动重试,因此,如果安全规则拒绝更新(例如:如果另一个客户端刚刚更新了相同的值),则您必须自己在客户端代码中进行处理。

有关此方法的更多信息,请在这里查看我的答案:Is the way the Firebase database quickstart handles counts secure?


我能想到的最后一种选择是在可控制并行性的受信任环境中运行此代码。例如,假设您在常规节点进程中运行它,那么您可以确保只有一个实例在运行。然后,如果您还确保只有该(管理)过程可以更新这些值,则不需要事务处理就可以进行更新,并且可以更好地进行扩展。