在典型生命周期中,Firebase Realtime Database 函数会执行以下操作:
- 等待对特定 Realtime Database 路径执行的更改。
- 在事件发生时触发并执行相应的任务。
- 接收相关数据对象,该对象包含了存储在该路径下的数据的快照。
您可以触发一个函数来响应 Firebase Realtime Database 中的数据库节点的写入、创建、更新或删除操作。
在 Firebase Realtime Database 更改时触发函数
使用 firebase-functions/v2/database
子软件包创建用于处理 Firebase Realtime Database 事件的函数。如需控制函数何时触发,请指定一个事件处理程序,并指定要监听事件的 Realtime Database 路径。
设置函数位置
Realtime Database 实例与函数位置之间的距离可能会造成严重的网络延迟。此外,区域之间的不匹配也可能会导致部署失败。为避免这些情况,请指定函数位置,使其与数据库实例位置相匹配。
处理 Realtime Database 事件
函数可让您以两种不同的监听范围处理 Realtime Database 事件;您可以只监听写入、创建、更新或删除事件,也可以监听某个引用对象上任何类型的任何更改。
响应 Realtime Database 事件可使用以下处理程序:
onValueWritten()
:仅在 Realtime Database 中写入数据时触发。onValueCreated()
:仅在 Realtime Database 中创建数据时触发。onValueUpdated()
:仅在 Realtime Database 中更新数据时触发。onValueDeleted()
:仅在 Realtime Database 中删除数据时触发。
指定实例和路径
如需控制函数的触发时间和位置,请为函数配置一个路径,并视需要指定一个 Realtime Database 实例。如果您没有指定实例,函数将部署到函数区域中的所有 Realtime Database 实例。您还可以指定一种 Realtime Database 实例模式,将函数部署到在同一区域中选定的部分实例。
下面以使用 onValueWritten()
为例:
# All Realtime Database instances in default function region us-central1 at path "/user/{uid}" # There must be at least one Realtime Database present in us-central1. const onwrittenfunctiondefault = onValueWritten("/user/{uid}", (event) => { // … }); # Instance named "my-app-db-2", at path "/user/{uid}". # The "my-app-db-2" instance must exist in this region. const onwrittenfunctioninstance = onValueWritten( { ref: "/user/{uid}", instance: "my-app-db-2" // This example assumes us-central1, but to set location: // region: "europe-west1" }, (event) => { // … } ); # Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com. # There must be at least one Realtime Database with "my-app-db-*" prefix in this region. const onwrittenfunctioninstance = onValueWritten( { ref: "/user/{uid=*@gmail.com}", instance: "my-app-db-*" // This example assumes us-central1, but to set location: // region: "europe-west1" }, (event) => { // … } );
这些参数用于指示您的函数处理在 Realtime Database 实例中的特定路径下的写入操作。
根据路径规范,涉及该路径的所有写入操作均匹配(包括在此路径下任何位置发生的写入操作)。如果您将函数的路径设置为 /foo/bar
,则以下两个位置发生的事件均匹配:
/foo/bar
/foo/bar/baz/really/deep/path
不管是哪种情况,Firebase 都会解读为事件是在 /foo/bar
下发生的,且事件数据包括 /foo/bar
下的旧数据和新数据。如果事件数据可能很大,请考虑在更深的路径下使用多个函数,而不是在靠近数据库根目录处使用单个函数。为了获得最佳性能,请仅在尽可能深的路径层级中发出数据请求。
通配符和捕获
您可以使用 {key}
、{key=*}
、{key=prefix*}
、{key=*suffix}
进行捕获;使用 *
、prefix*
、*suffix
进行单段通配符匹配。注意:**
表示多段通配符匹配,RTDB 不支持此类通配符。请参阅了解路径模式。
路径通配符。您可以用通配符指定路径组成部分:
- 使用星号
*
。例如,foo/*
会匹配foo/
下一个级层的节点层次结构中的所有子项。 - 使用包含一个星号
*
的分段。例如,foo/app*-us
会匹配foo/
下使用app
前缀和-us
后缀的所有子段。
例如,包含通配符的路径可以匹配单个写入操作引发的多个事件。下列代码的一次插入
{
"foo": {
"hello": "world",
"firebase": "functions"
}
}
会匹配路径 "/foo/*"
两次:第一次是 "hello": "world"
,第二次是 "firebase": "functions"
。
路径捕获。您可以将路径匹配捕获到命名变量中,以供函数代码使用(例如 /user/{uid}
、/user/{uid=*-us}
)。
捕获变量的值在函数的 database.DatabaseEvent.params 对象中提供。
实例通配符。您还可以使用通配符来指定实例组件。实例通配符可以具有前缀和/或后缀(例如 my-app-*-prod
)。
通配符和捕获引用
使用 Cloud Functions(第 2 代)和 Realtime Database 时,您可以在指定 ref
和 instance
时使用模式。每个触发器接口都有以下选项,您可以用它们来限定函数范围:
指定 ref |
指定 instance |
行为 |
---|---|---|
单一 (/foo/bar ) |
未指定 | 将处理程序作用域限定为函数区域中的所有实例。 |
单一 (/foo/bar ) |
单一 (‘my-new-db' ) |
将处理程序作用域限定为函数区域中的特定实例。 |
单一 (/foo/bar ) |
模式 (‘inst-prefix*' ) |
将处理程序作用域限定为与函数区域中的模式匹配的所有实例。 |
模式 (/foo/{bar} ) |
未指定 | 将处理程序作用域限定为函数区域中的所有实例。 |
模式 (/foo/{bar} ) |
单一 (‘my-new-db' ) |
将处理程序作用域限定为函数区域中的特定实例。 |
模式 (/foo/{bar} ) |
模式 (‘inst-prefix*' ) |
将处理程序作用域限定为与函数区域中的模式匹配的所有实例。 |
处理事件数据
处理 Realtime Database 事件时,返回的数据对象是一个 DataSnapshot
。
对于 onValueWritten
或 onValueUpdated
事件,第一个参数是一个 Change
对象,其中包含代表触发事件前后的数据状态的两个快照。
对于 onValueCreated
和 onValueDeleted
事件,返回的数据对象是已创建或已删除的数据的快照。
在以下示例中,函数会将指定路径 foo/bar
的快照检索为 snap
,将该位置中的字符串转换为大写,并将修改后的字符串写入数据库:
// Listens for new messages added to /messages/:pushId/original and creates an // uppercase version of the message to /messages/:pushId/uppercase export makeuppercase = onValueCreated("foo/bar", (event) => { // Grab the current value of what was written to the Realtime Database. const original = event.data.val(); functions.logger.log('Uppercasing', event.params.pushId, original); const uppercase = original.toUpperCase(); // You must return a Promise when performing asynchronous tasks inside a Functions such as // writing to the Firebase Realtime Database. // Setting an "uppercase" sibling in the Realtime Database returns a Promise. return event.data.ref.parent.child('uppercase').set(uppercase); });
读取先前的值
Change
对象有一个 before
属性,可用于检查在事件发生之前保存到 Realtime Database 中的内容。before
属性会返回一个 DataSnapshot
,其中所有方法(例如 val()
和 exists()
)都会引用先前的值。您可以通过以下两种方法再次读取新的值:使用原始 DataSnapshot
或读取 after
属性。任何 Change
对象的此属性都是另一个 DataSnapshot
,代表事件发生后的数据状态。
例如,before
属性可用于确保函数仅在第一次创建记录时将文本改为大写:
exports makeuppercase = onValueWritten("/messages/{pushId}/original", (event) => { // Only edit data when it is first created. if (event.data.before.exists()) { return null; } // Exit when the data is deleted. if (!event.data.after.exists()) { return null; } // Grab the current value of what was written to the Realtime Database. const original = event.data.after.val(); console.log('Uppercasing', event.params.pushId, original); const uppercase = original.toUpperCase(); // You must return a Promise when performing asynchronous tasks inside a Functions such as // writing to the Firebase Realtime Database. // Setting an "uppercase" sibling in the Realtime Database returns a Promise. return event.data.after.ref.parent.child('uppercase').set(uppercase); });