Dzięki Cloud Functions możesz obsługiwać zdarzenia w Cloud Firestore bez konieczności aktualizowania kodu klienta. Możesz wprowadzać zmiany w Cloud Firestore za pomocą interfejsu migawki dokumentu lub za pomocą pakietu Admin SDK.
W typowym cyklu życia funkcja Cloud Firestore wykonuje te czynności:
- Czeka na zmiany w konkretnym dokumencie.
- Uruchamia się, gdy wystąpi zdarzenie, i wykonuje swoje zadania.
- Otrzymuje obiekt danych, który zawiera migawkę 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 zdarzeniu wywołującym.
Odległość między lokalizacją instancji Firestore a lokalizacją funkcji może powodować znaczne opóźnienie sieci. Aby zoptymalizować wydajność, rozważ określenie lokalizacji funkcji, jeśli to możliwe.
Aktywatory funkcji Cloud Firestore
Pakiet Cloud Functions for Firebase SDK eksportuje te aktywatory zdarzeń Cloud Firestore , aby umożliwić Ci tworzenie procedur obsługi powiązanych z konkretnymi zdarzeniami Cloud Firestore:
Node.js
| Typ zdarzenia | Aktywator |
|---|---|
onDocumentCreated |
Wywoływane, gdy dokument jest zapisywany po raz pierwszy. |
onDocumentUpdated |
Wywoływane, gdy dokument już istnieje i zmieniła się jego wartość. |
onDocumentDeleted |
Wywoływane, gdy dokument zostanie usunięty. |
onDocumentWritten |
Wywoływane, gdy zostanie wywołane zdarzenie onDocumentCreated, onDocumentUpdated lub onDocumentDeleted. |
onDocumentCreatedWithAuthContext |
onDocumentCreated z dodatkowymi informacjami o uwierzytelnianiu |
onDocumentWrittenWithAuthContext |
onDocumentWritten z dodatkowymi informacjami o uwierzytelnianiu |
onDocumentDeletedWithAuthContext |
onDocumentDeleted z dodatkowymi informacjami o uwierzytelnianiu |
onDocumentUpdatedWithAuthContext |
onDocumentUpdated z dodatkowymi informacjami o uwierzytelnianiu |
Python
| Typ zdarzenia | Aktywator |
|---|---|
on_document_created |
Wywoływane, gdy dokument jest zapisywany po raz pierwszy. |
on_document_updated |
Wywoływane, gdy dokument już istnieje i zmieniła się jego wartość. |
on_document_deleted |
Wywoływane, gdy dokument zostanie usunięty. |
on_document_written |
Wywoływane, gdy zostanie wywołane zdarzenie on_document_created, on_document_updated lub on_document_deleted. |
on_document_created_with_auth_context |
on_document_created z dodatkowymi informacjami o uwierzytelnianiu |
on_document_updated_with_auth_context |
on_document_updated z dodatkowymi informacjami o uwierzytelnianiu |
on_document_deleted_with_auth_context |
on_document_deleted z dodatkowymi informacjami o uwierzytelnianiu |
on_document_written_with_auth_context |
on_document_written z dodatkowymi informacjami o uwierzytelnianiu |
Zdarzenia Cloud Firestore są wywoływane tylko w przypadku zmian w dokumencie. Aktualizacja dokumentu Cloud Firestore, w którym dane nie uległy zmianie (zapis bez zmian), nie generuje zdarzenia aktualizacji ani zapisu. Nie można dodawać zdarzeń do konkretnych pól.
Jeśli nie masz jeszcze projektu z włączoną usługą Cloud Functions for Firebase, przeczytaj Pierwsze kroki z Cloud Functions for Firebase (2 generacji) aby skonfigurować projekt Cloud Functions for Firebase.
Pisanie funkcji wywoływanych przez Cloud Firestore
Definiowanie aktywatora funkcji
Aby zdefiniować aktywator Cloud Firestore, określ ścieżkę dokumentu i typ zdarzenia:
Node.js
const {
onDocumentWritten,
onDocumentCreated,
onDocumentUpdated,
onDocumentDeleted,
Change,
FirestoreEvent
} = require('firebase-functions/v2/firestore');
exports.myfunction = onDocumentWritten("my-collection/{docId}", (event) => {
/* ... */
});
Python
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:
Ścieżki dokumentów mogą odwoływać się do konkretnego dokumentu lub wzorca z symbolem wieloznacznym.
Określanie pojedynczego dokumentu
Jeśli chcesz wywoływać zdarzenie przy każdej zmianie w konkretnym dokumencie, możesz użyć tej funkcji.
Node.js
const {
onDocumentWritten,
Change,
FirestoreEvent
} = require('firebase-functions/v2/firestore');
exports.myfunction = onDocumentWritten("users/marie", (event) => {
// Your code here
});
Python
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:
Określanie grupy dokumentów za pomocą symboli wieloznacznych
Jeśli chcesz powiązać aktywator z grupą dokumentów, np. z dowolnym dokumentem w
określonej kolekcji, użyj {wildcard} zamiast
identyfikatora dokumentu:
Node.js
const {
onDocumentWritten,
Change,
FirestoreEvent
} = require('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
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
W tym przykładzie, gdy zmieni się dowolne pole w dowolnym dokumencie w kolekcji users, zostanie dopasowany symbol wieloznaczny o nazwie userId.
Jeśli dokument w kolekcji users ma podkolekcje i zmieni się pole w jednym z dokumentów podkolekcji, symbol wieloznaczny userId nie zostanie wywołany.
Dopasowania symboli wieloznacznych są wyodrębniane ze ścieżki dokumentu i przechowywane w event.params.
Możesz zdefiniować dowolną liczbę symboli wieloznacznych, aby zastąpić nimi jawne identyfikatory kolekcji lub dokumentów, np.:
Node.js
const {
onDocumentWritten,
Change,
FirestoreEvent
} = require('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
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"}
Aktywator musi zawsze wskazywać dokument, nawet jeśli używasz symbolu wieloznacznego.
Na przykład users/{userId}/{messageCollectionId} jest nieprawidłowy, ponieważ {messageCollectionId}
to kolekcja. Jednak users/{userId}/{messageCollectionId}/{messageId} jest
prawidłowy, ponieważ {messageId} zawsze będzie wskazywać dokument.
Aktywatory zdarzeń
Wywoływanie funkcji po utworzeniu nowego dokumentu
Możesz wywoływać funkcję za każdym razem, gdy w kolekcji zostanie utworzony nowy dokument. Ta przykładowa funkcja jest wywoływana za każdym razem, gdy dodawany jest nowy profil użytkownika:
Node.js
const {
onDocumentCreated,
Change,
FirestoreEvent
} = require('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 ...
});
Aby uzyskać dodatkowe informacje o uwierzytelnianiu, użyj onDocumentCreatedWithAuthContext.
Python
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 ...
Wywoływanie funkcji po zaktualizowaniu dokumentu
Możesz też wywoływać funkcję po zaktualizowaniu dokumentu. Ta przykładowa funkcja jest wywoływana, gdy użytkownik zmieni swój profil:
Node.js
const {
onDocumentUpdated,
Change,
FirestoreEvent
} = require('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 ...
});
Aby uzyskać dodatkowe informacje o uwierzytelnianiu, użyj onDocumentUpdatedWithAuthContext.
Python
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 ...
Wywoływanie funkcji po usunięciu dokumentu
Możesz też wywoływać funkcję po usunięciu dokumentu. Ta przykładowa funkcja jest wywoływana, gdy użytkownik usunie swój profil:
Node.js
const {
onDocumentDeleted,
Change,
FirestoreEvent
} = require('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 ...
});
Aby uzyskać dodatkowe informacje o uwierzytelnianiu, użyj onDocumentDeletedWithAuthContext.
Python
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 ...
Wywoływanie 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ą aktywatora zdarzenia „zapis dokumentu”. Ta przykładowa funkcja jest wywoływana, gdy użytkownik zostanie utworzony, zaktualizowany lub usunięty:
Node.js
const {
onDocumentWritten,
Change,
FirestoreEvent
} = require('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 ...
});
Aby uzyskać dodatkowe informacje o uwierzytelnianiu, użyj onDocumentWrittenWithAuthContext.
Python
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 ...
Odczytywanie i zapisywanie danych
Gdy funkcja zostanie wywołana, udostępnia migawkę danych związanych ze zdarzeniem. Możesz użyć tej migawki do odczytu lub zapisu w dokumencie, 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 zostanie wywołana, możesz chcieć pobrać dane z zaktualizowanego dokumentu lub dane sprzed aktualizacji. Poprzednie dane możesz uzyskać za pomocą event.data.before, które zawiera zrzut dokumentu sprzed aktualizacji.
Podobnie event.data.after zawiera stan migawki dokumentu po aktualizacji.
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
@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()
Do właściwości możesz uzyskiwać dostęp tak jak w przypadku każdego innego obiektu. Możesz też użyć funkcji get, aby uzyskać dostęp do konkretnych pól:
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
# 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"]
Zapisywanie danych
Każde wywołanie funkcji jest powiązane z konkretnym dokumentem w bazie danych Cloud Firestore. Możesz uzyskać dostęp do tego dokumentu w migawce zwróconej do funkcji.
Odwołanie do dokumentu zawiera metody takie jak update(), set() i remove(), dzięki czemu możesz modyfikować dokument, który wywołał funkcję.
Node.js
const {onDocumentUpdated} = require('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 event.data.after.ref.set({
name_change_count: count + 1
}, {merge: true});
});
Python
@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})
Uzyskiwanie dostępu do informacji o uwierzytelnianiu użytkowników
Jeśli używasz jednego z tych typów zdarzeń, możesz uzyskać dostęp do informacji o uwierzytelnianiu użytkownika, który wywołał zdarzenie. Te informacje są dodatkiem do informacji zwracanych w zdarzeniu podstawowym.
Node.js
onDocumentCreatedWithAuthContextonDocumentWrittenWithAuthContextonDocumentDeletedWithAuthContextonDocumentUpdatedWithAuthContext
Python
on_document_created_with_auth_contexton_document_updated_with_auth_contexton_document_deleted_with_auth_contexton_document_written_with_auth_context
Informacje o danych dostępnych w kontekście uwierzytelniania znajdziesz w artykule Kontekst uwierzytelniania. Ten przykład pokazuje, jak pobrać informacje o uwierzytelnianiu:
Node.js
const {onDocumentWrittenWithAuthContext} = require('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
@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
Dane spoza zdarzenia wywołującego
Cloud Functions działają w zaufanym środowisku. Są autoryzowane jako konto usługi w Twoim projekcie i możesz wykonywać odczyty i zapisy za pomocą pakietu Firebase Admin SDK:
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
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({
# ...
})
Ograniczenia
Pamiętaj o tych ograniczeniach dotyczących Cloud Firestore aktywatorów dla 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 skonfiguruj zdarzenia za pomocą Cloud Functions (2 generacji).
- Konfiguracja międzyprojektowa z aktywatorem Cloud Functions i Cloud Firestore jest ograniczona. Aby skonfigurować aktywator Cloud Firestore, Cloud Functions muszą znajdować się w tym samym projekcie.
- Kolejność nie jest gwarantowana. Szybkie zmiany mogą wywoływać funkcje w nieoczekiwanej kolejności.
- Zdarzenia są dostarczane co najmniej raz, ale pojedyncze zdarzenie może spowodować wiele wywołań funkcji. Nie polegaj na mechanizmach „dokładnie raz” i pisz funkcje idempotentne.
- Cloud Firestore w trybie Datastore wymaga Cloud Functions (2 generacji). 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 aktywatorów tej bazy danych. Aktywator przestaje dostarczać zdarzenia, ale nadal istnieje, dopóki go nie usuniesz.
- Jeśli dopasowane zdarzenie przekracza maksymalny rozmiar żądania, zdarzenie 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 1 generacji..." o poziomie ważności
error. Nazwę funkcji znajdziesz w polufunctionName. Jeśli polereceiveTimestampnadal mieści się w ciągu godziny od teraz, możesz wywnioskować rzeczywistą treść zdarzenia, odczytując dany dokument z migawką przed i po sygnaturze czasowej. - Aby uniknąć takiej kadencji, możesz:
- przeprowadzić migrację i uaktualnić do Cloud Functions (2 generacji);
- zmniejszyć rozmiar dokumentu;
- usunąć odpowiednie Cloud Functions.
- Możesz wyłączyć samo logowanie za pomocą wykluczeń ale pamiętaj, że problematyczne zdarzenia nadal nie będą dostarczane.