I am trying to implement a trigger (as a google cloud function) in golang that gets invoked when a firestore document is updated. I am following the documentation give here: https://cloud.google.com/functions/docs/calling/cloud-firestore
My firestore document is given below:
{
"aaBool": true,
"aaInt": 1111,
"aaStr": "Lorem",
"map1": {
"m1book": true,
"m1int": 3333,
"m1str": "m1Lorem"
},
"map2": {
"map3": {
"m3int": 888,
"m3str": "m3Lorem"
}
}
}
The code I have currently is:
package trigger_firestore
import (
"cloud.google.com/go/firestore"
_ "encoding/json"
firebase "firebase.google.com/go/v4"
"fmt"
"github.com/GoogleCloudPlatform/functions-framework-go/functions"
"github.com/cloudevents/sdk-go/v2/event"
"github.com/googleapis/google-cloudevents-go/cloud/firestoredata"
"golang.org/x/net/context"
"google.golang.org/protobuf/proto"
"log"
"os"
"reflect"
"strings"
)
// 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("ProcessTrigger", ProcessTrigger)
}
func ProcessTrigger(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)
}
fullPath := strings.Split(data.GetValue().GetName(), "/documents/")[1]
pathParts := strings.Split(fullPath, "/")
doc := strings.Join(pathParts[1:], "/")
log.Printf("Doc: %+v\n", doc)
log.Printf("Function triggered by change to: %v\n", event.Source())
log.Printf("Old value: %+v\n", data.GetOldValue())
log.Printf("New value: %+v\n", data.GetValue())
processUpdate(&data)
return nil
}
func processUpdate(data *firestoredata.DocumentEventData) {
updateMask := data.GetUpdateMask()
log.Printf("Update Mask: %+v\n", updateMask)
for _, fieldName := range updateMask.GetFieldPaths() {
log.Printf("=================================")
log.Printf("FieldName: %v", fieldName)
log.Printf("FieldNameType: %v", reflect.TypeOf(fieldName))
fieldValue, ok := data.GetValue().GetFields()[fieldName]
if ok {
log.Printf("FieldValue: %v", fieldValue)
stringSlice := strings.Split(fieldValue.String(), ":")
log.Println(stringSlice)
}
}
}
The above code works fine for updates to the top-level non-nested-fields (prefix aa) and I am able to print the fieldValue.
However for updates to nested fields (inside map1 and map3), ok is false.
How do I get the updated fieldValue for nested fields?
The logs generated are:
2024/04/14 14:55:44 Doc: testdoc
2024/04/14 14:55:44 Function triggered by change to: //firestore.googleapis.com/projects/myproj/databases/(default)
2024/04/14 14:55:44 Old value: name:"projects/myproj/databases/(default)/documents/testcollection/testdoc" fields:{key:"aaBool" value:{boolean_value:true}} fields:{key:"aaInt" value:{integer_value:11111}} fields:{key:"aaStr" value:{string_value:"Lorem"}} fields:{key:"map1" value:{map_value:{fields:{key:"m1book" value:{boolean_value:true}} fields:{key:"m1int" value:{integer_value:3333}} fields:{key:"m1str" value:{string_value:"m1Lorem"}}}}} fields:{key:"map2" value:{map_value:{fields:{key:"map3" value:{map_value:{fields:{key:"m3int" value:{integer_value:888}} fields:{key:"m3str" value:{string_value:"m3Lorem"}}}}}}}} create_time:{seconds:1713104864 nanos:548214000} update_time:{seconds:1713106076 nanos:80031000}
2024/04/14 14:55:44 New value: name:"projects/myproj/databases/(default)/documents/testcollection/testdoc" fields:{key:"aaBool" value:{boolean_value:false}} fields:{key:"aaInt" value:{integer_value:222}} fields:{key:"aaStr" value:{string_value:"LoremIpsum"}} fields:{key:"map1" value:{map_value:{fields:{key:"m1book" value:{boolean_value:false}} fields:{key:"m1int" value:{integer_value:4444}} fields:{key:"m1str" value:{string_value:"m1LoremIpsum"}}}}} fields:{key:"map2" value:{map_value:{fields:{key:"map3" value:{map_value:{fields:{key:"m3int" value:{integer_value:999}} fields:{key:"m3str" value:{string_value:"m3LoremIpsum"}}}}}}}} create_time:{seconds:1713104864 nanos:548214000} update_time:{seconds:1713106543 nanos:26415000}
2024/04/14 14:55:44 Update Mask: field_paths:"map2.map3.m3int" field_paths:"map2.map3.m3str" field_paths:"map1.m1str" field_paths:"map1.m1int" field_paths:"map1.m1book" field_paths:"aaStr" field_paths:"aaBool" field_paths:"aaInt"
2024/04/14 14:55:44 =================================
2024/04/14 14:55:44 FieldName: map2.map3.m3int
2024/04/14 14:55:44 FieldNameType: string
2024/04/14 14:55:44 =================================
2024/04/14 14:55:44 FieldName: map2.map3.m3str
2024/04/14 14:55:44 FieldNameType: string
2024/04/14 14:55:44 =================================
2024/04/14 14:55:44 FieldName: map1.m1str
2024/04/14 14:55:44 FieldNameType: string
2024/04/14 14:55:44 =================================
2024/04/14 14:55:44 FieldName: map1.m1int
2024/04/14 14:55:44 FieldNameType: string
2024/04/14 14:55:44 =================================
2024/04/14 14:55:44 FieldName: map1.m1book
2024/04/14 14:55:44 FieldNameType: string
2024/04/14 14:55:44 =================================
2024/04/14 14:55:44 FieldName: aaStr
2024/04/14 14:55:44 FieldNameType: string
2024/04/14 14:55:44 FieldValue: string_value:"LoremIpsum"
2024/04/14 14:55:44 [string_value "LoremIpsum"]
2024/04/14 14:55:44 =================================
2024/04/14 14:55:44 FieldName: aaBool
2024/04/14 14:55:44 FieldNameType: string
2024/04/14 14:55:44 FieldValue: boolean_value:false
2024/04/14 14:55:44 [boolean_value false]
2024/04/14 14:55:44 =================================
2024/04/14 14:55:44 FieldName: aaInt
2024/04/14 14:55:44 FieldNameType: string
2024/04/14 14:55:44 FieldValue: integer_value:222
2024/04/14 14:55:44 [integer_value 222]
In case required, this is how I deploy the trigger:
gcloud functions deploy test-trigger-1 \
--gen2 \
--runtime=go122 \
--region=us-west1 \
--trigger-location=us-west1 \
--source=. \
--entry-point=ProcessTrigger \
--min-instances=1 \
--memory=256Mi \
--set-env-vars=[GOOGLE_CLOUD_PROJECT=myproj] \
--trigger-event-filters=type=google.cloud.firestore.document.v1.written \
--trigger-event-filters=database='(default)' \
--trigger-event-filters-path-pattern=document='testcollection/{docID}'