Firestore-Trigger

Sie können Ihre Cloud Run-Funktionen so konfigurieren, dass sie durch Ereignisse in einer Firestore-Datenbank ausgelöst werden. Nach der Auslösung kann Ihre Funktion Als Reaktion auf diese Ereignisse eine Firestore-Datenbank lesen und aktualisieren und zwar durch die Firestore APIs und client-Bibliotheken.

Der typische Lebenszyklus einer Cloud Firestore-Funktion sieht so aus:

  1. Sie wartet auf Änderungen an einem bestimmten Dokument.

  2. Sie wird ausgelöst, wenn ein Ereignis eintritt, und führt dessen Aufgaben aus.

  3. Sie empfängt ein Datenobjekt mit einem Snapshot des betreffenden Dokuments. Für write- oder update-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
google.cloud.firestore.document.v1.created (Standard) Wird ausgelöst, wenn ein Dokument zum ersten Mal beschrieben wird.
google.cloud.firestore.document.v1.updated Wird ausgelöst, wenn ein Dokument bereits existiert und sich ein Wert geändert hat.
google.cloud.firestore.document.v1.deleted Wird ausgelöst, wenn ein Dokument mit Daten gelöscht wird.
google.cloud.firestore.document.v1.written 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. Der Dokumentpfad muss sich im selben Google Cloud-Projekt wie die Funktion befinden.

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 Sammlung addresses 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.

  • users/{user=**}: Gültiger Trigger. Überwacht alle Nutzerdokumente und alle Dokumente in untergeordneten Sammlungen unter jedem Nutzerdokument, z. B. /users/userID/address/home oder /users/userID/phone/work.

Platzhalter und Parameter

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. Wenn Sie auf Ereignisse in Untersammlungen reagieren möchten, verwenden Sie den Multi-Segment-Platzhalter {username=**}.

Platzhalterübereinstimmungen werden aus Dokumentpfaden extrahiert. Sie können beliebig viele Platzhalter für explizite Sammlungs- oder Dokument-IDs festlegen. Sie können bis zu einen Platzhalter mit mehreren Segmenten wie {username=**} verwenden.

Ereignisstrukturen

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.

Beispiele

In den folgenden Beispielen wird gezeigt, wie Sie Funktionen schreiben, die auf einen Firestore-Trigger reagieren.

Hinweise

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. Enable the Cloud Functions, Cloud Build, Artifact Registry, Eventarc, Logging, Pub/Sub, and Cloud Run APIs.

    Enable the APIs

  5. Install the Google Cloud CLI.
  6. To initialize the gcloud CLI, run the following command:

    gcloud init
  7. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  8. Make sure that billing is enabled for your Google Cloud project.

  9. Enable the Cloud Functions, Cloud Build, Artifact Registry, Eventarc, Logging, Pub/Sub, and Cloud Run APIs.

    Enable the APIs

  10. Install the Google Cloud CLI.
  11. To initialize the gcloud CLI, run the following command:

    gcloud init
  12. Wenn Sie die gcloud CLI bereits installiert haben, aktualisieren Sie sie mit dem folgenden Befehl:

    gcloud components update
  13. Bereiten Sie die Entwicklungsumgebung vor.

Firestore-Datenbank einrichten

Sie benötigen eine Cloud Firestore-Datenbank, um die Beispiele in diesem Dokument zu testen. Sie muss vorhanden sein, bevor Sie die Funktionen bereitstellen. Wenn Sie noch keine Firestore-Datenbank haben, erstellen Sie so eine:

  1. Zur Firestore-Seite "Daten"

  2. Klicken Sie auf Nativen Modus auswählen.

  3. Wählen Sie die Region (Standort) aus, in der sich die Datenbank befinden soll. Diese Entscheidung ist endgültig.

  4. Klicken Sie auf Create database (Datenbank erstellen).

Das Firestore-Datenmodell besteht aus Sammlungen, die Dokumente enthalten. Jedes Dokument enthält eine Reihe von Schlüssel/Wert-Paaren.

Die in dieser Anleitung erstellten Funktionen werden ausgelöst, wenn Sie Änderungen an einem Dokument innerhalb einer angegebenen Sammlung vornehmen.

Beispiel 1: Hello Firestore-Funktion

Die folgende beispielhafte Cloud Run-Funktion gibt die Felder eines auslösenden Cloud Firestore-Ereignisses aus:

Node.js

Decodieren Sie die Ereignisdaten mit protobufjs. Fügen Sie google.events.cloud.firestore.v1 data.proto in Ihre Quelle ein.

/**
 * Cloud Event Function triggered by a change to a Firestore document.
 */
const functions = require('@google-cloud/functions-framework');
const protobuf = require('protobufjs');

functions.cloudEvent('helloFirestore', async cloudEvent => {
  console.log(`Function triggered by event on: ${cloudEvent.source}`);
  console.log(`Event type: ${cloudEvent.type}`);

  console.log('Loading protos...');
  const root = await protobuf.load('data.proto');
  const DocumentEventData = root.lookupType(
    'google.events.cloud.firestore.v1.DocumentEventData'
  );

  console.log('Decoding data...');
  const firestoreReceived = DocumentEventData.decode(cloudEvent.data);

  console.log('\nOld value:');
  console.log(JSON.stringify(firestoreReceived.oldValue, null, 2));

  console.log('\nNew value:');
  console.log(JSON.stringify(firestoreReceived.value, null, 2));
});

Python

from cloudevents.http import CloudEvent
import functions_framework
from google.events.cloud import firestore


@functions_framework.cloud_event
def hello_firestore(cloud_event: CloudEvent) -> None:
    """Triggers by a change to a Firestore document.

    Args:
        cloud_event: cloud event with information on the firestore event trigger
    """
    firestore_payload = firestore.DocumentEventData()
    firestore_payload._pb.ParseFromString(cloud_event.data)

    print(f"Function triggered by change to: {cloud_event['source']}")

    print("\nOld value:")
    print(firestore_payload.old_value)

    print("\nNew value:")
    print(firestore_payload.value)

Go


// Package hellofirestore contains a Cloud Event Function triggered by a Cloud Firestore event.
package hellofirestore

import (
	"context"
	"fmt"

	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
	"github.com/cloudevents/sdk-go/v2/event"
	"github.com/googleapis/google-cloudevents-go/cloud/firestoredata"
	"google.golang.org/protobuf/proto"
)

func init() {
	functions.CloudEvent("helloFirestore", HelloFirestore)
}

// HelloFirestore is triggered by a change to a Firestore document.
func HelloFirestore(ctx context.Context, event event.Event) error {
	var data firestoredata.DocumentEventData
	if err := proto.Unmarshal(event.Data(), &data); err != nil {
		return fmt.Errorf("proto.Unmarshal: %w", err)
	}

	fmt.Printf("Function triggered by change to: %v\n", event.Source())
	fmt.Printf("Old value: %+v\n", data.GetOldValue())
	fmt.Printf("New value: %+v\n", data.GetValue())
	return nil
}

Java

import com.google.cloud.functions.CloudEventsFunction;
import com.google.events.cloud.firestore.v1.DocumentEventData;
import com.google.protobuf.InvalidProtocolBufferException;
import io.cloudevents.CloudEvent;
import java.util.logging.Logger;

public class FirebaseFirestore implements CloudEventsFunction {
  private static final Logger logger = Logger.getLogger(FirebaseFirestore.class.getName());

  @Override
  public void accept(CloudEvent event) throws InvalidProtocolBufferException {
    DocumentEventData firestorEventData = DocumentEventData.parseFrom(event.getData().toBytes());

    logger.info("Function triggered by event on: " + event.getSource());
    logger.info("Event type: " + event.getType());

    logger.info("Old value:");
    logger.info(firestorEventData.getOldValue().toString());

    logger.info("New value:");
    logger.info(firestorEventData.getValue().toString());
  }
}

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

Funktion "Hello Firestore" bereitstellen

  1. Richten Sie Ihre Firestore-Datenbank ein, falls noch nicht geschehen.

  2. Führen Sie den folgenden Befehl in dem Verzeichnis aus, das den Beispielcode (oder im Fall von Java die Datei pom.xml) enthält, um die Hello Firestore-Funktion mit einem Firestore-Trigger bereitzustellen:

    gcloud functions deploy FUNCTION_NAME \
    --gen2 \
    --runtime=RUNTIME \
    --region=REGION \
    --trigger-location=TRIGGER REGION \
    --source=. \
    --entry-point=ENTRY_POINT \
    --trigger-event-filters=type=google.cloud.firestore.document.v1.written \
    --trigger-event-filters=database='(default)' \
    --trigger-event-filters-path-pattern=document='users/{username}'

    Ersetzen Sie Folgendes:

    • FUNCTION_NAME: Ein Name für die bereitgestellte Funktion.
    • RUNTIME: Die Sprachlaufzeit, die Ihre Funktion verwendet.
    • REGION: Die Region, in der die Funktion bereitgestellt werden soll.
    • TRIGGER_REGION: Der Speicherort des Triggers, der der Region der Firestore-Datenbank entsprechen muss.
    • ENTRY_POINT: Der Einstiegspunkt zur Funktion in Ihrem Quellcode. Dies ist der Code, der beim Ausführen der Funktion ausgeführt wird.

    Die anderen Felder unverändert verwenden:

    • --trigger-event-filters=type=google.cloud.firestore.document.v1.written gibt an, dass die Funktion ausgelöst wird, wenn ein Dokument gemäß dem Ereignistyp google.cloud.firestore.document.v1.written erstellt, aktualisiert oder gelöscht wird.
    • --trigger-event-filters=database='(default)' gibt die Firebase-Datenbank an. Verwenden Sie für den Standarddatenbanknamen (default).
    • --trigger-event-filters-path-pattern=document='users/{username}' enthält das Pfadmuster der Dokumente, die auf relevante Änderungen überwacht werden sollen. Dieses Pfadmuster gibt an, dass alle Dokumente in der Sammlung users überwacht werden sollen. Weitere Informationen finden Sie unter Informationen zu Pfadmustern.

Funktion "Hello Firestore" testen

Zum Testen der Funktion Hello Firestore richten Sie eine Sammlung mit dem Namen users in Ihrer Firestore-Datenbank ein:

  1. Klicken Sie auf der Firestore-Datenseite auf Sammlung starten.

  2. Geben Sie users als Sammlungs-ID an.

  3. Um das erste Dokument der Sammlung hinzuzufügen, akzeptieren Sie unter Erstes Dokument hinzufügen die automatisch generierte Dokument-ID.

  4. Fügen Sie mindestens ein Feld für das Dokument hinzu und geben Sie einen Namen und einen Wert an. In diesem Beispiel lautet der Name „username“ und der Wert „rowan:“

    Screenshot zum Erstellen einer Firestore-Sammlung

  5. Wenn Sie fertig sind, klicken Sie auf Speichern.

    Dadurch wird ein neues Dokument erstellt und die Funktion wird ausgelöst.

  6. Klicken Sie auf der Übersichtsseite "Cloud Run Functions" in der Google Cloud Console auf den verknüpften Namen der Funktion, um Funktionsdetails zu öffnen und zu prüfen, ob die Funktion ausgelöst wurde.

  7. Öffnen Sie den Tab Protokolle und suchen Sie nach diesem String:

Function triggered by change to: //firestore.googleapis.com/projects/your-project-id/databases/(default)'

Beispiel 2: In Großbuchstaben konvertieren-Funktion

In diesem 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

Decodieren Sie die Ereignisdaten mit protobufjs. Fügen Sie google.events.cloud.firestore.v1 data.proto in Ihre Quelle ein.

const functions = require('@google-cloud/functions-framework');
const Firestore = require('@google-cloud/firestore');
const protobuf = require('protobufjs');

const firestore = new Firestore({
  projectId: process.env.GOOGLE_CLOUD_PROJECT,
});

// Converts strings added to /messages/{pushId}/original to uppercase
functions.cloudEvent('makeUpperCase', async cloudEvent => {
  console.log('Loading protos...');
  const root = await protobuf.load('data.proto');
  const DocumentEventData = root.lookupType(
    'google.events.cloud.firestore.v1.DocumentEventData'
  );

  console.log('Decoding data...');
  const firestoreReceived = DocumentEventData.decode(cloudEvent.data);

  const resource = firestoreReceived.value.name;
  const affectedDoc = firestore.doc(resource.split('/documents/')[1]);

  const curValue = firestoreReceived.value.fields.original.stringValue;
  const newValue = curValue.toUpperCase();

  if (curValue === newValue) {
    // Value is already upper-case
    // Don't perform a(nother) write to avoid infinite loops
    console.log('Value is already upper-case.');
    return;
  }

  console.log(`Replacing value: ${curValue} --> ${newValue}`);
  affectedDoc.set({
    original: newValue,
  });
});

Python

from cloudevents.http import CloudEvent
import functions_framework
from google.cloud import firestore
from google.events.cloud import firestore as firestoredata

client = firestore.Client()


# Converts strings added to /messages/{pushId}/original to uppercase
@functions_framework.cloud_event
def make_upper_case(cloud_event: CloudEvent) -> None:
    firestore_payload = firestoredata.DocumentEventData()
    firestore_payload._pb.ParseFromString(cloud_event.data)

    path_parts = firestore_payload.value.name.split("/")
    separator_idx = path_parts.index("documents")
    collection_path = path_parts[separator_idx + 1]
    document_path = "/".join(path_parts[(separator_idx + 2) :])

    print(f"Collection path: {collection_path}")
    print(f"Document path: {document_path}")

    affected_doc = client.collection(collection_path).document(document_path)

    cur_value = firestore_payload.value.fields["original"].string_value
    new_value = cur_value.upper()

    if cur_value != new_value:
        print(f"Replacing value: {cur_value} --> {new_value}")
        affected_doc.set({"original": new_value})
    else:
        # Value is already upper-case
        # Don't perform a second write (which can trigger an infinite loop)
        print("Value is already upper-case.")

Go


// Package upper contains a Firestore Cloud Function.
package upper

import (
	"context"
	"errors"
	"fmt"
	"log"
	"os"
	"strings"

	"cloud.google.com/go/firestore"
	firebase "firebase.google.com/go/v4"
	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
	"github.com/cloudevents/sdk-go/v2/event"
	"github.com/googleapis/google-cloudevents-go/cloud/firestoredata"
	"google.golang.org/protobuf/proto"
)

// set the GOOGLE_CLOUD_PROJECT environment variable when deploying.
var projectID = os.Getenv("GOOGLE_CLOUD_PROJECT")

// client is a Firestore client, reused between function invocations.
var client *firestore.Client

func init() {
	// Use the application default credentials.
	conf := &firebase.Config{ProjectID: projectID}

	// Use context.Background() because the app/client should persist across
	// invocations.
	ctx := context.Background()

	app, err := firebase.NewApp(ctx, conf)
	if err != nil {
		log.Fatalf("firebase.NewApp: %v", err)
	}

	client, err = app.Firestore(ctx)
	if err != nil {
		log.Fatalf("app.Firestore: %v", err)
	}

	// Register cloud event function
	functions.CloudEvent("MakeUpperCase", MakeUpperCase)
}

// MakeUpperCase is triggered by a change to a Firestore document. It updates
// the `original` value of the document to upper case.
func MakeUpperCase(ctx context.Context, e event.Event) error {
	var data firestoredata.DocumentEventData
	if err := proto.Unmarshal(e.Data(), &data); err != nil {
		return fmt.Errorf("proto.Unmarshal: %w", err)
	}

	if data.GetValue() == nil {
		return errors.New("Invalid message: 'Value' not present")
	}

	fullPath := strings.Split(data.GetValue().GetName(), "/documents/")[1]
	pathParts := strings.Split(fullPath, "/")
	collection := pathParts[0]
	doc := strings.Join(pathParts[1:], "/")

	var originalStringValue string
	if v, ok := data.GetValue().GetFields()["original"]; ok {
		originalStringValue = v.GetStringValue()
	} else {
		return errors.New("Document did not contain field \"original\"")
	}

	newValue := strings.ToUpper(originalStringValue)
	if originalStringValue == newValue {
		log.Printf("%q is already upper case: skipping", originalStringValue)
		return nil
	}
	log.Printf("Replacing value: %q -> %q", originalStringValue, newValue)

	newDocumentEntry := map[string]string{"original": newValue}
	_, err := client.Collection(collection).Doc(doc).Set(ctx, newDocumentEntry)
	if err != nil {
		return fmt.Errorf("Set: %w", err)
	}
	return nil
}

Java

import com.google.cloud.firestore.Firestore;
import com.google.cloud.firestore.FirestoreOptions;
import com.google.cloud.firestore.SetOptions;
import com.google.cloud.functions.CloudEventsFunction;
import com.google.events.cloud.firestore.v1.DocumentEventData;
import com.google.events.cloud.firestore.v1.Value;
import com.google.protobuf.InvalidProtocolBufferException;
import io.cloudevents.CloudEvent;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.logging.Logger;

public class FirebaseFirestoreReactive implements CloudEventsFunction {
  private static final Logger logger = Logger.getLogger(FirebaseFirestoreReactive.class.getName());
  private final Firestore firestore;

  private static final String FIELD_KEY = "original";
  private static final String APPLICATION_PROTOBUF = "application/protobuf";

  public FirebaseFirestoreReactive() {
    this(FirestoreOptions.getDefaultInstance().getService());
  }

  public FirebaseFirestoreReactive(Firestore firestore) {
    this.firestore = firestore;
  }

  @Override
  public void accept(CloudEvent event)
      throws InvalidProtocolBufferException, InterruptedException, ExecutionException {
    if (event.getData() == null) {
      logger.warning("No data found in event!");
      return;
    }

    if (!event.getDataContentType().equals(APPLICATION_PROTOBUF)) {
      logger.warning(String.format("Found unexpected content type %s, expected %s",
          event.getDataContentType(),
          APPLICATION_PROTOBUF));
      return;
    }

    DocumentEventData firestoreEventData = DocumentEventData
        .parseFrom(event.getData().toBytes());

    // Get the fields from the post-operation document snapshot
    // https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents#Document
    Map<String, Value> fields = firestoreEventData.getValue().getFieldsMap();
    if (!fields.containsKey(FIELD_KEY)) {
      logger.warning("Document does not contain original field");
      return;
    }
    String currValue = fields.get(FIELD_KEY).getStringValue();
    String newValue = currValue.toUpperCase();

    if (currValue.equals(newValue)) {
      logger.info("Value is already upper-case");
      return;
    }

    // Retrieve the document name from the resource path:
    // projects/{project_id}/databases/{database_id}/documents/{document_path}
    String affectedDoc = firestoreEventData.getValue()
        .getName()
        .split("/documents/")[1]
        .replace("\"", "");

    logger.info(String.format("Replacing values: %s --> %s", currValue, newValue));

    // Wait for the async call to complete
    this.firestore
        .document(affectedDoc)
        .set(Map.of(FIELD_KEY, newValue), SetOptions.merge())
        .get();
  }
}

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

Funktion "In Großbuchstaben umwandeln" bereitstellen

  1. Richten Sie Ihre Firestore-Datenbank ein, falls noch nicht geschehen.

  2. Verwenden Sie den folgenden Befehl, um eine Funktion bereitzustellen, die durch Schreibereignisse im Dokument companies/{CompanyId} ausgelöst wird:

    gcloud functions deploy FUNCTION_NAME \
    --gen2 \
    --runtime=RUNTIME \
    --trigger-location=TRIGGER REGION \
    --region=REGION \
    --source=. \
    --entry-point=ENTRY_POINT \
    --set-env-vars GOOGLE_CLOUD_PROJECT=PROJECT_ID \
    --trigger-event-filters=type=google.cloud.firestore.document.v1.written \
    --trigger-event-filters=database='(default)' \
    --trigger-event-filters-path-pattern=document='messages/{pushId}'

    Ersetzen Sie Folgendes:

    • FUNCTION_NAME: Ein Name für die bereitgestellte Funktion.
    • RUNTIME: Die Sprachlaufzeit, die Ihre Funktion verwendet.
    • REGION: Die Region, in der die Funktion bereitgestellt werden soll.
    • TRIGGER_REGION: Der Speicherort des Triggers, der der Region der Firestore-Datenbank entsprechen muss.
    • ENTRY_POINT: Der Einstiegspunkt zur Funktion in Ihrem Quellcode. Dies ist der Code, der beim Ausführen der Funktion ausgeführt wird.
    • PROJECT_ID: Die eindeutige Kennung des Projekts.

    Verwenden Sie die anderen Felder unverändert:

    • --trigger-event-filters=type=google.cloud.firestore.document.v1.written gibt an, dass die Funktion ausgelöst wird, wenn ein Dokument gemäß dem Ereignistyp google.cloud.firestore.document.v1.written erstellt, aktualisiert oder gelöscht wird.
    • --trigger-event-filters=database='(default)' gibt die Firestore-Datenbank an. Verwenden Sie für den Standarddatenbanknamen (default).
    • --trigger-event-filters-path-pattern=document='messages/{pushId}' enthält das Pfadmuster der Dokumente, die auf relevante Änderungen überwacht werden sollen. Dieses Pfadmuster gibt an, dass alle Dokumente in der Sammlung messages überwacht werden sollen. Weitere Informationen finden Sie unter Informationen zu Pfadmustern.

Funktion "In Großbuchstaben umwandeln" testen

Richten Sie eine Sammlung mit dem Namen messages in Ihrer Firestore-Datenbank ein, um die gerade bereitgestellte Funktion In Großbuchstaben umwandeln zu testen:

  1. Zur Firestore-Seite "Daten"

  2. Klicken Sie auf Sammlung starten.

  3. Geben Sie messages als Sammlungs-ID an.

  4. Um das erste Dokument der Sammlung hinzuzufügen, akzeptieren Sie unter Erstes Dokument hinzufügen die automatisch generierte Dokument-ID.

  5. Um die bereitgestellte Funktion auszulösen, fügen Sie ein Dokument mit dem Namen "Original" und einem Feldwert in Kleinbuchstaben hinzu. Beispiel:

    Screenshot zum Erstellen einer Firestore-Sammlung

  6. Beim Speichern des Dokuments wird das Wort in Kleinbuchstaben im Wertfeld in ein Wort in Großbuchstaben umgewandelt.

    Wenn Sie den Feldwert anschließend so bearbeiten, dass er Kleinbuchstaben enthält, wird die Funktion noch einmal ausgelöst. Dadurch werden alle Kleinbuchstaben in Großbuchstaben umgewandelt.

Beschränkungen

  • 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.
  • Ein Trigger ist mit einer einzelnen Datenbank verknüpft. Sie können keinen Trigger erstellen, der mit mehreren Datenbanken übereinstimmt.
  • Durch das Löschen einer Datenbank werden Trigger für diese Datenbank nicht automatisch gelöscht. Der Trigger sendet keine Ereignisse mehr, bleibt aber bestehen, bis Sie ihn löschen.