-
Notifications
You must be signed in to change notification settings - Fork 114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Case insensitive matching of field names #337
Changes from 18 commits
834c572
588c1c6
9f192d2
4c1994d
4bcae9a
bde3d0a
e95f9dd
a91b614
4314b8c
1e9f300
eb88b7c
7d3b650
3de668b
011fbab
6ac4809
7faf8f3
81d1c6a
5dc652f
627b66e
41470e0
f23ddc7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ package gorm | |
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"reflect" | ||
"sort" | ||
"strings" | ||
|
@@ -18,22 +19,23 @@ type DefaultFieldSelectionConverter struct{} | |
|
||
// FieldSelectionStringToGorm is a shortcut to parse a string into FieldSelection struct and | ||
// receive a list of associations to preload. | ||
func FieldSelectionStringToGorm(ctx context.Context, fs string, obj interface{}) ([]string, error) { | ||
func FieldSelectionStringToGorm(ctx context.Context, fs string, obj interface{}, ignoreCase ...bool) ([]string, error) { | ||
c := &DefaultFieldSelectionConverter{} | ||
return c.FieldSelectionToGorm(ctx, query.ParseFieldSelection(fs), obj) | ||
return c.FieldSelectionToGorm(ctx, query.ParseFieldSelection(fs), obj, ignoreCase...) | ||
} | ||
|
||
// FieldSelectionToGorm receives FieldSelection struct and returns a list of associations to preload. | ||
func (converter *DefaultFieldSelectionConverter) FieldSelectionToGorm(ctx context.Context, fs *query.FieldSelection, obj interface{}) ([]string, error) { | ||
func (converter *DefaultFieldSelectionConverter) FieldSelectionToGorm(ctx context.Context, fs *query.FieldSelection, obj interface{}, ignoreCase ...bool) ([]string, error) { | ||
objType := indirectType(reflect.TypeOf(obj)) | ||
if fs.GetFields() == nil { | ||
selectedFields := fs.GetFields() | ||
if selectedFields == nil { | ||
return preloadEverything(objType, nil) | ||
} | ||
var toPreload []string | ||
fieldNames := getSortedFieldNames(fs.GetFields()) | ||
fieldNames := getSortedFieldNames(selectedFields) | ||
for _, fieldName := range fieldNames { | ||
f := fs.GetFields()[fieldName] | ||
subPreload, err := handlePreloads(f, objType) | ||
f := selectedFields[fieldName] | ||
subPreload, err := handlePreloads(f, objType, ignoreCase...) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
@@ -75,36 +77,55 @@ fields: | |
return toPreload, nil | ||
} | ||
|
||
func handlePreloads(f *query.Field, objType reflect.Type) ([]string, error) { | ||
sf, ok := objType.FieldByName(util.Camel(f.GetName())) | ||
func handlePreloads(f *query.Field, objType reflect.Type, ignoreCase ...bool) ([]string, error) { | ||
queryFieldName := f.GetName() | ||
log.Printf("Query name = %v\n", queryFieldName) | ||
|
||
var sf reflect.StructField | ||
var ok bool | ||
if len(ignoreCase) > 0 && ignoreCase[0] { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe I'm missing something but why is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an "optional" parameter for backward compatibility. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok that makes sense. if that's the case then make sure that there is a unti test for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, there are. In test file, the function has been called as followed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it might be nice to have at least one |
||
sf, ok = objType.FieldByNameFunc(func(name string) bool { | ||
return strings.EqualFold(name, strings.ToLower(strings.ReplaceAll(queryFieldName, "_", ""))) | ||
}) | ||
} else { | ||
sf, ok = objType.FieldByName(util.Camel(queryFieldName)) | ||
} | ||
|
||
if !ok { | ||
log.Printf("no field found for query '%v\n'", queryFieldName) | ||
return nil, nil | ||
} | ||
|
||
fType := indirectType(sf.Type) | ||
if f.GetSubs() == nil { | ||
fName := sf.Name | ||
log.Printf("found field by name '%v'\n", fName) | ||
|
||
fieldSubs := f.GetSubs() | ||
|
||
if fieldSubs == nil { | ||
if isModel(fType) { | ||
return []string{util.Camel(f.GetName())}, nil | ||
return []string{fName}, nil | ||
} else { | ||
return nil, nil | ||
} | ||
} | ||
if !isModel(fType) { | ||
return nil, fmt.Errorf("%s is expected to be a model, but got %s ", f.GetName(), fType) | ||
return nil, fmt.Errorf("%s is expected to be a model, but got %s ", queryFieldName, fType) | ||
} | ||
var toPreload []string | ||
fieldNames := getSortedFieldNames(f.GetSubs()) | ||
fieldNames := getSortedFieldNames(fieldSubs) | ||
for _, fieldName := range fieldNames { | ||
subField := f.GetSubs()[fieldName] | ||
subPreload, err := handlePreloads(subField, fType) | ||
subField := fieldSubs[fieldName] | ||
subPreload, err := handlePreloads(subField, fType, ignoreCase...) | ||
if err != nil { | ||
return nil, err | ||
} | ||
for i, e := range subPreload { | ||
subPreload[i] = util.Camel(f.GetName()) + "." + e | ||
subPreload[i] = fName + "." + e | ||
} | ||
toPreload = append(toPreload, subPreload...) | ||
} | ||
return append(toPreload, util.Camel(f.GetName())), nil | ||
return append(toPreload, fName), nil | ||
} | ||
|
||
func getSortedFieldNames(fields map[string]*query.Field) []string { | ||
|
@@ -126,7 +147,7 @@ func preload(db *gorm.DB, obj interface{}, assoc string) (*gorm.DB, error) { | |
for i, part := range assocPath { | ||
sf, ok := objType.FieldByName(part) | ||
if !ok { | ||
return nil, fmt.Errorf("Cannot find %s in %s", part, objType) | ||
return nil, fmt.Errorf("cannot find %s in %s", part, objType) | ||
} | ||
objType = indirectType(sf.Type) | ||
if !isModel(objType) { | ||
|
@@ -143,5 +164,5 @@ func preload(db *gorm.DB, obj interface{}, assoc string) (*gorm.DB, error) { | |
} | ||
} | ||
} | ||
return nil, fmt.Errorf("Cannot preload empty association") | ||
return nil, fmt.Errorf("cannot preload empty association") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this needs to be a option struct{} not a boolean. consider the meaning is for multiple options.