转到控制台

使用 Cloud Functions 函数扩展 Cloud Firestore

借助 Cloud Functions,您可以部署 Node.js 代码来处理由 Cloud Firestore 数据库中的更改触发的事件。这样您就可以轻松向您的应用中添加服务器端功能,而无需运行您自己的服务器。

如需用例的示例,请参阅 Cloud Functions 有哪些用途? 或 GitHub 代码库中的 Functions 示例

Cloud Firestore 函数触发器

Cloud Functions for Firebase SDK 会导出一个 functions.firestore 对象,可用于创建与特定 Cloud Firestore 事件关联的处理程序。

事件类型 触发器
onCreate 首次写入某个文档时触发。
onUpdate 当某文档已存在且值发生了任何更改时触发。
onDelete 当文档的数据被删除时触发。
onWrite 当触发了 onCreateonUpdateonDelete 时触发。

如果您还没有启用了 Cloud Functions for Firebase 的项目,请参阅《使用入门:编写和部署您的第一批函数》,配置并设置 Cloud Functions for Firebase 项目。

编写 Cloud Firestore 触发的函数

定义函数触发器

要定义 Cloud Firestore 触发器,请指定文档路径和事件类型:

Node.js

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

exports.myFunction = functions.firestore
  .document('...')
  .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.updateUser = 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 触发器是一项测试版功能,具有一些已知限制:

  • 函数最长可能需要 10 秒钟才会响应 Cloud Firestore 中的更改。
  • 无法保证顺序。快速更改会以意想不到的顺序触发函数调用。
  • 事件至少会被传送一次,但单个事件可能会导致多次调用函数。避免依赖一次性机制,并编写幂等函数
  • 适用于 Cloud Functions 的 Cloud Firestore 触发器仅适用于原生模式下的 Cloud Firestore,不适用于 Datastore 模式 Cloud Firestore。