借助 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 |
在触发 onCreate 、onUpdate 或 onDelete 时触发。 |
如果您还没有启用 Cloud Functions for Firebase 的项目,请参阅《使用入门:编写和部署您的第一批函数》,配置并设置 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 Functions(第 1 代)前提条件是 Firestore 原生模式的现有“(默认)”数据库。它不支持 Cloud Firestore 命名数据库或 Datastore 模式。在这种情况下,请使用 Cloud Functions(第 2 代)来配置事件。
- 无法保证顺序。快速更改可能会以意想不到的顺序触发函数调用。
- 事件至少会被传送一次,但单个事件可能会导致多次调用函数。应该避免依赖“正好一次”机制,并编写幂等函数。
- Datastore 模式下的 Cloud Firestore 需要 Cloud Functions(第 2 代)。Cloud Functions(第 1 代)不支持 Datastore 模式。
- 一个触发器与单一数据库相关联。您无法创建与多个数据库匹配的触发器。
- 删除数据库不会自动删除该数据库的任何触发器。触发器会停止传送事件,但会继续存在,直到您删除触发器。
- 如果匹配的事件超过请求大小上限,该事件可能不会传送到 Cloud Functions(第 1 代)。
- 因请求大小而未传送的事件会记录在平台日志中,并计入项目的日志使用量。
- 您可以在 Logs Explorer 中找到这些日志,其严重性为
error
且内容为“由于大小超出第 1 代的限制,因此事件无法传送到 Cloud Functions 函数”消息。您可以在functionName
字段下方找到函数名称。如果receiveTimestamp
字段仍在从现在起的一小时内,您可以利用该时间戳之前和之后的快照来读取相关文档,从而推断实际事件内容。 - 为避免这种情况发生,您可以:
- 迁移和升级到 Cloud Functions(第 2 代)
- 缩小文档
- 删除相关的 Cloud Functions
- 您可以使用排除功能关闭日志记录功能本身,但请注意,违规事件仍然不会传送。