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 migawek dokumentu lub za pomocą pakietu Admin SDK.

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

  1. Czeka na zmiany w danym dokumencie.
  2. Aktywuje się, gdy nastą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 reprezentują stan danych przed i po wywołaniu zdarzenia.

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

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 Wyzwalane, gdy dokument już istnieje i jego jakakolwiek wartość ulegnie 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łączonego projektu Cloud Functions for Firebase, przeczytaj artykuł Pierwsze kroki: 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ć regułę 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 do wzorca z symbolem wieloznacznym.

Określ pojedynczy dokument

Jeśli chcesz wywołać zdarzenie dla dowolnej zmiany w konkretnym 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 zastosować regułę do grupy dokumentów, np. do dowolnego dokumentu w konkretnej kolekcji, zamiast identyfikatora dokumentu użyj {wildcard}:

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 users, zostanie ono dopasowane do znaku zapytania o nazwie userId.

Jeśli dokument w users ma podkolekcje, a pole w jednym z tych dokumentów zostało zmienione, symbol wieloznaczny userId nie nie zostanie uruchomiony.

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ć jawne identyfikatory zbiorów lub 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"}
    });

Aktywatory zdarzeń

Wywoływanie funkcji po utworzeniu nowego dokumentu

Funkcję możesz wywołać w każdej chwili, gdy w zbiorze zostanie utworzony nowy dokument, używając onCreate()symbolem wieloznacznym. Ta przykładowa funkcja wywołuje funkcję 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() z symbolem wieloznacznym. Ta przykładowa funkcja wywołuje funkcję updateUser, jeśli użytkownik zmienił 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 ...
    });

Wywoływanie funkcji po usunięciu dokumentu

Możesz też uruchomić funkcję po usunięciu dokumentu, używając funkcji onDelete() z symbolem wieloznacznym. W tym przykładzie funkcja deleteUser jest wywoływana, 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 przy wszystkich zmianach w dokumencie

Jeśli nie interesuje Cię typ wywoływanego zdarzenia, możesz nasłuchiwać wszystkich zmian w dokumencie Cloud Firestore za pomocą funkcji onWrite()symbolem wieloznacznym. 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

Gdy funkcja zostanie wywołana, udostępnia migawkę danych powiązanych z wydarzeniem. Możesz użyć tego migawka do odczytu lub zapisu dokumentu, który wywołał zdarzenie, albo użyć pakietu Admin SDK Firebase, aby uzyskać dostęp do innych części bazy danych.

Dane zdarzenia

Czytanie danych

Gdy funkcja zostanie wywołana, możesz pobrać dane z zaktualizowanego dokumentu lub dane sprzed aktualizacji. Poprzednie dane możesz uzyskać za pomocą parametru change.before.data(), który 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 możesz uzyskać 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 bazie danych Cloud Firestore. Dostęp do tego dokumentu uzyskasz jako DocumentReference w ramach właściwości ref przechwytu zwróconego do funkcji.

Ten obiekt DocumentReference pochodzi z pakietu SDK Cloud Firestore dla Node.js i zawiera metody takie jak update(), set()remove(), dzięki którym 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 uruchamiającego

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

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

  • Cloud Functions (pierwsza generacja) wymaga istniejącej bazy danych „(domyślna)” w trybie natywnym Firestore. Nie obsługuje ona Cloud Firestore nazwanych baz danych ani trybu Datastore. W takich przypadkach użyj Cloud Functions (2 generacji), aby skonfigurować zdarzenia.
  • Zamówienie nie jest gwarantowane. Nagłe zmiany mogą powodować wywołania funkcji w nieoczekiwanej kolejności.
  • Zdarzenia są wywoływane co najmniej raz, ale pojedyncze zdarzenie może spowodować wielokrotne wywołanie funkcji. Unikaj zależności od mechanizmu „dokładnie raz” i pisz funkcje idempotentne.
  • Cloud Firestore w trybie Datastore wymaga Cloud Functions (2 generacji). Interfejs Cloud Functions (1 generacji) nie obsługuje trybu Datastore.
  • Aktywator jest powiązany z jedną bazą danych. Nie możesz utworzyć aktywatora, który pasuje do wielu baz danych.
  • Usunięcie bazy danych nie powoduje automatycznego usunięcia żadnych jej reguł. Wyzwalacz przestaje dostarczać zdarzenia, ale nadal istnieje, dopóki go nie usuniesz.
  • Jeśli dopasowane 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 poważnym error. Nazwa funkcji znajduje się w polu functionName. Jeśli receiveTimestamp pole jest nadal w ciągu godziny od tego momentu, możesz wywnioskować rzeczywistą treść zdarzenia, czytając odpowiedni dokument ze zrzutem ekranu przed i po sygnaturze czasową.
    • Aby uniknąć takiej częstotliwości, możesz:
      • Migracja i uaktualnienie do Cloud Functions (2 generacji)
      • zmniejszyć rozmiar dokumentu,
      • Usuń Cloud Functions
    • Możesz wyłączyć samo rejestrowanie za pomocą wykluczeń, ale pamiętaj, że nieprawidłowe zdarzenia nadal nie będą przesyłane.