Blog

Wie man Firestore-Ereignisse mit Cloud-Funktionen und Golang liest

Luca Cavallin

Aktualisiert Oktober 20, 2025
6 Minuten

Sie möchten wissen, wie man "Firestore Events mit Cloud Functions und Golang liest"? Dann sind Sie hier richtig! Ich habe kürzlich an einem Nebenprojekt namens "Syn" gearbeitet, das darauf abzielt, die Umgebung mit Hilfe des Raspberry Pi, Google Cloud und React Native visuell zu überwachen. Der Raspberry Pi verwendet ein Tool namens motion, das Bilder (mit der Pi-Kamera) macht, wenn eine Bewegung erkannt wird. Die Bilder werden dann in den Cloud-Speicher hochgeladen, und eine Cloud-Funktion lauscht auf Ereignisse, die ausgelöst werden, wenn ein neues Objekt in den Bucket hochgeladen wird. Wenn ein neues Bild hochgeladen wird, versucht die Funktion, das Bild mithilfe der Vision API zu beschriften und speichert das Ergebnis in Firestore. Firestore löst selbst ein Ereignis aus, auf das ich in einer anderen Cloud-Funktion warte, die IFTTT verwendet, um mich zu benachrichtigen, wenn eine Bewegung erkannt wurde und was die Vision API im Bild gefunden hat. Das Ziel dieses Blogbeitrags ist es, zu erklären, wie man Firestore-Ereignisse analysiert, die in einem ziemlich verwirrenden Format an die Funktion geliefert werden!

Form und Farbe der Ereignisstruktur

Die Cloud-Funktion, die neue Ereignisse (= neue Uploads vom Raspberry Pi, wenn eine Bewegung erkannt wird) verarbeitet, erstellt nach der Kennzeichnung des Bildes mit der Vision API ein neues Ereignisobjekt und speichert es in der Sammlung Events in Firestore. Die Ereignisstruktur sieht wie folgt aus:

type Event struct {
    URI     string    json:"uri" firestore:"uri"
    Created time.Time json:"created" firestore:"created"
    Labels  []Label   json:"labels" firestore:"labels"
}

Die Struktur verweist auch auf ein Array von Labels. Label ist eine Struktur, die wie folgt definiert ist:

type Label struct {
    Description string  json:"description" firestore:"description"
    Score       float32 json:"score" firestore:"score"
}

Dies ist das Ergebnis, nachdem die Informationen in Firestore gespeichert wurden:

Syn - Firestore

Erstellen Sie eine Funktion zum Abhören von Firestore-Ereignissen

Eine weitere Funktion namens Notify wartet auf Ereignisse von Firestore (und benachrichtigt dann den Benutzer über IFTTT), die ausgelöst werden, wenn neue Daten in die Datenbank aufgenommen werden. Ich habe Terraform verwendet, um die Funktion einzurichten:

resource "google_cloudfunctions_function" "notify" {
  project               = data.google_project.this.project_id
  region                = "europe-west1"
  name                  = "Notify"
  description           = "Notifies of newly labeled uploads"
  service_account_email = google_service_account.functions.email
  runtime               = "go113"
  ingress_settings      = "ALLOW_INTERNAL_ONLY"
  available_memory_mb   = 128

  entry_point = "Notify"

  source_repository {
    url = "https://source.developers.google.com/projects/${data.google_project.this.project_id}/repos/syn/moveable-aliases/master/paths/functions"
  }

  event_trigger {
    event_type = "providers/cloud.firestore/eventTypes/document.create"
    resource   = "Events/{ids}"
  }

  environment_variables = {
    "IFTTT_WEBHOOK_URL" : var.ifttt_webhook_url
  }
}

Der Block event_trigger definiert das Ereignis, auf das die Funktion warten soll. In diesem Fall warte ich auf providers/cloud.firestore/eventTypes/document.create Ereignisse in der Sammlung Events.

Wie sieht ein "rohes" Firestore Ereignis aus?

Anhand von fmt.Printf("%+v", event) können wir sehen, dass das Firestore-Ereignisobjekt wie folgt aussieht:

{OldValue:{CreateTime:0001-01-01 00:00:00 +0000 UTC Fields:{Created:{TimestampValue:0001-01-01 00:00:00 +0000 UTC} File:{MapValue:{Fields:{Bucket:{StringValue:} Name:{StringValue:}}}} Labels:{ArrayValue:{Values:[]}}} Name: UpdateTime:0001-01-01 00:00:00 +0000 UTC} Value:{CreateTime:2021-07-27 09:22:03.654255 +0000 UTC Fields:{Created:{TimestampValue:2021-07-27 09:22:01.4 +0000 UTC} File:{MapValue:{Fields:{Bucket:{StringValue:} Name:{StringValue:}}}} Labels:{ArrayValue:{Values:[{MapValue:{Fields:{Description:{StringValue:cat} Score:{DoubleValue:0.8764283061027527}}}} {MapValue:{Fields:{Description:{StringValue:carnivore} Score:{DoubleValue:0.8687784671783447}}}} {MapValue:{Fields:{Description:{StringValue:asphalt} Score:{DoubleValue:0.8434737920761108}}}} {MapValue:{Fields:{Description:{StringValue:felidae} Score:{DoubleValue:0.8221824765205383}}}} {MapValue:{Fields:{Description:{StringValue:road surface} Score:{DoubleValue:0.807261049747467}}}}]}}} Name:projects/cvln-syn/databases/(default)/documents/Events/tVhYbIZBQypHtHzDUabq UpdateTime:2021-07-27 09:22:03.654255 +0000 UTC} UpdateMask:{FieldPaths:[]}}

...was extrem verwirrend ist! Ich hatte erwartet, dass das Ereignis genauso aussieht wie die Daten, die ich zuvor in der Datenbank gespeichert hatte, aber aus irgendeinem Grund sieht ein Firebase-Ereignis so aus. Glücklicherweise hilft das "JSON to Go Struct" IntelliJ IDEA Plugin dabei, aus dem oben Gesagten schlau zu werden:

type FirestoreUpload struct {
    Created struct {
        TimestampValue time.Time json:"timestampValue"
    } json:"created"
    File struct {
        MapValue struct {
            Fields struct {
                Bucket struct {
                    StringValue string json:"stringValue"
                } json:"bucket"
                Name struct {
                    StringValue string json:"stringValue"
                } json:"name"
            } json:"fields"
        } json:"mapValue"
    } json:"file"
    Labels struct {
        ArrayValue struct {
            Values []struct {
                MapValue struct {
                    Fields struct {
                        Description struct {
                            StringValue string json:"stringValue"
                        } json:"description"
                        Score struct {
                            DoubleValue float64 json:"doubleValue"
                        } json:"score"
                    } json:"fields"
                } json:"mapValue"
            } json:"values"
        } json:"arrayValue"
    } json:"labels"
}

Das ist zwar immer noch verwirrend, aber zumindest kann ich jetzt die Struktur aufteilen, so dass ich die Typen an anderer Stelle in der Anwendung korrekt referenzieren kann, wenn ich z.B. eine Schleife durch die Beschriftungen machen muss.

Aufräumen der Ereignisstruktur

Die FirestoreUpload kann aufgeteilt werden, um benannte Felder anstelle von anonymen Strukturen zu haben. Dies ist nützlich, um an anderer Stelle in der Anwendung auf die richtigen Felder und Typen verweisen zu können, z.B. beim Durchlaufen von Schleifen in den Labels.

package events

import (
    "github.com/thoas/go-funk"
    "time"
)

//FirestoreEvent is the payload of a Firestore event
type FirestoreEvent struct {
    OldValue   FirestoreValue json:"oldValue"
    Value      FirestoreValue json:"value"
    UpdateMask struct {
        FieldPaths []string json:"fieldPaths"
    } json:"updateMask"
}

// FirestoreValue holds Firestore fields
type FirestoreValue struct {
    CreateTime time.Time       json:"createTime"
    Fields     FirestoreUpload json:"fields"
    Name       string          json:"name"
    UpdateTime time.Time       json:"updateTime"
}

// FirestoreUpload represents a Firebase event of a new record in the Upload collection
type FirestoreUpload struct {
    Created Created json:"created"
    File    File    json:"file"
    Labels  Labels  json:"labels"
}

type Created struct {
    TimestampValue time.Time json:"timestampValue"
}

type File struct {
    MapValue FileMapValue json:"mapValue"
}

type FileMapValue struct {
    Fields FileFields json:"fields"
}

type FileFields struct {
    Bucket StringValue json:"bucket"
    Name   StringValue json:"name"
}

type Labels struct {
    ArrayValue LabelArrayValue json:"arrayValue"
}

type LabelArrayValue struct {
    Values []LabelValues json:"values"
}

type LabelValues struct {
    MapValue LabelsMapValue json:"mapValue"
}

type LabelsMapValue struct {
    Fields LabelFields json:"fields"
}

type LabelFields struct {
    Description StringValue json:"description"
    Score       DoubleValue json:"score"
}

type StringValue struct {
    StringValue string json:"stringValue"
}

type DoubleValue struct {
    DoubleValue float64 json:"doubleValue"
}

// GetUploadLabels returns the labels of the image as an array of strings
func (e FirestoreEvent) GetUploadLabels() []string {
    return funk.Map(e.Value.Fields.Labels.ArrayValue.Values, func(l LabelValues) string {
        return l.MapValue.Fields.Description.StringValue
    }).([]string)
}

Die Funktion GetUploadLabels() ist ein Beispiel dafür, wie auf das Ereignisobjekt FirestoreUpload zugegriffen werden sollte. Hier verwende ich auch das go-funk-Paket, das Go einige zusätzliche funktionale Fähigkeiten hinzufügt (aber die Leistung ist nicht so gut wie eine "native" Schleife).

Zusammenfassung

In diesem Artikel habe ich erklärt, wie Sie Firestore-Ereignisse von Cloud-Funktionen lesen können, die auf sie warten. Die Beispiele sind in Golang geschrieben, aber andere Sprachen müssen die Nachrichten auf ähnliche Weise parsen. Das ist zwar nicht sehr praktisch, aber das ist das aktuelle Format der Firestore-Ereignisse! Sobald Sie wissen, wie Sie sie lesen können, ist der Rest zum Glück ganz einfach!

Credits: Header image by Unsplash - Luca Cavallin.

Verfasst von

Luca Cavallin

Luca is a Software Engineer and Trainer with full-stack experience ranging from distributed systems to cross-platform apps. He is currently interested in building modern, serverless solutions on Google Cloud using Golang, Rust and React and leveraging SRE and Agile practices. Luca holds 3 Google Cloud certifications, he is part of the Google Developers Experts community and he is the co-organizer of the Google Cloud User Group that Binx.io holds with Google.

Contact

Let’s discuss how we can support your journey.