Rozszerzanie Cloud Firestore za pomocą Cloud Functions

Dzięki Cloud Functions możesz wdrażać kod Node.js do obsługi zdarzeń wywoływanych przez zmiany w bazie danych Cloud Firestore. Dzięki temu możesz łatwo dodawać do aplikacji funkcje po stronie serwera bez konieczności uruchamiania własnych serwerów.

Przykłady zastosowań znajdziesz w artykule Co mogę zrobić za pomocą: Cloud Functions? lub Przykładowe funkcje na GitHubie.

Cloud Firestore aktywatora funkcji

Pakiet SDK Cloud Functions for Firebase eksportuje pakiet functions.firestore , który pozwala tworzyć moduły obsługi powiązane z określonymi zdarzeniami Cloud Firestore.

Typ zdarzenia Aktywator
onCreate Wywoływane, gdy dokument jest zapisywany po raz pierwszy.
onUpdate Wywoływane, gdy dokument już istnieje, ale jego wartość uległa zmianie.
onDelete Wywoływane po usunięciu dokumentu z danymi.
onWrite Wywoływane, gdy zostanie wywołane zdarzenie onCreate, onUpdate lub onDelete.

Jeśli nie masz jeszcze w projekcie włączonego projektu Cloud Functions for Firebase, przeczytaj Pierwsze kroki: pisanie i wdrażanie pierwszych funkcji aby skonfigurować projekt Cloud Functions for Firebase.

Zapisywanie funkcji wywoływanych przez Cloud Firestore

Zdefiniuj aktywator funkcji

Aby zdefiniować regułę Cloud Firestore, podaj ścieżkę dokumentu i typ zdarzenia:

Node.js

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

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

Ścieżki dokumentów mogą odwoływać się do konkretnego dokumentu lub wzorzec wieloznaczny.

Wskaż pojedynczy dokument

Jeśli chcesz wywołać zdarzenie w przypadku dowolnej zmiany w określonym dokumencie: możesz użyć następującej funkcji.

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

Określanie grupy dokumentów za pomocą symboli wieloznacznych

Jeśli chcesz dołączyć czynnik uruchamiający do grupy dokumentów, takich jak dowolny dokument w dla określonej kolekcji, a następnie użyj {wildcard} zamiast identyfikator dokumentu:

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

W tym przykładzie, gdy zmieni się dowolne pole w dowolnym dokumencie w dokumencie users, zostanie ono dopasowane symbol wieloznaczny o nazwie userId.

Jeśli dokument w kolekcji users zawiera kolekcje podrzędne i pole w jednej z tych kolekcji dokument został zmieniony, symbol wieloznaczny userId nie zostanie wywołany.

Dopasowania do symboli wieloznacznych są wyodrębniane z ścieżki dokumentu i przechowywane w context.params. Możesz zdefiniować dowolną liczbę symboli wieloznacznych, aby zastąpić zbiór jawny identyfikatory dokumentów, na przykład:

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

Wyzwalacze zdarzeń

Wywoływanie funkcji po utworzeniu nowego dokumentu

Możesz aktywować funkcję uruchamiania za każdym razem, gdy w kolekcji zostanie utworzony nowy dokument za pomocą modułu obsługi onCreate() z symbolem wieloznacznym. Ta przykładowa funkcja wywołuje createUser za każdym razem, gdy dodawany jest nowy profil użytkownika:

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

Wywoływanie funkcji po zaktualizowaniu dokumentu

Możesz też aktywować funkcję uruchamiania po zaktualizowaniu dokumentu za pomocą funkcji onUpdate() z symbolem wieloznacznym. Ta przykładowa funkcja wywołuje updateUser, jeśli użytkownik zmienia swój profil:

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

Aktywowanie funkcji po usunięciu dokumentu

Możesz też uruchomić funkcję po usunięciu dokumentu, używając funkcji onDelete() z symbolem wieloznacznym. Ten przykład funkcja wywołuje deleteUser, gdy użytkownik usunie swój profil:

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

Aktywowanie funkcji przy wszystkich zmianach w dokumencie

Jeśli nie interesuje Cię typ wywoływanego zdarzenia, możesz nasłuchiwać zmiany w dokumencie Cloud Firestore przy użyciu funkcji onWrite() za pomocą symbolu wieloznacznego. Ta przykładowa funkcja wywołuje funkcję modifyUser, jeśli użytkownik zostanie utworzony, zaktualizowany lub usunięty:

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

Odczytywanie i zapisywanie danych

Po wywołaniu funkcja udostępnia zrzut danych związanych z . Możesz użyć tego zrzutu do odczytu lub zapisu w dokumencie, który wywołało zdarzenie lub użyj pakietu Firebase Admin SDK, aby uzyskać dostęp do innych części. Twojej bazy danych.

Dane zdarzenia

Odczytywanie danych

Po wywołaniu funkcji warto pobrać dane z dokumentu, który została zaktualizowana, lub pobierz dane przed aktualizacją. Wcześniejsze dane możesz uzyskać, używając funkcji change.before.data(), który zawiera zrzut dokumentu przed aktualizacją. Podobnie change.after.data() zawiera stan zrzutu dokumentu po fragmencie .

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

Możesz uzyskać dostęp do właściwości tak samo jak w przypadku każdego innego obiektu. Możesz też użyć funkcji get, aby uzyskać dostęp do określonych pól:

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

Zapisywanie danych

Każde wywołanie funkcji jest powiązane z konkretnym dokumentem w Twojej bazie danych Cloud Firestore. Dostęp do tego dokumentu uzyskasz jako DocumentReference w właściwości ref przechwytywania zwracanego przez funkcję.

To źródło danych (DocumentReference) pochodzi z: Cloud Firestore pakiet SDK Node.js i obejmuje metody takie jak update(), set() i remove(), dzięki czemu możesz modyfikować dokument, który uruchomił tę funkcję.

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

Dane poza zdarzeniem aktywującym

Cloud Functions uruchamiać w zaufanym środowisku, co oznacza, że są autoryzowane jako konto usługi w Twoim projekcie. Za pomocą pakietu Firebase Admin SDK możesz wykonywać operacje odczytu i zapisu:

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

Ograniczenia

Zwróć uwagę na te ograniczenia reguł Cloud Firestore dla Cloud Functions:

  • Cloud Functions (pierwsza generacja) wymaga istniejącej bazy danych „(domyślna)” w trybie natywnym Firestore. Nie obsługują nazwane bazy danych Cloud Firestore lub tryb Datastore. Użyj konta Cloud Functions (2 generacji), aby skonfigurować zdarzenia w takich przypadkach.
  • Zamówienie nie jest gwarantowane. Nagłe zmiany mogą powodować wywołania funkcji w nieoczekiwanej kolejności.
  • Zdarzenia są wysyłane co najmniej raz, ale pojedyncze zdarzenie może spowodować wielokrotne wywołanie funkcji. Unikaj zależnie od mechanika dokładnie raz, funkcji idempotentnych.
  • Cloud Firestore w trybie Datastore wymaga Cloud Functions (2 generacji). Cloud Functions (1 generacji) nie obsługują tryb Datastore.
  • Aktywator jest powiązany z jedną bazą danych. Nie można utworzyć aktywatora pasującego do wielu baz danych.
  • Usunięcie bazy danych nie powoduje automatycznego usunięcia żadnych jej aktywatorów. reguła przestaje dostarczać zdarzenia, ale będzie istnieć do momentu jej usunięcia;
  • Jeśli dopasowane zdarzenie przekracza maksymalny rozmiar żądania, w tagu zdarzenie może nie zostać dostarczone do domeny Cloud Functions (1 generacji).
    • Zdarzenia, które nie zostały dostarczone z powodu rozmiaru żądania, są rejestrowane w logach platformy. i wliczają się do wykorzystania logów w projekcie.
    • Te logi znajdziesz w eksploratorze logów z komunikatem „Event cannot deliver to Cloud function due to size exceeding the limit for 1st gen..." o poważnym error. Nazwa funkcji znajduje się w polu functionName. Jeśli pole receiveTimestamp znajduje się jeszcze w ciągu godziny, możesz określić faktycznej treści wydarzenia, odczytując dany dokument z przed sygnaturą czasową i po niej.
    • Aby ich uniknąć:
      • Przeprowadź migrację i przejdź na Cloud Functions (2 generacji)
      • Zmniejsz rozmiar dokumentu
      • Usuń: Cloud Functions, którego dotyczy problem
    • Możesz wyłączyć samo rejestrowanie za pomocą wykluczeń, ale pamiętaj, że nieprawidłowe zdarzenia nadal nie będą dostarczane.