Cloud Firestore 觸發器


借助 Cloud Functions,您可以處理 Cloud Firestore 中的事件,而無需更新客戶端程式碼。您可以透過文件快照介面或透過Admin SDK進行 Cloud Firestore 變更。

在典型的生命週期中,Cloud Firestore 函數會執行以下操作:

  1. 等待對特定文檔的變更。
  2. 當事件發生並執行其任務時觸發。
  3. 接收一個資料對象,其中包含指定文件中儲存的資料的快照。對於寫入或更新事件,資料物件包含兩個快照,分別表示觸發事件之前和之後的資料狀態。

Firestore 執行個體的位置與函數的位置之間的距離可能會產生顯著的網路延遲。為了最佳化效能,請考慮在適用的情況下指定函數位置

Cloud Firestore 函數觸發器

Cloud Functions for Firebase SDK 匯出一個functions.firestore對象,讓您可以建立與特定 Cloud Firestore 事件關聯的處理程序。

事件類型扳機
onCreate第一次寫入文件時觸發。
onUpdate當文件已存在並且任何值發生變更時觸發。
onDelete當刪除包含資料的文件時觸發。
onWrite當觸發onCreateonUpdateonDelete時觸發。

如果您尚未為 Cloud Functions for Firebase 啟用項目,請閱讀入門:編寫和部署您的第一個 Functions以設定和設定您的 Cloud Functions for Firebase 項目。

編寫 Cloud Firestore 觸發的函數

定義函數觸發器

若要定義 Cloud Firestore 觸發器,請指定文件路徑和事件類型:

Node.js

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

exports.myFunction = functions.firestore
  .document('my-collection/{docId}')
  .onWrite((change, context) => { /* ... */ });

文件路徑可以引用特定文件通配符模式

指定單一文檔

如果您想要針對特定文件的任何變更觸發事件,則可以使用下列函數。

Node.js

// Listen for any change on document `marie` in collection `users`
exports.myFunctionName = functions.firestore
    .document('users/marie').onWrite((change, context) => {
      // ... Your code here
    });

使用通配符指定一組文檔

如果要將觸發器附加到一組文件(例如某個集合中的任何文件),請使用{wildcard}取代文件 ID:

Node.js

// Listen for changes in all documents in the 'users' collection
exports.useWildcard = functions.firestore
    .document('users/{userId}')
    .onWrite((change, context) => {
      // If we set `/users/marie` to {name: "Marie"} then
      // context.params.userId == "marie"
      // ... and ...
      // change.after.data() == {name: "Marie"}
    });

在此範例中,當users中的任何文件上的任何欄位發生變更時,它會符合名為userId的通配符。

如果users中的文件具有子集合,並且這些子集合的文件之一中的欄位發生更改,則不會觸發userId通配符。

從文件路徑中提取通配符匹配並將其儲存到context.params 。您可以定義任意數量的通配符來取代明確集合或文件 ID,例如:

Node.js

// Listen for changes in all documents in the 'users' collection and all subcollections
exports.useMultipleWildcards = functions.firestore
    .document('users/{userId}/{messageCollectionId}/{messageId}')
    .onWrite((change, context) => {
      // If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
      // context.params.userId == "marie";
      // context.params.messageCollectionId == "incoming_messages";
      // context.params.messageId == "134";
      // ... and ...
      // change.after.data() == {body: "Hello"}
    });

事件觸發器

建立新文件時觸發函數

透過使用帶有通配符onCreate()處理程序,您可以在集合中建立新文件時隨時觸發函數。每次新增使用者設定檔時,此範例函數都會呼叫createUser

Node.js

exports.createUser = functions.firestore
    .document('users/{userId}')
    .onCreate((snap, context) => {
      // Get an object representing the document
      // e.g. {'name': 'Marie', 'age': 66}
      const newValue = snap.data();

      // access a particular field as you would any JS property
      const name = newValue.name;

      // perform desired operations ...
    });

當文檔更新時觸發函數

您也可以使用帶有通配符的onUpdate()函數在更新文件時觸發函數。如果使用者更改其個人資料,此範例函數將呼叫updateUser

Node.js

exports.updateUser = functions.firestore
    .document('users/{userId}')
    .onUpdate((change, context) => {
      // Get an object representing the document
      // e.g. {'name': 'Marie', 'age': 66}
      const newValue = change.after.data();

      // ...or the previous value before this update
      const previousValue = change.before.data();

      // access a particular field as you would any JS property
      const name = newValue.name;

      // perform desired operations ...
    });

刪除文檔時觸發函數

您也可以使用帶有通配符的onDelete()函數在刪除文件時觸發函數。當使用者刪除其使用者設定檔時,此範例函數會呼叫deleteUser

Node.js

exports.deleteUser = functions.firestore
    .document('users/{userID}')
    .onDelete((snap, context) => {
      // Get an object representing the document prior to deletion
      // e.g. {'name': 'Marie', 'age': 66}
      const deletedValue = snap.data();

      // perform desired operations ...
    });

對文檔的所有更改觸發函數

如果您不關心觸發的事件類型,則可以使用帶有通配符onWrite()函數來偵聽 Cloud Firestore 文件中的所有變更。如果建立、更新或刪除用戶,此範例函數將呼叫modifyUser

Node.js

exports.modifyUser = functions.firestore
    .document('users/{userID}')
    .onWrite((change, context) => {
      // Get an object with the current document value.
      // If the document does not exist, it has been deleted.
      const document = change.after.exists ? change.after.data() : null;

      // Get an object with the previous document value (for update or delete)
      const oldDocument = change.before.data();

      // perform desired operations ...
    });

讀取和寫入數據

當觸發函數時,它會提供與事件相關的資料的快照。您可以使用此快照讀取或寫入觸發事件的文檔,或使用 Firebase Admin SDK 存取資料庫的其他部分。

事件數據

讀取數據

當觸發函數時,您可能想要從已更新的文件中取得數據,或取得更新先前的數據。您可以使用change.before.data()取得先前的數據,其中包含更新先前的文件快照。同樣, change.after.data()包含更新後的文檔快照狀態。

Node.js

exports.updateUser2 = functions.firestore
    .document('users/{userId}')
    .onUpdate((change, context) => {
      // Get an object representing the current document
      const newValue = change.after.data();

      // ...or the previous value before this update
      const previousValue = change.before.data();
    });

您可以像存取任何其他物件一樣存取屬性。或者,您可以使用get函數來存取特定欄位:

Node.js

// Fetch data using standard accessors
const age = snap.data().age;
const name = snap.data()['name'];

// Fetch data using built in accessor
const experience = snap.get('experience');

寫入數據

每個函數呼叫都與 Cloud Firestore 資料庫中的特定文件相關聯。您可以在傳回函數的快照的ref屬性中以DocumentReference形式存取該文件。

DocumentReference來自Cloud Firestore Node.js SDK ,包含update()set()remove()等方法,因此您可以輕鬆修改觸發函數的文件。

Node.js

// Listen for updates to any `user` document.
exports.countNameChanges = functions.firestore
    .document('users/{userId}')
    .onUpdate((change, context) => {
      // Retrieve the current and previous value
      const data = change.after.data();
      const previousData = change.before.data();

      // We'll only update if the name has changed.
      // This is crucial to prevent infinite loops.
      if (data.name == previousData.name) {
        return null;
      }

      // Retrieve the current count of name changes
      let count = data.name_change_count;
      if (!count) {
        count = 0;
      }

      // Then return a promise of a set operation to update the count
      return change.after.ref.set({
        name_change_count: count + 1
      }, {merge: true});
    });

觸發事件之外的數據

Cloud Functions 在受信任的環境中執行,這表示它們被授權為專案的服務帳戶。您可以使用Firebase Admin SDK執行讀取和寫入:

Node.js

const admin = require('firebase-admin');
admin.initializeApp();

const db = admin.firestore();

exports.writeToFirestore = functions.firestore
  .document('some/doc')
  .onWrite((change, context) => {
    db.doc('some/otherdoc').set({ ... });
  });

限制

請注意 Cloud Functions 的 Cloud Firestore 觸發器的以下限制:

  • 不保證訂購。快速變化可能會以意外的順序觸發函數呼叫。
  • 事件至少傳遞一次,但單一事件可能會導致多個函數呼叫。避免依賴一次性機制,並寫出冪等函數
  • 資料儲存模式下的 Cloud Firestore需要 Cloud Functions(第二代)。 Cloud Functions(第一代)不支援資料儲存模式。
  • Cloud Functions(第一代)僅適用於「(預設)」資料庫,不支援 Cloud Firestore 命名資料庫。請使用 Cloud Functions(第二代)為指定資料庫配置事件。
  • 觸發器與單一資料庫關聯。您無法建立與多個資料庫相符的觸發器。
  • 刪除資料庫不會自動刪除該資料庫的任何觸發器。觸發器停止傳送事件,但會繼續存在,直到您刪除觸發器
  • 如果符合的事件超出最大請求大小,則該事件可能不會傳遞到 Cloud Functions(第一代)。
    • 由於請求大小而未交付的事件將記錄在平台日誌中,並計入專案的日誌使用量。
    • 您可以在日誌資源管理器中找到這些日誌,其中包含error嚴重性訊息「由於大小超出第一代的限制,事件無法傳遞到雲端函數...」。您可以在functionName欄位下找到函數名稱。如果receiveTimestamp欄位仍在一個小時內,您可以透過讀取相關文件以及時間戳前後的快照來推斷實際事件內容。
    • 為了避免這種節奏,您可以:
      • 遷移並升級到 Cloud Functions(第二代)
      • 縮小文檔大小
      • 刪除有問題的雲端函數
    • 您可以使用排除功能關閉日誌記錄本身,但請注意,違規事件仍不會被傳遞。