{ "aaBool": true, "aaInt": 1111, "aaStr": "Lorem", "map1": { "m1book": true, "m1int": 3333, "m1str": "m1Lorem" }, "map2": { "map3": { "m3int": 888, "m3str": "m3Lorem" } } }
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}'