Điều kiện kích hoạt Cloud Firestore


Với Chức năng đám mây, bạn có thể xử lý các sự kiện trong Cloud Firestore mà không cần cập nhật mã ứng dụng. Bạn có thể thực hiện các thay đổi trong Cloud Firestore thông qua giao diện tổng quan nhanh về tài liệu hoặc thông qua SDK dành cho quản trị viên.

Trong một vòng đời thông thường, một hàm Cloud Firestore sẽ thực hiện những việc sau:

  1. Chờ các thay đổi đối với một tài liệu cụ thể.
  2. Kích hoạt khi một sự kiện xảy ra và thực hiện các tác vụ của sự kiện đó.
  3. Nhận đối tượng dữ liệu chứa thông tin tổng quan nhanh về dữ liệu được lưu trữ trong tài liệu được chỉ định. Để ghi hoặc cập nhật các sự kiện, đối tượng dữ liệu chứa hai thông tin tổng quan nhanh thể hiện trạng thái dữ liệu trước và sau sự kiện kích hoạt.

Khoảng cách giữa vị trí của thực thể Firestore và vị trí của hàm có thể tạo ra độ trễ mạng đáng kể. Để tối ưu hoá hiệu suất, hãy cân nhắc chỉ định vị trí của hàm (nếu có).

Điều kiện kích hoạt hàm Cloud Firestore

SDK Cloud Functions cho Firebase sẽ xuất một đối tượng functions.firestore cho phép bạn tạo các trình xử lý liên kết với các sự kiện Cloud Firestore cụ thể.

Loại sự kiện Điều kiện kích hoạt
onCreate Được kích hoạt khi tài liệu được ghi vào lần đầu tiên.
onUpdate Được kích hoạt khi tài liệu đã tồn tại và có bất kỳ giá trị nào thay đổi.
onDelete Được kích hoạt khi tài liệu có dữ liệu bị xoá.
onWrite Được kích hoạt khi onCreate, onUpdate hoặc onDelete được kích hoạt.

Nếu bạn chưa bật dự án sử dụng Cloud Functions cho Firebase, hãy đọc bài viết Bắt đầu: Viết và triển khai các hàm đầu tiên của bạn để định cấu hình và thiết lập dự án Cloud Functions cho Firebase.

Ghi hàm được kích hoạt trên Cloud Firestore

Xác định điều kiện kích hoạt hàm

Để xác định trình 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

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

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

Đường dẫn đế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 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ể, bạn có thể sử dụng hàm sau.

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

Chỉ định nhóm tài liệu bằng ký tự đại diện

Nếu bạn muốn đính kèm đ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 tập hợp nhất định, hãy sử dụng {wildcard} thay cho mã tài liệu:

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

Trong ví dụ này, khi một trường bất kỳ trên tài liệu users thay đổi, trường đó sẽ khớp với một ký tự đại diện có tên là userId.

Nếu một tài liệu trong users có các tập hợp con và một trường ở một trong các tài liệu của các tập hợp con đó bị thay đổi, thì ký tự đại diện userId sẽ 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 context.params. Bạn có thể xác định số lượng ký tự đại diện tuỳ thích để thay thế bộ sưu tập hoặc mã nhận dạng tài liệu phản cảm, ví dụ:

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

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 một bộ sưu tập bằng cách sử dụng trình xử lý onCreate()ký tự đại diện. Hàm ví dụ này gọi createUser mỗi khi thêm một hồ sơ người dùng mới:

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

Kích hoạt một hàm khi tài liệu được cập nhật

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 bằng cách sử dụng hàm onUpdate()ký tự đại diện. Hàm ví dụ này sẽ gọi updateUser nếu người dùng thay đổi hồ sơ của họ:

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

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á bằng cách sử dụng hàm onDelete()ký tự đại diện. Hàm ví dụ này gọi deleteUser khi người dùng xoá hồ sơ người dùng của họ:

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

Kích hoạt một hàm cho tất cả thay đổi đối với tài liệu

Nếu không quan tâm đến loại sự kiện được kích hoạt, bạn có thể theo dõi tất cả các thay đổi trong tài liệu Cloud Firestore bằng cách sử dụng hàm onWrite() với một ký tự đại diện. Hàm ví dụ này sẽ gọi modifyUser nếu một người dùng được tạo, cập nhật hoặc bị xoá:

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

Đọc và ghi dữ liệu

Khi một hàm được kích hoạ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 nên lấy dữ liệu từ tài liệu đã cập nhật hoặc lấy 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 change.before.data(), chứa ảnh chụp nhanh của tài liệu trước khi cập nhật. Tương tự, change.after.data() chứa trạng thái tổng quan nhanh của tài liệu sau khi cập nhật.

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

Bạn có thể truy cập vào các thuộc tính như bạn truy cập trong bất kỳ đối tượng nào 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 = snap.data().age;
const name = snap.data()['name'];

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

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 của bạn. Bạn có thể truy cập vào tài liệu đó dưới dạng DocumentReference trong thuộc tính ref của ảnh chụp nhanh được trả về cho hàm của bạn.

DocumentReference này bắt nguồn từ SDK Node.js của Cloud Firestore và bao gồm các phương thức như update(), set()remove() để bạn có thể dễ dàng sửa đổi tài liệu đã kích hoạt hàm này.

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

Dữ liệu bên ngoài sự kiện kích hoạt

Chức năng đám mây thực thi trong một môi trường đáng tin cậy, có nghĩa là các chức năng này được uỷ quyền như một tài khoản dịch vụ trong dự án của bạn. Bạn có thể đọc và ghi bằng SDK dành cho quản trị viên của Firebase:

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({ ... });
  });

Các điểm hạn chế

Lưu ý các điểm hạn chế sau đối với điều kiện kích hoạt Cloud Firestore cho Cloud Functions:

  • Việc đặt hàng không được đảm bảo. Các thay đổi nhanh có thể kích hoạt lệnh gọi hàm theo thứ tự ngoài dự kiế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 có thể dẫn đến nhiều lệnh gọi hàm. Tránh phụ thuộc vào cơ chế chỉ một lần và viết các hàm không thay đổi.
  • Cloud Firestore ở chế độ Datastore cần có Chức năng đám mây (thế hệ thứ 2). Chức năng đám mây (thế hệ 1) không hỗ trợ chế độ Kho dữ liệu.
  • Chức năng đám mây (thế hệ 1) chỉ hoạt động với cơ sở dữ liệu "(mặc định)" và không hỗ trợ cơ sở dữ liệu có tên trên Cloud Firestore. Vui lòng sử dụng Chức năng đám mây (thế hệ thứ 2) để định cấu hình sự kiện cho cơ sở dữ liệu được đặt tên.
  • Mỗi điều kiện kích hoạt được liên kết với một cơ sở dữ liệu duy nhất. Bạn không thể tạo điều kiện kích hoạt khớp với nhiều cơ sở dữ liệu.
  • Việc xoá cơ sở dữ liệu sẽ không tự động xoá bất kỳ điều kiện kích hoạt nào cho cơ sở dữ liệu đó. Điều kiện kích hoạt này 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 trùng 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ệ thứ 1).
    • Các sự kiện không được phân phối do kích thước yêu cầu được ghi vào nhật ký nền tảng và tính vào mức sử dụng nhật ký của 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 tới hàm Cloud do kích thước vượt quá giới hạn cho thế hệ 1..." về mức độ nghiêm trọng của error. Bạn có thể tìm thấy tên hàm trong trường functionName. Nếu trường receiveTimestamp vẫn còn trong vòng một giờ kể từ bây giờ, bạn có thể suy ra nội dung thực tế của sự kiện bằng cách đọc tài liệu được đề cập qua một bản tổng quan 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á các Chức năng đám mây được đề cập
    • Bạn có thể tắt tính năng ghi nhật ký bằng cách loại trừ, nhưng xin lưu ý rằng hệ thống sẽ vẫn không phân phối các sự kiện vi phạm.