即時資料庫觸發條件


有了 Cloud Functions,您就能在 Firebase Realtime Database 中處理事件,而無需更新用戶端程式碼。Cloud Functions 可讓您以完整的管理權限執行 Realtime Database 作業,並確保對 Realtime Database 的每項變更都會個別處理。您可以透過資料快照或 Admin SDK 變更 Firebase Realtime Database

在典型的生命週期中,Firebase Realtime Database 函式會執行以下操作:

  1. 等待特定 Realtime Database 路徑的變更。
  2. 在事件發生時觸發並執行它的工作。
  3. 接收包含該路徑中儲存資料快照的資料物件。

您可以觸發函式,回應 Firebase Realtime Database 中資料庫節點的寫入、建立、更新或刪除作業。如要控制函式觸發時機,請指定其中一個事件處理常式,並指定要監聽事件的 Realtime Database 路徑。

設定函式位置

Realtime Database 例項位置與函式位置之間的距離可能會造成嚴重的網路延遲。此外,區域之間的不一致性也可能導致部署失敗。為避免發生這種情況,請指定函式位置,讓其與資料庫執行個體位置相符。

處理 Realtime Database 事件

透過函式,您可以以兩種精確度處理 Realtime Database 事件:您可以專門監聽寫入、建立、更新或刪除事件,也可以監聽參照項目的任何變更。

以下是用於回應 Realtime Database 事件的處理常式:

Node.js

  • onValueWritten()Realtime Database 中建立、更新或刪除資料時觸發。
  • onValueCreated() 只有在 Realtime Database 中建立資料時才會觸發。
  • onValueUpdated() 只有在 Realtime Database 中更新資料時才會觸發。
  • onValueDeleted() 只有在 Realtime Database 中刪除資料時才會觸發。

Python

  • on_value_written()Realtime Database 中建立、更新或刪除資料時觸發。
  • on_value_created() 只有在 Realtime Database 中建立資料時才會觸發。
  • on_value_updated() 只有在 Realtime Database 中更新資料時才會觸發。
  • on_value_deleted() 只有在 Realtime Database 中刪除資料時才會觸發。

匯入必要模組

您必須在函式來源中匯入要使用的 SDK 模組。在這個範例中,您必須匯入 HTTP 和 Realtime Database 模組,以及用於寫入 Realtime DatabaseFirebase Admin SDK 模組。

Node.js

// The Cloud Functions for Firebase SDK to setup triggers and logging.
const {onRequest} = require("firebase-functions/v2/https");
const {onValueCreated} = require("firebase-functions/v2/database");
const {logger} = require("firebase-functions");

// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp();

Python

# The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
from firebase_functions import db_fn, https_fn

# The Firebase Admin SDK to access the Firebase Realtime Database.
from firebase_admin import initialize_app, db

app = initialize_app()

指定執行個體和路徑

如要控管函式應在何時何處觸發,請使用路徑和選用的 Realtime Database 例項設定函式。如果您未指定例項,函式會監聽函式區域中的所有 Realtime Database 例項。您也可以指定 Realtime Database 執行個體模式,將部署作業指定給同一地區的部分執行個體。

例如:

Node.js

// 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) => {
    // …
  }
);

Python

# 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.
@db_fn.on_value_written(r"/user/{uid}")
def onwrittenfunctiondefault(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

# Instance named "my-app-db-2", at path "/user/{uid}".
# The "my-app-db-2" instance must exist in this region.
@db_fn.on_value_written(
    reference=r"/user/{uid}",
    instance="my-app-db-2",
    # This example assumes us-central1, but to set location:
    # region="europe-west1",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

# 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.
@db_fn.on_value_written(
    reference=r"/user/{uid=*@gmail.com}",
    instance="my-app-db-*",
    # This example assumes us-central1, but to set location:
    # region="europe-west1",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

這些參數會指示函式在 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:用於單一區段的萬用字元。注意:** 代表多段萬用字元,Realtime Database 不支援這類萬用字元。請參閱「瞭解路徑模式」。

路徑萬用字元。您可以將路徑元件指定為萬用字元:

  • 使用星號 *。舉例來說,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 中,您可以在指定 refinstance 時使用模式。每個觸發事件介面都會提供下列函式範圍選項:

指定 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 事件觸發時,會將 Event 物件傳遞至處理常式函式。這個物件具有 data 屬性,針對建立和刪除事件,其中包含建立或刪除的資料快照。

在這個範例中,函式會擷取參照路徑的資料,將該位置的字串轉換為大寫,並將修改過的字串寫入資料庫:

Node.js

// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
// for all databases in 'us-central1'
exports.makeuppercase = onValueCreated(
    "/messages/{pushId}/original",
    (event) => {
    // Grab the current value of what was written to the Realtime Database.
      const original = event.data.val();
      logger.log("Uppercasing", event.params.pushId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing
      // asynchronous tasks inside a function, 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);
    },
);

Python

@db_fn.on_value_created(reference="/messages/{pushId}/original")
def makeuppercase(event: db_fn.Event[Any]) -> None:
    """Listens for new messages added to /messages/{pushId}/original and
    creates an uppercase version of the message to /messages/{pushId}/uppercase
    """

    # Grab the value that was written to the Realtime Database.
    original = event.data
    if not isinstance(original, str):
        print(f"Not a string: {event.reference}")
        return

    # Use the Admin SDK to set an "uppercase" sibling.
    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    parent = db.reference(event.reference).parent
    if parent is None:
        print("Message can't be root node.")
        return
    parent.child("uppercase").set(upper)

讀取先前的值

對於 writeupdate 事件,data 屬性是 Change 物件,其中包含兩個快照,分別代表觸發事件前後的資料狀態。Change 物件具有 before 屬性,可讓您檢查事件發生前儲存至 Realtime Database 的內容,以及代表事件發生後資料狀態的 after 屬性。

舉例來說,您可以使用 before 屬性,確保函式在首次建立時只會將文字轉為大寫:

Node.js

  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);
      });

Python

@db_fn.on_value_written(reference="/messages/{pushId}/original")
def makeuppercase2(event: db_fn.Event[db_fn.Change]) -> None:
    """Listens for new messages added to /messages/{pushId}/original and
    creates an uppercase version of the message to /messages/{pushId}/uppercase
    """

    # Only edit data when it is first created.
    if event.data.before is not None:
        return

    # Exit when the data is deleted.
    if event.data.after is None:
        return

    # Grab the value that was written to the Realtime Database.
    original = event.data.after
    if not hasattr(original, "upper"):
        print(f"Not a string: {event.reference}")
        return

    # Use the Admin SDK to set an "uppercase" sibling.
    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    parent = db.reference(event.reference).parent
    if parent is None:
        print("Message can't be root node.")
        return
    parent.child("uppercase").set(upper)