Catch up on everthing we announced at this year's Firebase Summit. Learn more

數據庫觸發器

借助 Cloud Functions,您無需更新客戶端代碼即可處理 Firebase 實時數據庫中的事件。 Cloud Functions 可讓您以完全管理權限運行實時數據庫操作,並確保對實時數據庫的每個更改都單獨處理。您可以通過實時火力地堡的數據庫更改DataSnapshot或通過管理SDK

在典型的生命週期中,Firebase 實時數據庫函數執行以下操作:

  1. 等待對特定實時數據庫位置的更改。
  2. 觸發事件發生時,並執行其任務(見我可以用雲功能做些什麼?有關使用案例的例子)。
  3. 接收一個數據對象,該對象包含存儲在指定文檔中的數據的快照。

觸發實時數據庫功能

創建用於實時的數據庫事件的新功能functions.database 。要控制函數何時觸發,請指定事件處理程序之一,並指定它將偵聽事件的實時數據庫路徑。

設置事件處理程序

函數讓您可以在兩個特定級別上處理實時數據庫事件;您可以只偵聽創建、更新或刪除事件,也可以偵聽任何類型的路徑更改。 Cloud Functions 支持實時數據庫的以下事件處理程序:

  • onWrite()其觸發器被創建數據時,更新或在實時數據庫中刪除。
  • onCreate()其觸發時在實時數據庫中創建新的數據。
  • onUpdate()該觸發器當數據以實時數據庫被更新。
  • onDelete()該觸發器當數據從實時數據庫中刪除。

指定實例和路徑

為了控制何時何地你的函數應該觸發,呼叫ref(path)指定的路徑,並任選指定實時數據庫實例instance('INSTANCE_NAME')如果您不指定實例,該函數將部署到 Firebase 項目的默認實時數據庫實例。例如:

  • 默認實時數據庫實例: functions.database.ref('/foo/bar')
  • 實例名為“我的-APP-DB-2”: functions.database.instance('my-app-db-2').ref('/foo/bar')

這些方法指示您的函數處理實時數據庫實例中特定路徑的寫入。路徑規格相匹配的觸摸路徑,包括發生在它下面的任何地方寫所有的寫操作。如果您為您的函數作為路徑/foo/bar ,它匹配在這兩個地點的事件:

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

在任一情況下,火力地堡解釋該事件發生在/foo/bar ,並且所述事件數據包括在舊的和新的數據/foo/bar 。如果事件數據可能很大,請考慮在更深的路徑中使用多個函數,而不是在數據庫根目錄附近使用單個函數。為獲得最佳性能,請僅請求最深層次的數據。

你可以用大括號將路徑組件指定為通配符; ref('foo/{bar}')相匹配的任何兒童/foo 。這些通配符路徑組件的值是內可用EventContext.params你的函數的對象。在這個例子中,該值可作為context.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();
      functions.logger.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);
    });