Skip to content

Instantly share code, notes, and snippets.

@Yama-Tomo
Created August 19, 2021 06:02
Show Gist options
  • Save Yama-Tomo/435b0b637075d776cc18726e9ea13567 to your computer and use it in GitHub Desktop.
Save Yama-Tomo/435b0b637075d776cc18726e9ea13567 to your computer and use it in GitHub Desktop.
transform struct to map for firestore
package main
import (
"cloud.google.com/go/firestore"
"time"
"fmt"
"reflect"
"strings"
)
type Struct struct {
DocID string `firestore:"-"`
Name string `firestore:"name"`
Time time.Time `firestore:"time"`
Tags []string `firestore:"tags"`
NestedData1 *NestedStruct `firestore:"nested_data_1" nested:"true"`
NestedData2 NestedStruct `firestore:"nested_data_2" nested:"true"`
NestedDataItems []NestedStruct `firestore:"nested_data_items" nested:"true"`
UpdatedAt *time.Time `firestore:"updated_at, serverTimestamp"`
CreatedAt *time.Time `firestore:"created_at,serverTimestamp"`
}
type NestedStruct struct {
Label string `firestore:"label"`
Count int `firestore:"count"`
UpdatedAt *time.Time `firestore:"updated_at,serverTimestamp"`
CreatedAt *time.Time `firestore:"created_at,serverTimestamp"`
}
func main() {
nested1 := &NestedStruct{
Label: "test",
Count: 1,
}
now := time.Now()
nested2 := NestedStruct{
Label: "test",
Count: 2,
CreatedAt: &now,
}
strct := Struct{
DocID: "xxxxx-yyyyyy-zzzzz",
Name: "hoge",
Tags: []string{"foo", "bar"},
NestedDataItems: []NestedStruct{nested2},
Time: time.Now(),
NestedData1: nested1,
NestedData2: nested2,
}
fmt.Println(TransformStructToMap(strct))
}
func TransformStructToMap(structVal interface{}) map[string]interface{} {
result := map[string]interface{}{}
structRef := reflect.Indirect(reflect.ValueOf(structVal))
if structRef.Kind() != reflect.Struct {
return result
}
for i := 0; i < structRef.NumField(); i++ {
fieldName, meta := getFieldNameAndMeta(structRef.Type().Field(i))
if fieldName == "" || fieldName == "-" {
continue
}
if strings.Contains(meta, "serverTimestamp") {
result[fieldName] = firestore.ServerTimestamp
continue
}
fieldRef := structRef.Field(i)
if structRef.Type().Field(i).Tag.Get("nested") == "true" {
var nestedVal interface{}
if fieldRef.Kind() == reflect.Slice {
var slice []interface{}
for sliceIdx := 0; i < structRef.Field(sliceIdx).Len(); sliceIdx++ {
slice = append(slice, TransformStructToMap(fieldRef.Index(sliceIdx).Interface()))
}
nestedVal = slice
} else {
nestedVal = TransformStructToMap(fieldRef.Interface())
}
result[fieldName] = nestedVal
continue
}
result[fieldName] = fieldRef.Interface()
}
return result
}
func getFieldNameAndMeta(field reflect.StructField) (string, string) {
targets := []string{
field.Tag.Get("firestore"),
field.Tag.Get("json"),
field.Name,
}
for _, target := range targets {
if target == "" {
continue
}
if strings.Contains(target, ",") {
arr := strings.SplitN(target, ",", 2)
return arr[0], arr[1]
}
return target, ""
}
return "", ""
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment