Skip to content

Commit

Permalink
INF-659 - ignore fields with GORM tags such as 'many2many' and by def…
Browse files Browse the repository at this point in the history
…ault avoid using the explicit select
  • Loading branch information
Ronnie Lazar committed Aug 7, 2019
1 parent e5f5b22 commit a10697c
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 26 deletions.
37 changes: 33 additions & 4 deletions query/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ func NewBuilder(c *Config) (*Builder, error) {
if err := c.defaults(); err != nil {
return nil, err
}

if c.OnlySelectNonDetailedFields {
c.ExplicitSelect = true
}

b := &Builder{
Config: c,
sortFields: make(map[string]bool),
Expand Down Expand Up @@ -287,17 +292,41 @@ func (b *Builder) addFilterFieldsForBoolFields(withSep, colName string, parse pa
b.addFilterField(withSep+opNotEqual, colName+" <> ?", parse, splitOnComma)
}

var (
ignoreOptions []string = []string{
"-",
"foreignkey",
"association_foreignkey",
"many2many",
}
)

func (b *Builder) appendToSelect(colName string, gormOptions []string, options []string) {
for _, s := range ignoreOptions {
if contains(gormOptions, s) {
return
}
}
if b.OnlySelectNonDetailedFields && contains(options, detailedTag) {
return
}

b.selectFields = append(b.selectFields, colName)

}

// parseField handle sort and filter fields.
func (b *Builder) parseField(field *structs.Field) {
colName := gorm.ToDBName(field.Name())

// get all options from the struct field.
options := strings.Split(field.Tag(b.TagName), ",")
gormOptions := strings.Split(field.Tag("gorm"),",")
gormOptions := strings.Split(field.Tag("gorm"), ";")

if !contains(gormOptions,"-") && (!b.OnlySelectNonDetailedFields || !contains(options, detailedTag)) {
b.selectFields = append(b.selectFields, colName)
if b.ExplicitSelect {
b.appendToSelect(colName, gormOptions, options)
}

// struct field has a sort option.
if contains(options, sortTag) {
b.sortFields[colName] = true
Expand Down Expand Up @@ -390,7 +419,7 @@ func hasQueryParam(l []string) (string, bool) {
// contains test if string is in the given list.
func contains(l []string, s string) bool {
for i := range l {
if l[i] == s {
if l[i] == s || strings.HasPrefix(l[i], s) {
return true
}
}
Expand Down
6 changes: 5 additions & 1 deletion query/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ type Config struct {
OffsetParam string
// SearchOperator used to combine search condition together. defaults to "AND".
SearchOperator string
// OnlySelectNonDetailedFields - if true will select only the non 'detailed' fields.
// ExplicitSelect - if true, the query will select the relevant specific columns.
// else will select '*'
ExplicitSelect bool
// OnlySelectNonDetailedFields - if true will select only the non 'detailed' fields
// true implies ExplicitSelect = true
OnlySelectNonDetailedFields bool
}

Expand Down
62 changes: 41 additions & 21 deletions query/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,21 @@ func (Tags) Wrap(s string) string {

// model used in the unit tests.
type model struct {
Name string `query:"sort,filter"`
Status string `query:"filter"`
Age int64 `query:"filter"`
Year int `query:"filter,detailed"`
Dummy int `gorm:"-"`
CreatedAt time.Time `query:"sort,filter"`
UpdatedAt time.Time `query:"sort,filter"`
Tags Tags `query:"filter,param=tag_name"`
FlagPtr *bool `query:"filter,sort"`
Flag bool `query:"filter,sort"`
EnumValPtr *MyEnum `query:"filter,sort"`
EnumVal MyEnum `query:"filter,sort"`
Name string `query:"sort,filter"`
Status string `query:"filter"`
Age int64 `query:"filter"`
Year int `query:"filter,detailed"`
Dummy int `gorm:"-"`
CreatedAt time.Time `query:"sort,filter"`
UpdatedAt time.Time `query:"sort,filter"`
Tags Tags `query:"filter,param=tag_name"`
FlagPtr *bool `query:"filter,sort"`
Flag bool `query:"filter,sort"`
EnumValPtr *MyEnum `query:"filter,sort"`
EnumVal MyEnum `query:"filter,sort"`
FieldToIgnore1 bool `gorm:"foreignkey:ExternalEndpointID"`
FieldToIgnore2 bool `gorm:"association_foreignkey:UUID"`
FieldToIgnore3 bool `gorm:"many2many:UUID"`
}

const (
Expand Down Expand Up @@ -87,12 +90,12 @@ func TestQuery(t *testing.T) {
{
name: "simple filters",
parseInput: url.Values{
"flag": []string{"true"},
"flag_ptr_eq": []string{"false"},
"enum_val": []string{"v1"},
"enum_val_ptr": []string{"v2"},
"age_gt": []string{"10"},
"name_eq": []string{"a8m", "pos", "yossi"},
"flag": []string{"true"},
"flag_ptr_eq": []string{"false"},
"enum_val": []string{"v1"},
"enum_val_ptr": []string{"v2"},
"age_gt": []string{"10"},
"name_eq": []string{"a8m", "pos", "yossi"},
},
configInput: &Config{},
expectedQueryInput: &DBQuery{
Expand Down Expand Up @@ -233,13 +236,14 @@ func TestQuery(t *testing.T) {
},
},
{
name: "select with details",
name: "select with details and explicit",
parseInput: url.Values{
"sort": []string{"+updated_at", "name", "-created_at"},
},
configInput: &Config{
Model: &model{},
OnlySelectNonDetailedFields: false,
ExplicitSelect: true,
},
expectedQueryInput: &DBQuery{
Limit: 25,
Expand All @@ -248,6 +252,22 @@ func TestQuery(t *testing.T) {
Select: detailedFields,
},
},
{
name: "select with details",
parseInput: url.Values{
"sort": []string{"+updated_at", "name", "-created_at"},
},
configInput: &Config{
Model: &model{},
OnlySelectNonDetailedFields: false,
},
expectedQueryInput: &DBQuery{
Limit: 25,
Offset: 0,
Sort: "name desc",
Select: "",
},
},
{
name: "select with details by default",
parseInput: url.Values{
Expand All @@ -260,11 +280,11 @@ func TestQuery(t *testing.T) {
Limit: 25,
Offset: 0,
Sort: "name desc",
Select: detailedFields,
Select: "",
},
},
{
name: "select with without details",
name: "select without details",
parseInput: url.Values{
"sort": []string{"+updated_at", "name", "-created_at"},
},
Expand Down

0 comments on commit a10697c

Please sign in to comment.