@@ -444,18 +444,22 @@ func (b *execBuilder) makeObjectExec(typeName string, fields ast.FieldsDefinitio
444
444
445
445
Fields := make (map [string ]* Field )
446
446
rt := unwrapPtr (resolverType )
447
- fieldsCount := fieldCount (rt , map [string ]int {})
447
+ fieldsCount , fieldTagsCount := fieldCount (rt , map [ string ] int {} , map [string ]int {})
448
448
for _ , f := range fields {
449
449
var fieldIndex []int
450
450
methodIndex := findMethod (resolverType , f .Name )
451
451
if b .useFieldResolvers && methodIndex == - 1 {
452
- if fieldsCount [strings .ToLower (stripUnderscore (f .Name ))] > 1 {
452
+ // If a resolver field is ambiguous thrown an error unless there is exactly one field with the given graphql
453
+ // reflect tag. In that case use the field with the reflect tag.
454
+ if fieldTagsCount [f .Name ] > 1 {
455
+ return nil , fmt .Errorf ("%s does not resolve %q: multiple fields have a graphql reflect tag %q" , resolverType , typeName , f .Name )
456
+ } else if fieldsCount [strings .ToLower (stripUnderscore (f .Name ))] > 1 && fieldTagsCount [f .Name ] != 1 {
453
457
return nil , fmt .Errorf ("%s does not resolve %q: ambiguous field %q" , resolverType , typeName , f .Name )
454
458
}
455
- fieldIndex = findField (rt , f .Name , []int {})
459
+ fieldIndex = findField (rt , f .Name , []int {}, fieldTagsCount )
456
460
}
457
461
if methodIndex == - 1 && len (fieldIndex ) == 0 {
458
- hint := ""
462
+ var hint string
459
463
if findMethod (reflect .PtrTo (resolverType ), f .Name ) != - 1 {
460
464
hint = " (hint: the method exists on the pointer type)"
461
465
}
@@ -529,9 +533,7 @@ func (b *execBuilder) makeObjectExec(typeName string, fields ast.FieldsDefinitio
529
533
var contextType = reflect .TypeOf ((* context .Context )(nil )).Elem ()
530
534
var errorType = reflect .TypeOf ((* error )(nil )).Elem ()
531
535
532
- func (b * execBuilder ) makeFieldExec (typeName string , f * ast.FieldDefinition , m reflect.Method , sf reflect.StructField ,
533
- methodIndex int , fieldIndex []int , methodHasReceiver bool ) (* Field , error ) {
534
-
536
+ func (b * execBuilder ) makeFieldExec (typeName string , f * ast.FieldDefinition , m reflect.Method , sf reflect.StructField , methodIndex int , fieldIndex []int , methodHasReceiver bool ) (* Field , error ) {
535
537
var argsPacker * packer.StructPacker
536
538
var hasError bool
537
539
var hasContext bool
@@ -662,17 +664,29 @@ func findMethod(t reflect.Type, name string) int {
662
664
return - 1
663
665
}
664
666
665
- func findField (t reflect.Type , name string , index []int ) []int {
667
+ func findField (t reflect.Type , name string , index []int , matchingTagsCount map [ string ] int ) []int {
666
668
for i := 0 ; i < t .NumField (); i ++ {
667
669
field := t .Field (i )
668
670
669
671
if field .Type .Kind () == reflect .Struct && field .Anonymous {
670
- newIndex := findField (field .Type , name , []int {i })
672
+ newIndex := findField (field .Type , name , []int {i }, matchingTagsCount )
671
673
if len (newIndex ) > 1 {
672
674
return append (index , newIndex ... )
673
675
}
674
676
}
675
677
678
+ if gt , ok := field .Tag .Lookup ("graphql" ); ok {
679
+ if name == gt {
680
+ return append (index , i )
681
+ }
682
+ }
683
+
684
+ // The current field's tag didn't match, however, if the tag of another field matches,
685
+ // then skip the name matching until we find the desired field with the correct tag.
686
+ if matchingTagsCount [name ] > 0 {
687
+ continue
688
+ }
689
+
676
690
if strings .EqualFold (stripUnderscore (name ), stripUnderscore (field .Name )) {
677
691
return append (index , i )
678
692
}
@@ -682,26 +696,40 @@ func findField(t reflect.Type, name string, index []int) []int {
682
696
}
683
697
684
698
// fieldCount helps resolve ambiguity when more than one embedded struct contains fields with the same name.
685
- func fieldCount (t reflect.Type , count map [string ]int ) map [string ]int {
699
+ // or when a field has a `graphql` reflect tag with the same name as some other field causing name collision.
700
+ func fieldCount (t reflect.Type , count , tagsCount map [string ]int ) (map [string ]int , map [string ]int ) {
686
701
if t .Kind () != reflect .Struct {
687
- return nil
702
+ return nil , nil
688
703
}
689
704
690
705
for i := 0 ; i < t .NumField (); i ++ {
691
706
field := t .Field (i )
692
- fieldName := strings .ToLower (stripUnderscore (field .Name ))
707
+ var fieldName , gt string
708
+ var hasTag bool
709
+ if gt , hasTag = field .Tag .Lookup ("graphql" ); hasTag && gt != "" {
710
+ fieldName = gt
711
+ } else {
712
+ fieldName = strings .ToLower (stripUnderscore (field .Name ))
713
+ }
693
714
694
715
if field .Type .Kind () == reflect .Struct && field .Anonymous {
695
- count = fieldCount (field .Type , count )
716
+ count , tagsCount = fieldCount (field .Type , count , tagsCount )
696
717
} else {
697
718
if _ , ok := count [fieldName ]; ! ok {
698
719
count [fieldName ] = 0
699
720
}
700
721
count [fieldName ]++
722
+ if ! hasTag {
723
+ continue
724
+ }
725
+ if _ , ok := count [gt ]; ! ok {
726
+ tagsCount [gt ] = 0
727
+ }
728
+ tagsCount [gt ]++
701
729
}
702
730
}
703
731
704
- return count
732
+ return count , tagsCount
705
733
}
706
734
707
735
func unwrapNonNull (t ast.Type ) (ast.Type , bool ) {
0 commit comments