diff --git a/executor.go b/executor.go index 12d401c4..3c8441d2 100644 --- a/executor.go +++ b/executor.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "reflect" + "sort" "strings" "github.com/graphql-go/graphql/gqlerrors" @@ -254,7 +255,9 @@ func executeFieldsSerially(p executeFieldsParams) *Result { } finalResults := make(map[string]interface{}, len(p.Fields)) - for responseName, fieldASTs := range p.Fields { + for _, orderedField := range orderedFields(p.Fields) { + responseName := orderedField.responseName + fieldASTs := orderedField.fieldASTs fieldPath := p.Path.WithKey(responseName) resolved, state := resolveField(p.ExecutionContext, p.ParentType, p.Source, fieldASTs, fieldPath) if state.hasNoFieldDefs { @@ -1038,3 +1041,39 @@ func getFieldDef(schema Schema, parentType *Object, fieldName string) *FieldDefi } return parentType.Fields()[fieldName] } + +// contains field information that will be placed in an ordered slice +type orderedField struct { + responseName string + fieldASTs []*ast.Field +} + +// orders fields from a fields map by location in the source +func orderedFields(fields map[string][]*ast.Field) []*orderedField { + orderedFields := []*orderedField{} + fieldMap := map[int]*orderedField{} + startLocs := []int{} + + for responseName, fieldASTs := range fields { + // find the lowest location in the current fieldASTs + lowest := -1 + for _, fieldAST := range fieldASTs { + loc := fieldAST.GetLoc().Start + if lowest == -1 || loc < lowest { + lowest = loc + } + } + startLocs = append(startLocs, lowest) + fieldMap[lowest] = &orderedField{ + responseName: responseName, + fieldASTs: fieldASTs, + } + } + + sort.Ints(startLocs) + for _, startLoc := range startLocs { + orderedFields = append(orderedFields, fieldMap[startLoc]) + } + + return orderedFields +}