Google Cloud Firestore-Trigger (1. Generation)
Cloud Run Functions kann Ereignisse in Firestore im selben Google Cloud-Projekt wie die Funktion verarbeiten. Sie haben dabei die Möglichkeit, Firestore als Antwort auf diese Ereignisse mithilfe der Firestore APIs und Clientbibliotheken zu lesen und/oder zu aktualisieren.
Der typische Lebenszyklus einer Cloud Firestore-Funktion sieht so aus:
Sie wartet auf Änderungen an einem bestimmten Dokument.
Sie wird ausgelöst, wenn ein Ereignis eintritt, und führt dessen Aufgaben aus.
Sie empfängt ein Datenobjekt mit einem Snapshot des betreffenden Dokuments. Für
write
- oderupdate
-Ereignisse enthält das Datenobjekt Snapshots, die den Dokumentstatus vor und nach dem auslösenden Ereignis darstellen.
Ereignistypen
Firestore unterstützt Ereignisse vom Typ create
, update
, delete
und write
. Das write
-Ereignis umfasst alle Änderungen eines Dokuments.
Ereignistyp | Trigger |
---|---|
providers/cloud.firestore/eventTypes/document.create (Standard) |
Wird ausgelöst, wenn ein Dokument zum ersten Mal beschrieben wird. |
providers/cloud.firestore/eventTypes/document.update |
Wird ausgelöst, wenn ein Dokument bereits existiert und sich ein Wert geändert hat. |
providers/cloud.firestore/eventTypes/document.delete |
Wird ausgelöst, wenn ein Dokument mit Daten gelöscht wird. |
providers/cloud.firestore/eventTypes/document.write |
Wird ausgelöst, wenn ein Dokument erstellt, aktualisiert oder gelöscht wird. |
Platzhalter werden in Triggern in geschweiften Klammern dargestellt: "projects/YOUR_PROJECT_ID/databases/(default)/documents/collection/{document_wildcard}"
.
Dokumentpfad angeben
Wenn Sie eine Funktion auslösen möchten, müssen Sie den Dokumentpfad angeben, der überwacht werden soll. Funktionen reagieren nur auf Dokumentänderungen und können keine einzelnen Felder oder Sammlungen überwachen. Hier sehen Sie ein paar Beispiele gültiger Dokumentpfade:
users/marie
: Gültiger Trigger. Überwacht ein einzelnes Dokument,/users/marie
.users/{username}
: Gültiger Trigger. Überwacht alle Nutzerdokumente. Bei Angabe von Platzhaltern werden alle Dokumente in der Sammlung überwacht.users/{username}/addresses
: Ungültiger Trigger. Bezieht sich auf die untergeordnete Sammlungaddresses
und nicht auf ein Dokument.users/{username}/addresses/home
: Gültiger Trigger. Überwacht das Privatadressdokument für alle Nutzer.users/{username}/addresses/{addressId}
: Gültiger Trigger. Überwacht alle Adressdokumente.
Platzhalter und Parameter verwenden
Wenn Sie das Dokument, das überwacht werden soll, nicht kennen, verwenden Sie {wildcard}
anstelle der Dokument-ID:
users/{username}
wartet auf Änderungen für alle Nutzerdokumente.
Wenn in diesem Beispiel ein Feld in einem Dokument im Verzeichnis users
geändert wird, entspricht es einem Platzhalter namens {username}
.
Wenn ein Dokument in users
untergeordnete Sammlungen enthält und ein Feld in einem Dokument dieser Sammlungen geändert wird, wird der Platzhalter {username}
nicht ausgelöst.
Platzhalterübereinstimmungen werden aus Dokumentpfaden extrahiert. Sie können beliebig viele Platzhalter für explizite Sammlungs- oder Dokument-IDs festzulegen.
Ereignisstruktur
Dieser Trigger löst die Funktion mit einem Ereignis wie etwa dem folgenden aus:
{ "oldValue": { // Update and Delete operations only A Document object containing a pre-operation document snapshot }, "updateMask": { // Update operations only A DocumentMask object that lists changed fields. }, "value": { // A Document object containing a post-operation document snapshot } }
Jedes Document
-Objekt enthält ein oder mehrere Value
-Objekte. Informationen zu Typreferenzen finden Sie in der Dokumentation zu Value
. Diese sind besonders hilfreich, wenn Sie eine Programmiersprache wie Go zum Schreiben Ihrer Funktionen verwenden.
Codebeispiel
Diese beispielhafte Cloud Functions-Funktion gibt die Felder eines auslösenden Cloud Firestore-Ereignisses aus:
Node.js
Python
Go
Java
C#
using CloudNative.CloudEvents; using Google.Cloud.Functions.Framework; using Google.Events.Protobuf.Cloud.Firestore.V1; using Microsoft.Extensions.Logging; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace FirebaseFirestore; public class Function : ICloudEventFunction<DocumentEventData> { private readonly ILogger _logger; public Function(ILogger<Function> logger) => _logger = logger; public Task HandleAsync(CloudEvent cloudEvent, DocumentEventData data, CancellationToken cancellationToken) { _logger.LogInformation("Function triggered by event on {subject}", cloudEvent.Subject); _logger.LogInformation("Event type: {type}", cloudEvent.Type); MaybeLogDocument("Old value", data.OldValue); MaybeLogDocument("New value", data.Value); // In this example, we don't need to perform any asynchronous operations, so the // method doesn't need to be declared async. return Task.CompletedTask; } /// <summary> /// Logs the names and values of the fields in a document in a very simplistic way. /// </summary> private void MaybeLogDocument(string message, Document document) { if (document is null) { return; } // ConvertFields converts the Firestore representation into a .NET-friendly // representation. IReadOnlyDictionary<string, object> fields = document.ConvertFields(); var fieldNamesAndTypes = fields .OrderBy(pair => pair.Key) .Select(pair => $"{pair.Key}: {pair.Value}"); _logger.LogInformation(message + ": {fields}", string.Join(", ", fieldNamesAndTypes)); } }
Ruby
PHP
Im folgenden Beispiel wird der vom Nutzer hinzugefügte Wert abgerufen, der String an dieser Stelle in Großbuchstaben umgewandelt und der Wert durch den String in Großbuchstaben ersetzt:
Node.js
Python
Go
Java
C#
using CloudNative.CloudEvents; using Google.Cloud.Firestore; using Google.Cloud.Functions.Framework; using Google.Cloud.Functions.Hosting; using Google.Events.Protobuf.Cloud.Firestore.V1; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace FirestoreReactive; public class Startup : FunctionsStartup { public override void ConfigureServices(WebHostBuilderContext context, IServiceCollection services) => services.AddSingleton(FirestoreDb.Create()); } // Register the startup class to provide the Firestore dependency. [FunctionsStartup(typeof(Startup))] public class Function : ICloudEventFunction<DocumentEventData> { private readonly ILogger _logger; private readonly FirestoreDb _firestoreDb; public Function(ILogger<Function> logger, FirestoreDb firestoreDb) => (_logger, _firestoreDb) = (logger, firestoreDb); public async Task HandleAsync(CloudEvent cloudEvent, DocumentEventData data, CancellationToken cancellationToken) { // Get the recently-written value. This expression will result in a null value // if any of the following is true: // - The event doesn't contain a "new" document // - The value doesn't contain a field called "original" // - The "original" field isn't a string string currentValue = data.Value?.ConvertFields().GetValueOrDefault("original") as string; if (currentValue is null) { _logger.LogWarning($"Event did not contain a suitable document"); return; } string newValue = currentValue.ToUpperInvariant(); if (newValue == currentValue) { _logger.LogInformation("Value is already upper-cased; no replacement necessary"); return; } // The CloudEvent subject is "documents/x/y/...". // The Firestore SDK FirestoreDb.Document method expects a reference relative to // "documents" (so just the "x/y/..." part). This may be simplified over time. if (cloudEvent.Subject is null || !cloudEvent.Subject.StartsWith("documents/")) { _logger.LogWarning("CloudEvent subject is not a document reference."); return; } string documentPath = cloudEvent.Subject.Substring("documents/".Length); _logger.LogInformation("Replacing '{current}' with '{new}' in '{path}'", currentValue, newValue, documentPath); await _firestoreDb.Document(documentPath).UpdateAsync("original", newValue, cancellationToken: cancellationToken); } }
Ruby
PHP
Funktion bereitstellen
Mit dem folgenden gcloud
-Befehl wird eine Funktion bereitgestellt, die durch Schreibereignisse im Dokument /messages/{pushId}
ausgelöst wird:
gcloud functions deploy FUNCTION_NAME \ --no-gen2 \ --entry-point ENTRY_POINT \ --runtime RUNTIME \ --set-env-vars GOOGLE_CLOUD_PROJECT=YOUR_PROJECT_ID \ --trigger-event "providers/cloud.firestore/eventTypes/document.write" \ --trigger-resource "projects/YOUR_PROJECT_ID/databases/(default)/documents/messages/{pushId}"
Argument | Beschreibung |
---|---|
FUNCTION_NAME |
Der registrierte Name der Cloud Functions-Funktion, die Sie bereitstellen.
Dies kann entweder der Name einer Funktion in Ihrem Quellcode oder ein beliebiger String sein. Wenn FUNCTION_NAME ein beliebiger String ist, müssen Sie das Flag --entry-point einfügen.
|
--entry-point ENTRY_POINT |
Der Name einer Funktion oder Klasse in Ihrem Quellcode. Optional, es sei denn, Sie haben FUNCTION_NAME nicht verwendet, um die Funktion in Ihrem Quellcode anzugeben, die während der Bereitstellung ausgeführt werden soll. In diesem Fall müssen Sie mit --entry-point den Namen der ausführbaren Funktion angeben.
|
--runtime RUNTIME |
Der Name der Laufzeit, die Sie verwenden. Eine vollständige Liste finden Sie in der gcloud -Referenz.
|
--set-env-vars GOOGLE_CLOUD_PROJECT=YOUR_PROJECT_ID |
Die eindeutige Kennung des Projekts als Umgebungsvariable der Laufzeit. |
--trigger-event NAME |
Der Ereignistyp, den die Funktion überwacht, entweder write , create , update oder delete . |
--trigger-resource NAME |
Der voll qualifizierte Datenbankpfad, den die Funktion überwacht.
Dieser muss im Format "projects/YOUR_PROJECT_ID/databases/(default)/documents/PATH" angegeben werden. Der Text {pushId} ist ein Platzhalterparameter, der oben unter Dokumentpfad festlegen beschrieben wird.
|
Beschränkungen
Beachten Sie die folgenden Einschränkungen für Firestore-Trigger für Cloud Run Functions:
- Cloud Run Functions (1. Generation) erfordert eine vorhandene „(default)“-Datenbank im nativen Firestore-Modus. Es unterstützt keine benannten Firestore-Datenbanken und keinen Datastore-Modus. Verwenden Sie in solchen Fällen Cloud Run Functions (2. Generation), um Ereignisse zu konfigurieren.
- Die Reihenfolge ist nicht garantiert. Schnelle Änderungen können Funktionsaufrufe in einer unvorhergesehenen Reihenfolge auslösen.
- Ereignisse werden mindestens einmal übergeben. Ein einzelnes Ereignis kann aber zu mehreren Funktionsaufrufen führen. Vermeiden Sie die Abhängigkeit von genau einmal vorkommenden Verfahren und schreiben Sie idempotente Funktionen.
- Firestore im Datastore-Modus erfordert Cloud Run Functions (2. Generation). Cloud Run Functions (1. Generation) unterstützt den Datastore-Modus nicht.
- Ein Trigger ist mit einer einzelnen Datenbank verknüpft. Sie können keinen Trigger erstellen, der mit mehreren Datenbanken übereinstimmt.
- Wenn Sie eine Datenbank löschen, werden nicht automatisch die Trigger für diese Datenbank gelöscht. Der Trigger sendet keine Ereignisse mehr, bleibt aber bestehen, bis Sie ihn löschen.
- Wenn ein übereinstimmendes Ereignis die maximale Anfragegröße überschreitet, wird es möglicherweise nicht an Cloud Run Functions der 1. Generation gesendet.
- Ereignisse, die aufgrund der Anfragegröße nicht gesendet wurden, werden in Plattform-Logs protokolliert und auf die Lognutzung für das Projekt angerechnet.
- Sie finden diese Logs im Log-Explorer mit der Meldung "Ereignis kann nicht an die Cloud Functions-Funktion gesendet werden, da die Größe das Limit für die 1. Generation überschreitet..." mit dem Schweregrad
error
. Den Funktionsnamen finden Sie im FeldfunctionName
. Wenn das FeldreceiveTimestamp
noch bis zu einer Stunde in der Zukunft liegt, können Sie den tatsächlichen Ereignisinhalt ableiten, indem Sie das betreffende Dokument mit einem Snapshot vor und nach dem Zeitstempel lesen. - So können Sie das vermeiden:
- Migrieren und Upgrade auf Cloud Run Functions (2. Generation)
- Dokument verkleinern
- Die betreffenden Cloud Run Functions-Funktionen löschen
- Sie können die Protokollierung selbst mit Ausschlüssen deaktivieren. Die betreffenden Ereignisse werden jedoch weiterhin nicht gesendet.