Với Cloud Functions, bạn có thể triển khai mã để xử lý các sự kiện được kích hoạt theo các thay đổi trong cơ sở dữ liệu Cloud Firestore. Tính năng này cho phép bạn dễ dàng thêm phía máy chủ vào ứng dụng của bạn mà không cần chạy máy chủ của riêng bạn.
Cloud Functions (thế hệ thứ 2)
Sử dụng công nghệ của Cloud Run và Eventarc, Cloud Functions for Firebase (thế hệ thứ 2) mang đến cho bạn trải nghiệm mạnh mẽ hơn cơ sở hạ tầng, khả năng kiểm soát nâng cao về hiệu suất và khả năng có thể mở rộng và hơn thế nữa quyền kiểm soát thời gian chạy của các hàm. Để biết thêm thông tin về thế hệ thứ 2, hãy xem Chức năng đám mây dành cho Firebase (thế hệ thứ 2). Để xem thêm về thế hệ thứ 1, xem Mở rộng Cloud Firestore bằng Cloud Functions.
Điều kiện kích hoạt hàm Cloud Firestore
SDK Cloud Functions for Firebase xuất Cloud Firestore sau trình kích hoạt sự kiện để cho phép bạn tạo các trình xử lý liên kết với Cloud Firestore cụ thể sự kiện:
Node.js
Loại sự kiện | Kích hoạt |
---|---|
onDocumentCreated |
Được kích hoạt khi người dùng viết tài liệu lần đầu tiên. |
onDocumentUpdated |
Được kích hoạt khi tài liệu đã tồn tại và có giá trị thay đổi. |
onDocumentDeleted |
Được kích hoạt khi tài liệu bị xoá. |
onDocumentWritten |
Được kích hoạt khi onDocumentCreated , onDocumentUpdated hoặc onDocumentDeleted được kích hoạt. |
onDocumentCreatedWithAuthContext |
onDocumentCreated có thông tin xác thực bổ sung |
onDocumentWrittenWithAuthContext |
onDocumentWritten có thông tin xác thực bổ sung |
onDocumentDeletedWithAuthContext |
onDocumentDeleted có thông tin xác thực bổ sung |
onDocumentUpdatedWithAuthContext |
onDocumentUpdated có thông tin xác thực bổ sung |
Python (bản xem trước)
Loại sự kiện | Kích hoạt |
---|---|
on_document_created |
Được kích hoạt khi người dùng viết tài liệu lần đầu tiên. |
on_document_updated |
Được kích hoạt khi tài liệu đã tồn tại và có giá trị thay đổi. |
on_document_deleted |
Được kích hoạt khi tài liệu bị xoá. |
on_document_written |
Được kích hoạt khi on_document_created , on_document_updated hoặc on_document_deleted được kích hoạt. |
on_document_created_with_auth_context |
on_document_created có thông tin xác thực bổ sung |
on_document_updated_with_auth_context |
on_document_updated có thông tin xác thực bổ sung |
on_document_deleted_with_auth_context |
on_document_deleted có thông tin xác thực bổ sung |
on_document_written_with_auth_context |
on_document_written có thông tin xác thực bổ sung |
Chỉ điều kiện kích hoạt các sự kiện Cloud Firestore về các thay đổi đối với tài liệu. Bản cập nhật cho tài liệu Cloud Firestore trong đó dữ liệu không thay đổi (ghi không hoạt động) sẽ không tạo ra sự kiện cập nhật hoặc ghi. Đó là không thể thêm sự kiện vào một số trường cụ thể.
Nếu bạn chưa bật dự án nào cho Cloud Functions for Firebase, hãy đọc Bắt đầu sử dụng Cloud Functions for Firebase (thế hệ thứ 2) để định cấu hình và thiết lập dự án Cloud Functions for Firebase.
Viết các hàm được kích hoạt Cloud Firestore
Xác định điều kiện kích hoạt hàm
Để xác định một điều kiện kích hoạt Cloud Firestore, hãy chỉ định đường dẫn tài liệu và loại sự kiện:
Node.js
import {
onDocumentWritten,
onDocumentCreated,
onDocumentUpdated,
onDocumentDeleted,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.myfunction = onDocumentWritten("my-collection/{docId}", (event) => {
/* ... */
});
Python (bản xem trước)
from firebase_functions.firestore_fn import (
on_document_created,
on_document_deleted,
on_document_updated,
on_document_written,
Event,
Change,
DocumentSnapshot,
)
@on_document_created(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot]) -> None:
Đường dẫn tài liệu có thể tham chiếu đến một tài liệu cụ thể hoặc mẫu ký tự đại diện.
Chỉ định một tài liệu
Nếu bạn muốn kích hoạt một sự kiện cho bất kỳ thay đổi nào đối với một tài liệu cụ thể, thì bạn có thể sử dụng hàm sau.
Node.js
import {
onDocumentWritten,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.myfunction = onDocumentWritten("users/marie", (event) => {
// Your code here
});
Python (bản xem trước)
from firebase_functions.firestore_fn import (
on_document_written,
Event,
Change,
DocumentSnapshot,
)
@on_document_written(document="users/marie")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
Chỉ định một nhóm tài liệu bằng ký tự đại diện
Nếu bạn muốn đính kèm một điều kiện kích hoạt vào một nhóm tài liệu, chẳng hạn như tài liệu bất kỳ trong
một bộ sưu tập nhất định, thì hãy sử dụng {wildcard}
thay cho
ID tài liệu:
Node.js
import {
onDocumentWritten,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.myfunction = onDocumentWritten("users/{userId}", (event) => {
// If we set `/users/marie` to {name: "Marie"} then
// event.params.userId == "marie"
// ... and ...
// event.data.after.data() == {name: "Marie"}
});
Python (bản xem trước)
from firebase_functions.firestore_fn import (
on_document_written,
Event,
Change,
DocumentSnapshot,
)
@on_document_written(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# If we set `/users/marie` to {name: "Marie"} then
event.params["userId"] == "marie" # True
# ... and ...
event.data.after.to_dict() == {"name": "Marie"} # True
Trong ví dụ này, khi bất kỳ trường nào trên tài liệu bất kỳ trong users
được thay đổi, trường đó khớp với
ký tự đại diện có tên là userId
.
Nếu tài liệu trong users
có các tập hợp con và một trường thuộc một trong các tập đó
các tập hợp con tài liệu được thay đổi thì ký tự đại diện userId
không được kích hoạt.
Các kết quả khớp với ký tự đại diện được trích xuất từ đường dẫn tài liệu và lưu trữ vào event.params
.
Bạn có thể xác định bao nhiêu ký tự đại diện tuỳ thích để thay thế tập hợp nội dung rõ ràng
hoặc mã nhận dạng tài liệu, ví dụ:
Node.js
import {
onDocumentWritten,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.myfunction = onDocumentWritten("users/{userId}/{messageCollectionId}/{messageId}", (event) => {
// If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
// event.params.userId == "marie";
// event.params.messageCollectionId == "incoming_messages";
// event.params.messageId == "134";
// ... and ...
// event.data.after.data() == {body: "Hello"}
});
Python (bản xem trước)
from firebase_functions.firestore_fn import (
on_document_written,
Event,
Change,
DocumentSnapshot,
)
@on_document_written(document="users/{userId}/{messageCollectionId}/{messageId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
event.params["userId"] == "marie" # True
event.params["messageCollectionId"] == "incoming_messages" # True
event.params["messageId"] == "134" # True
# ... and ...
event.data.after.to_dict() == {"body": "Hello"}
Điều kiện kích hoạt của bạn phải luôn trỏ đến một tài liệu, ngay cả khi bạn đang sử dụng ký tự đại diện.
Ví dụ: users/{userId}/{messageCollectionId}
không hợp lệ vì {messageCollectionId}
là một bộ sưu tập. Tuy nhiên, users/{userId}/{messageCollectionId}/{messageId}
là
hợp lệ vì {messageId}
sẽ luôn trỏ tới một tài liệu.
Trình kích hoạt sự kiện
Kích hoạt một hàm khi tạo tài liệu mới
Bạn có thể kích hoạt một hàm để kích hoạt bất cứ khi nào một tài liệu mới được tạo trong bộ sưu tập. Hàm ví dụ này kích hoạt mỗi khi hồ sơ người dùng mới được thêm:
Node.js
import {
onDocumentCreated,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.createuser = onDocumentCreated("users/{userId}", (event) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const snapshot = event.data;
if (!snapshot) {
console.log("No data associated with the event");
return;
}
const data = snapshot.data();
// access a particular field as you would any JS property
const name = data.name;
// perform more operations ...
});
Để biết thêm thông tin xác thực, hãy dùng onDocumentCreatedWithAuthContext
.
Python (bản xem trước)
from firebase_functions.firestore_fn import (
on_document_created,
Event,
DocumentSnapshot,
)
@on_document_created(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot]) -> None:
# Get a dictionary representing the document
# e.g. {'name': 'Marie', 'age': 66}
new_value = event.data.to_dict()
# Access a particular field as you would any dictionary
name = new_value["name"]
# Perform more operations ...
Kích hoạt một hàm khi cập nhật tài liệu
Bạn cũng có thể kích hoạt một hàm để kích hoạt khi tài liệu được cập nhật. Hàm ví dụ này kích hoạt nếu người dùng thay đổi hồ sơ của họ:
Node.js
import {
onDocumentUpdated,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.updateuser = onDocumentUpdated("users/{userId}", (event) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const newValue = event.data.after.data();
// access a particular field as you would any JS property
const name = newValue.name;
// perform more operations ...
});
Để biết thêm thông tin xác thực, hãy dùng onDocumentUpdatedWithAuthContext
.
Python (bản xem trước)
from firebase_functions.firestore_fn import (
on_document_updated,
Event,
Change,
DocumentSnapshot,
)
@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# Get a dictionary representing the document
# e.g. {'name': 'Marie', 'age': 66}
new_value = event.data.after.to_dict()
# Access a particular field as you would any dictionary
name = new_value["name"]
# Perform more operations ...
Kích hoạt một hàm khi tài liệu bị xoá
Bạn cũng có thể kích hoạt một hàm khi tài liệu bị xoá. Ví dụ này hàm kích hoạt khi người dùng xoá hồ sơ người dùng của họ:
Node.js
import {
onDocumentDeleted,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.deleteuser = onDocumentDeleted("users/{userId}", (event) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const snap = event.data;
const data = snap.data();
// perform more operations ...
});
Để biết thêm thông tin xác thực, hãy dùng onDocumentDeletedWithAuthContext
.
Python (bản xem trước)
from firebase_functions.firestore_fn import (
on_document_deleted,
Event,
DocumentSnapshot,
)
@on_document_deleted(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot|None]) -> None:
# Perform more operations ...
Kích hoạt một hàm cho tất cả thay đổi đối với một tài liệu
Nếu bạn không quan tâm đến loại sự kiện được kích hoạt, bạn có thể lắng nghe tất cả các thay đổi trong tài liệu Cloud Firestore bằng cách sử dụng "tài liệu đã viết" sự kiện điều kiện kích hoạt. Hàm ví dụ này kích hoạt nếu người dùng được tạo, cập nhật hoặc bị xoá:
Node.js
import {
onDocumentWritten,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.modifyuser = onDocumentWritten("users/{userId}", (event) => {
// Get an object with the current document values.
// If the document does not exist, it was deleted
const document = event.data.after.data();
// Get an object with the previous document values
const previousValues = event.data.before.data();
// perform more operations ...
});
Để biết thêm thông tin xác thực, hãy dùng onDocumentWrittenWithAuthContext
.
Python (bản xem trước)
from firebase_functions.firestore_fn import (
on_document_written,
Event,
Change,
DocumentSnapshot,
)
@on_document_written(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot | None]]) -> None:
# Get an object with the current document values.
# If the document does not exist, it was deleted.
document = (event.data.after.to_dict()
if event.data.after is not None else None)
# Get an object with the previous document values.
# If the document does not exist, it was newly created.
previous_values = (event.data.before.to_dict()
if event.data.before is not None else None)
# Perform more operations ...
Đọc và ghi dữ liệu
Khi được kích hoạt, một hàm sẽ cung cấp thông tin tổng quan nhanh về dữ liệu liên quan đến sự kiện. Bạn có thể sử dụng ảnh chụp nhanh này để đọc hoặc ghi vào tài liệu đã kích hoạt sự kiện hoặc sử dụng SDK quản trị của Firebase để truy cập vào các phần khác của cơ sở dữ liệu.
Dữ liệu sự kiện
Đọc dữ liệu
Khi một hàm được kích hoạt, bạn có thể muốn lấy dữ liệu từ tài liệu
đã được cập nhật hoặc nhận dữ liệu trước khi cập nhật. Bạn có thể lấy dữ liệu trước đó bằng cách sử dụng
event.data.before
chứa ảnh chụp nhanh tài liệu trước khi cập nhật.
Tương tự, event.data.after
chứa trạng thái chụp nhanh tài liệu sau
cập nhật.
Node.js
exports.updateuser2 = onDocumentUpdated("users/{userId}", (event) => {
// Get an object with the current document values.
// If the document does not exist, it was deleted
const newValues = event.data.after.data();
// Get an object with the previous document values
const previousValues = event.data.before.data();
});
Python (bản xem trước)
@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# Get an object with the current document values.
new_value = event.data.after.to_dict()
# Get an object with the previous document values.
prev_value = event.data.before.to_dict()
Bạn có thể truy cập vào các thuộc tính như cách bạn thực hiện trong mọi đối tượng khác. Ngoài ra, bạn
có thể dùng hàm get
để truy cập vào các trường cụ thể:
Node.js
// Fetch data using standard accessors
const age = event.data.after.data().age;
const name = event.data.after.data()['name'];
// Fetch data using built in accessor
const experience = event.data.after.data.get('experience');
Python (bản xem trước)
# Get the value of a single document field.
age = event.data.after.get("age")
# Convert the document to a dictionary.
age = event.data.after.to_dict()["age"]
Ghi dữ liệu
Mỗi lệnh gọi hàm được liên kết với một tài liệu cụ thể trong Cơ sở dữ liệu Cloud Firestore. Bạn có thể truy cập vào tài liệu đó trong ảnh chụp nhanh được trả về hàm của bạn.
Tài liệu tham khảo bao gồm các phương thức như update()
, set()
và remove()
để bạn có thể sửa đổi tài liệu đã kích hoạt hàm đó.
Node.js
import { onDocumentUpdated } from "firebase-functions/v2/firestore";
exports.countnamechanges = onDocumentUpdated('users/{userId}', (event) => {
// Retrieve the current and previous value
const data = event.data.after.data();
const previousData = event.data.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 data.after.ref.set({
name_change_count: count + 1
}, {merge: true});
});
Python (bản xem trước)
@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# Get the current and previous document values.
new_value = event.data.after
prev_value = event.data.before
# We'll only update if the name has changed.
# This is crucial to prevent infinite loops.
if new_value.get("name") == prev_value.get("name"):
return
# Retrieve the current count of name changes
count = new_value.to_dict().get("name_change_count", 0)
# Update the count
new_value.reference.update({"name_change_count": count + 1})
Truy cập thông tin xác thực người dùng
Nếu sử dụng một trong các loại sự kiện sau, bạn có thể truy cập thông tin xác thực người dùng về đối tượng chính đã kích hoạt sự kiện. Thông tin này bổ sung cho thông tin được trả về trong sự kiện cơ sở.
Node.js
onDocumentCreatedWithAuthContext
onDocumentWrittenWithAuthContext
onDocumentDeletedWithAuthContext
onDocumentUpdatedWithAuthContext
Python (bản xem trước)
on_document_created_with_auth_context
on_document_updated_with_auth_context
on_document_deleted_with_auth_context
on_document_written_with_auth_context
Để biết thông tin về dữ liệu có sẵn trong ngữ cảnh xác thực, hãy xem Ngữ cảnh xác thực. Ví dụ sau minh hoạ cách truy xuất thông tin xác thực:
Node.js
import { onDocumentWrittenWithAuthContext } from "firebase-functions/v2/firestore"
exports.syncUser = onDocumentWrittenWithAuthContext("users/{userId}", (event) => {
const snapshot = event.data.after;
if (!snapshot) {
console.log("No data associated with the event");
return;
}
const data = snapshot.data();
// retrieve auth context from event
const { authType, authId } = event;
let verified = false;
if (authType === "system") {
// system-generated users are automatically verified
verified = true;
} else if (authType === "unknown" || authType === "unauthenticated") {
// admin users from a specific domain are verified
if (authId.endsWith("@example.com")) {
verified = true;
}
}
return data.after.ref.set({
created_by: authId,
verified,
}, {merge: true});
});
Python (bản xem trước)
@on_document_updated_with_auth_context(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# Get the current and previous document values.
new_value = event.data.after
prev_value = event.data.before
# Get the auth context from the event
user_auth_type = event.auth_type
user_auth_id = event.auth_id
Dữ liệu bên ngoài sự kiện kích hoạt
Cloud Functions thực thi trong một môi trường đáng tin cậy. Đó là được uỷ quyền dưới dạng tài khoản dịch vụ trong dự án của mình, đồng thời bạn có thể đọc và ghi bằng SDK quản trị của Firebase:
Node.js
const { initializeApp } = require('firebase-admin/app');
const { getFirestore, Timestamp, FieldValue } = require('firebase-admin/firestore');
initializeApp();
const db = getFirestore();
exports.writetofirestore = onDocumentWritten("some/doc", (event) => {
db.doc('some/otherdoc').set({ ... });
});
exports.writetofirestore = onDocumentWritten('users/{userId}', (event) => {
db.doc('some/otherdoc').set({
// Update otherdoc
});
});
Python (bản xem trước)
from firebase_admin import firestore, initialize_app
import google.cloud.firestore
initialize_app()
@on_document_written(document="some/doc")
def myfunction(event: Event[Change[DocumentSnapshot | None]]) -> None:
firestore_client: google.cloud.firestore.Client = firestore.client()
firestore_client.document("another/doc").set({
# ...
})
Các điểm hạn chế
Xin lưu ý các giới hạn sau đối với điều kiện kích hoạt Cloud Firestore cho Cloud Functions:
- Cloud Functions (thế hệ thứ 1) đòi hỏi điều kiện tiên quyết là "(mặc định)" hiện có cơ sở dữ liệu ở chế độ gốc Firestore. Cách này không hỗ trợ cơ sở dữ liệu có tên Cloud Firestore hoặc chế độ Kho dữ liệu. Vui lòng sử dụng Cloud Functions (thế hệ thứ 2) để định cấu hình các sự kiện trong những trường hợp như vậy.
- Chúng tôi không đảm bảo việc sắp xếp thứ tự. Những thay đổi nhanh chóng có thể kích hoạt lệnh gọi hàm trong một đơn đặt hàng không mong muốn.
- Các sự kiện được phân phối ít nhất một lần nhưng một sự kiện duy nhất có thể dẫn đến việc nhiều lệnh gọi hàm. Tránh phụ thuộc vào chính xác một lần và viết các hàm không thay đổi.
- Cloud Firestore ở chế độ Kho dữ liệu cần có Cloud Functions (thế hệ thứ 2). Cloud Functions (thế hệ thứ 1) không hỗ trợ chế độ Datastore.
- Mỗi điều kiện kích hoạt được liên kết với một cơ sở dữ liệu. Bạn không thể tạo một điều kiện kích hoạt khớp với nhiều cơ sở dữ liệu.
- Việc xoá một cơ sở dữ liệu sẽ không tự động xoá điều kiện kích hoạt nào trong cơ sở dữ liệu đó. Chiến lược phát hành đĩa đơn điều kiện kích hoạt sẽ ngừng phân phối sự kiện nhưng vẫn tiếp tục tồn tại cho đến khi bạn xoá điều kiện kích hoạt.
- Nếu một sự kiện được so khớp vượt quá kích thước yêu cầu tối đa, thì
sự kiện có thể không được gửi đến Cloud Functions (thế hệ 1).
- Các sự kiện không được gửi do quy mô yêu cầu được ghi vào nhật ký nền tảng và được tính vào mức sử dụng nhật ký cho dự án.
- Bạn có thể tìm thấy các nhật ký này trong Trình khám phá nhật ký với thông báo "Sự kiện không thể phân phối đến
Hàm đám mây do kích thước vượt quá giới hạn của thế hệ 1..." /
error
mức độ nghiêm trọng. Bạn có thể tìm thấy tên hàm trong trườngfunctionName
. Nếu trườngreceiveTimestamp
vẫn còn trong vòng một giờ kể từ bây giờ, bạn có thể suy luận nội dung sự kiện thực tế bằng cách đọc tài liệu liên quan với ảnh chụp nhanh trước và sau dấu thời gian. - Để tránh tần suất như vậy, bạn có thể:
- Di chuyển và nâng cấp lên Cloud Functions (thế hệ thứ 2)
- Giảm kích thước tài liệu
- Xoá Cloud Functions liên quan
- Bạn có thể tắt tính năng ghi nhật ký bằng tính năng loại trừ nhưng xin lưu ý rằng các sự kiện vi phạm sẽ vẫn không được gửi.