Skip to content

Commit

Permalink
fix(firestore): prefer exact matches when reflecting fields (#4908)
Browse files Browse the repository at this point in the history
* fix(firestore): prefer exact matches when reflecting fields

* refactor(firestore): store field to avoid having to relookup
  • Loading branch information
crwilcox committed Sep 29, 2021
1 parent a46cf92 commit d3d9420
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 2 deletions.
32 changes: 32 additions & 0 deletions firestore/document_test.go
Expand Up @@ -160,6 +160,38 @@ func TestDataAt(t *testing.T) {
}
}

func TestDataTo(t *testing.T) {
doc := &DocumentSnapshot{
proto: &pb.Document{
Fields: map[string]*pb.Value{
"tags": arrayval(strval("value1"), strval("value2")),
"Tags": arrayval(strval("value3"), strval("value4")),
"TaGs": {ValueType: &pb.Value_NullValue{}},
},
},
}

type test struct {
Nothing string
Tags []string
}

// DataTo displayed indeterminate results run to run.
// https://github.com/googleapis/google-cloud-go/issues/4722
want := &test{Tags: []string{"value3", "value4"}}

for i := 0; i < 20; i++ {
got := &test{}
if err := doc.DataTo(got); err != nil {
t.Fatal(err)
}

if !testEqual(got, want) {
t.Fatalf("got %#v\nwant %#v", got, want)
}
}
}

func TestDataAtPath(t *testing.T) {
for _, test := range []struct {
fieldPath FieldPath
Expand Down
28 changes: 26 additions & 2 deletions firestore/from_value.go
Expand Up @@ -20,6 +20,7 @@ import (
"reflect"
"strings"

"cloud.google.com/go/internal/fields"
"github.com/golang/protobuf/ptypes"
pb "google.golang.org/genproto/googleapis/firestore/v1"
)
Expand Down Expand Up @@ -306,15 +307,38 @@ func createMapFromValueMap(pm map[string]*pb.Value, c *Client) (map[string]inter
// populateStruct sets the fields of vs, which must be a struct, from
// the matching elements of pm.
func populateStruct(vs reflect.Value, pm map[string]*pb.Value, c *Client) error {
fields, err := fieldCache.Fields(vs.Type())
fs, err := fieldCache.Fields(vs.Type())
if err != nil {
return err
}

type match struct {
vproto *pb.Value
f *fields.Field
}
// Find best field matches
matched := make(map[string]match)
for k, vproto := range pm {
f := fields.Match(k)
f := fs.Match(k)
if f == nil {
continue
}
if _, ok := matched[f.Name]; ok {
// If multiple case insensitive fields match, the exact match
// should win.
if f.Name == k {
matched[k] = match{vproto: vproto, f: f}
}
} else {
matched[f.Name] = match{vproto: vproto, f: f}
}
}

// Reflect values
for _, v := range matched {
f := v.f
vproto := v.vproto

if err := setReflectFromProtoValue(vs.FieldByIndex(f.Index), vproto, c); err != nil {
return fmt.Errorf("%s.%s: %v", vs.Type(), f.Name, err)
}
Expand Down

0 comments on commit d3d9420

Please sign in to comment.