实时数据库触发器

借助 Cloud Functions,您可以处理 Firebase 实时数据库中的事件,而无需更新客户端代码。Cloud Functions 让您能以完整的管理员权限执行数据库操作,并可确保对数据库所做的每项更改都会单独进行处理。您可以通过 DataSnapshotAdmin SDK 更改 Firebase 实时数据库。

在典型生命周期中,Firebase 实时数据库函数执行以下操作:

  1. 等待特定数据库位置发生更改。
  2. 在有事件发生时触发并执行相应的任务(请参阅 Cloud Functions 有哪些用途?,了解使用情形示例) 。
  3. 接收相关数据对象,该对象包含了存储在指定文档中的数据的快照。

触发数据库函数

您可以使用 functions.database 针对各种实时数据库事件创建新函数。要控制函数何时触发,请指定一个事件处理程序,并指定要侦听哪个数据库路径中的事件。

设置事件处理程序

函数可让您在两种不同的侦听范围内处理数据库事件;您可以专门侦听创建、更新或删除事件,也可以侦听某个路径上任何类型的更改。对于实时数据库,Cloud Functions 支持以下这些事件处理程序:

  • onWrite():当实时数据库中发生数据创建、数据更新或数据删除事件时触发。
  • onCreate():当实时数据库中发生数据创建事件时触发。
  • onUpdate():当实时数据库中发生数据更新事件时触发。
  • onDelete():当实时数据库中发生数据删除事件时触发。

指定数据库实例和路径

要控制函数在什么情况下触发,请调用 ref(path) 以指定路径,并视需要使用 instance('INSTANCE_NAME') 指定数据库实例。如果您不指定实例,则该函数将部署到 Firebase 项目的默认数据库实例。例如:

  • 默认数据库实例:functions.database.ref('/foo/bar')
  • 名为“my-app-db-2”的实例:functions.database.instance('my-app-db-2').ref('/foo/bar')

这些方法用于指示您的函数处理在数据库实例中的特定路径下的写入操作:路径规范会匹配与某个路径相关的所有写入操作(包括在此路径下任何位置发生的写入操作)。如果您将函数的路径设置为 /foo/bar,以下两个位置发生的事件都会与其匹配:

 /foo/bar
 /foo/bar/baz/really/deep/path

不管是哪种情况,Firebase 都会解读为事件是在 /foo/bar 下发生的,且事件数据包括路径 /foo/bar 下的旧数据和新数据。如果事件数据可能很大,请考虑在更深的路径下使用多个函数,而不是在数据库根目录附近使用单个函数。为了获得最佳性能,请仅在尽可能深的路径层级中请求数据。

您可以用大括号括住路径组成部分,将其指定为通配符;ref('foo/{bar}') 会匹配 /foo 的任何子路径。这些通配符路径组成部分的值可从函数的 EventContext.params 对象中获得。在此示例中,该值通过 event.params.bar 提供。

包含通配符的路径可以匹配单个写入操作引发的多个事件。例如,以下数据:

{
  "foo": {
    "hello": "world",
    "firebase": "functions"
  }
}

该数据的插入操作会匹配路径 "/foo/{bar}" 两次:一次匹配 "hello": "world",第二次匹配 "firebase": "functions"

处理事件数据

处理实时数据库事件时,返回的数据对象是 DataSnapshot。对于 onWriteonUpdate 事件,第一个参数是 Change 对象,其中包含代表触发事件前后的数据状态的两份快照。对于 onCreateonDelete 事件,返回的数据对象是已创建或已删除的数据的快照。

在以下示例中,函数会将指定路径的快照检索为 snap,将该位置中的字符串转换为大写字母,并将修改后的字符串写入数据库:

// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snapshot, context) => {
      // Grab the current value of what was written to the Realtime Database.
      const original = snapshot.val();
      console.log('Uppercasing', context.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 snapshot.ref.parent.child('uppercase').set(uppercase);
    });

访问用户身份验证信息

EventContext.authEventContext.authType 中,您可以访问触发函数的用户的用户信息(包括权限)。这对于强制实施安全规则非常有用,因为这样可让您的函数根据用户的权限级别完成不同的操作:

const functions = require('firebase-functions');
const admin = require('firebase-admin');

exports.simpleDbFunction = functions.database.ref('/path')
    .onCreate((snap, context) => {
      if (context.authType === 'ADMIN') {
        // do something
      } else if (context.authType === 'USER') {
        console.log(snap.val(), 'written by', context.auth.uid);
      }
    });

此外,您可以利用用户身份验证信息来“模拟”用户并代表用户执行写入操作。请务必删除如下所示的应用实例,以防出现并发问题:

exports.impersonateMakeUpperCase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snap, context) => {
      const appOptions = JSON.parse(process.env.FIREBASE_CONFIG);
      appOptions.databaseAuthVariableOverride = context.auth;
      const app = admin.initializeApp(appOptions, 'app');
      const uppercase = snap.val().toUpperCase();
      const ref = snap.ref.parent.child('uppercase');

      const deleteApp = () => app.delete().catch(() => null);

      return app.database().ref(ref).set(uppercase).then(res => {
        // Deleting the app is necessary for preventing concurrency leaks
        return deleteApp().then(() => res);
      }).catch(err => {
        return deleteApp().then(() => Promise.reject(err));
      });
    });

读取先前的值

Change 对象有一个 before 属性,该属性可用于检查在事件发生之前数据库中保存的内容。before 属性会返回一个 DataSnapshot,其中的所有方法(例如 val()exists())指向的都是先前的值。您可以通过以下两种方法再次读取新的值:使用原来的 DataSnapshot;或读取 after 属性。任意 Change 中的此属性都是另一个 DataSnapshot,代表事件发生后的数据状态。

例如,您可以使用 before 属性来确保函数仅在第一次创建后返回大写的文本:

exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onWrite((change, context) => {
      // Only edit data when it is first created.
      if (change.before.exists()) {
        return null;
      }
      // Exit when the data is deleted.
      if (!change.after.exists()) {
        return null;
      }
      // Grab the current value of what was written to the Realtime Database.
      const original = change.after.val();
      console.log('Uppercasing', context.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 change.after.ref.parent.child('uppercase').set(uppercase);
    });

发送以下问题的反馈:

此网页
需要帮助?请访问我们的支持页面