Aktywatory Cloud Firestore


Dzięki Cloud Functions możesz obsługiwać zdarzenia w Cloud Firestore bez konieczności aktualizowania kodu klienta. Zmiany w Cloud Firestore możesz wprowadzać za pomocą interfejsu migawki dokumentu lub pakietu Admin SDK.

W typowym cyklu życia funkcja Cloud Firestore wykonuje te czynności:

  1. Oczekiwanie na zmiany w określonym dokumencie.
  2. Uruchamia się, gdy wystąpi zdarzenie, i wykonuje swoje zadania.
  3. Otrzymuje obiekt danych zawierający zrzut danych przechowywanych w określonym dokumencie. W przypadku zdarzeń zapisu lub aktualizacji obiekt danych zawiera 2 migawki, które przedstawiają stan danych przed zdarzeniem wywołującym i po nim.

Odległość między lokalizacją instancji Firestore a lokalizacją funkcji może powodować znaczne opóźnienia sieci. Aby zoptymalizować wydajność, w odpowiednich przypadkach rozważ określenie lokalizacji funkcji.

Aktywatory funkcji Cloud Firestore

Pakiet Cloud Functions for Firebase SDK eksportuje obiekt functions.firestore , który umożliwia tworzenie modułów obsługi powiązanych 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 i zmieni się jego wartość.
onDelete Wywoływane, gdy dokument z danymi zostanie usunięty.
onWrite Wywoływane, gdy zostanie wywołany element onCreate, onUpdate lub onDelete.

Jeśli nie masz jeszcze projektu z włączoną usługą Cloud Functions for Firebase, przeczytaj artykuł Wprowadzenie: pisanie i wdrażanie pierwszych funkcji, aby skonfigurować projekt Cloud Functions for Firebase.

Pisanie funkcji wywoływanych przez Cloud Firestore

Definiowanie wyzwalacza funkcji

Aby zdefiniować aktywator Cloud Firestore, określ ś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 wzorca z symbolami wieloznacznymi.

Określanie pojedynczego dokumentu

Jeśli chcesz wywołać zdarzenie przy każdej zmianie w określonym dokumencie, możesz użyć tej 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ć wyzwalacz do grupy dokumentów, np. do dowolnego dokumentu w określonej kolekcji, użyj znaku {wildcard} zamiast identyfikatora 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 jakiekolwiek pole w dowolnym dokumencie w users zostanie zmienione, będzie pasować do symbolu wieloznacznego o nazwie userId.

Jeśli dokument w users ma podzbiory, a pole w jednym z dokumentów podzbioru zostanie zmienione, symbol wieloznaczny userId nie zostanie wywołany.

Wzorce wieloznaczne są wyodrębniane ze ścieżki dokumentu i przechowywane w context.params. Możesz zdefiniować dowolną liczbę symboli wieloznacznych, aby zastąpić nimi np. identyfikatory kolekcji lub dokumentów:

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

Aktywatory zdarzeń

Wywoływanie funkcji po utworzeniu nowego dokumentu

Możesz wywołać funkcję za każdym razem, gdy w kolekcji zostanie utworzony nowy dokument, używając modułu obsługi onCreate()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 ...
    });

Uruchamianie funkcji po zaktualizowaniu dokumentu

Możesz też uruchomić funkcję, gdy dokument zostanie zaktualizowany, używając funkcji onUpdate()wieloznacznym symbolem. Ta przykładowa funkcja wywołuje updateUser, jeśli użytkownik zmieni 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 ...
    });

Uruchamianie funkcji po usunięciu dokumentu

Możesz też uruchomić funkcję, gdy dokument zostanie usunięty, używając funkcji onDelete()symbolem wieloznacznym. W tym przykładzie funkcja wywołuje deleteUser, gdy użytkownik usuwa 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 w przypadku wszystkich zmian w dokumencie

Jeśli nie zależy Ci na typie uruchamianego zdarzenia, możesz nasłuchiwać wszystkich zmian w dokumencie Cloud Firestore za pomocą funkcji onWrite() z symbolem wieloznacznym. Ta przykładowa funkcja wywołuje modifyUser, gdy 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

Gdy funkcja zostanie wywołana, udostępni migawkę danych związanych z wydarzeniem. Możesz użyć tego migawki, aby odczytać lub zapisać dokument, który wywołał zdarzenie, albo użyć pakietu Firebase Admin SDK, aby uzyskać dostęp do innych części bazy danych.

Dane zdarzenia

Odczytywanie danych

Gdy funkcja jest wywoływana, możesz chcieć pobrać dane z dokumentu, który został zaktualizowany, lub pobrać dane sprzed aktualizacji. Poprzednie dane możesz uzyskać za pomocą funkcji change.before.data(), która zawiera zrzut dokumentu sprzed aktualizacji. Podobnie change.after.data() zawiera stan zrzutu dokumentu po aktualizacji.

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

Dostęp do właściwości jest taki sam jak w przypadku innych obiektów. 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 określonym dokumentem w bazie danych Cloud Firestore. Dostęp do tego dokumentu możesz uzyskać jako DocumentReference we właściwości ref przeglądu dnia zwróconego do funkcji.

Ten obiekt DocumentReference pochodzi z pakietu SDK Cloud Firestore Node.js i zawiera metody takie jak update(), set() i remove(), dzięki czemu możesz łatwo modyfikować dokument, który wywołał 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 spoza zdarzenia wywołującego

Cloud Functions wykonywać w zaufanym środowisku, co oznacza, że są autoryzowane jako konto usługi w Twoim projekcie. Możesz wykonywać odczyty i zapisy za pomocą pakietu 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({ ... });
  });

Ograniczenia

Pamiętaj o tych ograniczeniach dotyczących aktywatorów Cloud Firestore w Cloud Functions:

  • Cloud Functions (1 generacji) wymaga istniejącej bazy danych „(default)” w trybie natywnym Firestore. Nie obsługuje Cloud Firestore nazwanych baz danych ani trybu Datastore. W takich przypadkach do konfigurowania zdarzeń używaj urządzenia Cloud Functions (2 generacji).
  • Kolejność nie jest gwarantowana. Szybkie zmiany mogą powodować wywoływanie funkcji w nieoczekiwanej kolejności.
  • Zdarzenia są dostarczane co najmniej raz, ale jedno zdarzenie może spowodować wielokrotne wywołanie funkcji. Unikaj polegania na mechanizmach przetwarzania dokładnie raz i pisz funkcje idempotentne.
  • Cloud Firestore w trybie Datastore wymaga Cloud Functions (2 generacji). Usługa Cloud Functions (1 generacji) nie obsługuje trybu Datastore.
  • Aktywator jest powiązany z 1 bazą danych. Nie możesz utworzyć aktywatora, który pasuje do wielu baz danych.
  • Usunięcie bazy danych nie powoduje automatycznego usunięcia żadnych wyzwalaczy tej bazy. Wywoływacz przestaje dostarczać zdarzenia, ale nadal istnieje, dopóki go nie usuniesz.
  • Jeśli pasujące zdarzenie przekracza maksymalny rozmiar żądania, może nie zostać dostarczone do Cloud Functions (1 generacji).
    • Zdarzenia, które nie zostały dostarczone z powodu rozmiaru żądania, są rejestrowane w logach platformy i wliczane 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 error poziomie ważności. Nazwę funkcji znajdziesz w polu functionName. Jeśli pole receiveTimestamp nadal mieści się w zakresie godziny od teraz, możesz wywnioskować rzeczywistą treść wydarzenia, czytając dany dokument z migawką przed i po sygnaturze czasowej.
    • Aby uniknąć takiej częstotliwości:
      • Migracja i uaktualnienie do Cloud Functions (2 generacji)
      • Zmniejszanie rozmiaru dokumentu
      • Usuń Cloud Functions, o które chodzi.
    • Możesz wyłączyć logowanie za pomocą wykluczeń, ale pamiętaj, że problematyczne zdarzenia nadal nie będą dostarczane.