Skip to content

Commit

Permalink
feat: added NOT IN filter
Browse files Browse the repository at this point in the history
Signed-off-by: Geoffrey Bauduin <geoffrey.bauduin@ovhcloud.com>
  • Loading branch information
geoffreybauduin committed Feb 14, 2020
1 parent 93d25b6 commit 478169a
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 9 deletions.
17 changes: 17 additions & 0 deletions filterapply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,20 @@ func TestFilterApplier_Distinct(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, 1, len(models))
}

func TestFilterApplier_ApplyNotIn(t *testing.T) {
killDb, err := testdata.SetupTestDatabase("test")
defer killDb()
assert.Nil(t, err)
dbp, err := yaorm.NewDBProvider(context.TODO(), "test")
assert.Nil(t, err)
category := &testdata.Category{Name: "category"}
saveModel(t, dbp, category)
category2 := &testdata.Category{Name: "category2"}
saveModel(t, dbp, category2)

models, err := yaorm.GenericSelectAll(dbp, testdata.NewCategoryFilter().ID(yaormfilter.NotIn(category2.ID)))
assert.Nil(t, err)
assert.Len(t, models, 1)
assert.Equal(t, models[0].(*testdata.Category).ID, category.ID)
}
10 changes: 10 additions & 0 deletions yaormfilter/boolfilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ func (f *BoolFilter) In(values ...interface{}) ValueFilter {
return f
}

// NotIn adds a NOT IN filter
func (f *BoolFilter) NotIn(values ...interface{}) ValueFilter {
interfaceValues := []interface{}{}
for _, v := range values {
interfaceValues = append(interfaceValues, f.getValue(v))
}
f.notIn(interfaceValues)
return f
}

// Lt is not applicable on bool
func (f *BoolFilter) Lt(v interface{}) ValueFilter {
return f
Expand Down
5 changes: 5 additions & 0 deletions yaormfilter/datefilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ func (f *DateFilter) In(values ...interface{}) ValueFilter {
return f
}

// NotIn adds a NOT IN filter (not implemented)
func (f *DateFilter) NotIn(values ...interface{}) ValueFilter {
return f
}

// Lt adds a < filter
func (f *DateFilter) Lt(v interface{}) ValueFilter {
f.lt(f.getValue(v))
Expand Down
10 changes: 10 additions & 0 deletions yaormfilter/int64filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ func (f *Int64Filter) In(values ...interface{}) ValueFilter {
return f
}

// NotIn adds a NOT IN filter
func (f *Int64Filter) NotIn(values ...interface{}) ValueFilter {
interfaceValues := []interface{}{}
for _, v := range values {
interfaceValues = append(interfaceValues, f.getValue(v))
}
f.notIn(interfaceValues)
return f
}

// Lt adds a < filter
func (f *Int64Filter) Lt(v interface{}) ValueFilter {
f.lt(f.getValue(v))
Expand Down
5 changes: 5 additions & 0 deletions yaormfilter/nilfilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ func (f *NilFilter) In(values ...interface{}) ValueFilter {
return f
}

// NotIn adds a NOT IN filter (not implemented)
func (f *NilFilter) NotIn(values ...interface{}) ValueFilter {
return f
}

// Lt adds a < filter
func (f *NilFilter) Lt(v interface{}) ValueFilter {
return f
Expand Down
19 changes: 17 additions & 2 deletions yaormfilter/stringfilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ func (f *StringFilter) Nil(v bool) ValueFilter {
return f
}

// In adds a IN filter (not implemented)
// In adds a IN filter
func (f *StringFilter) In(values ...interface{}) ValueFilter {
interfaceValues := []interface{}{}
for _, v := range values {
underlyingValue := tools.GetNonPtrValue(v)
// make sure we have an int64
// make sure we have a string
if underlyingValue.Kind() != reflect.String {
panic("Value in StringFilter is not an string")
}
Expand All @@ -69,6 +69,21 @@ func (f *StringFilter) In(values ...interface{}) ValueFilter {
return f
}

// NotIn adds a NOT IN filter
func (f *StringFilter) NotIn(values ...interface{}) ValueFilter {
interfaceValues := []interface{}{}
for _, v := range values {
underlyingValue := tools.GetNonPtrValue(v)
// make sure we have a string
if underlyingValue.Kind() != reflect.String {
panic("Value in StringFilter is not an string")
}
interfaceValues = append(interfaceValues, underlyingValue.Interface())
}
f.notIn(interfaceValues)
return f
}

// Lt adds a < filter
func (f *StringFilter) Lt(v interface{}) ValueFilter {
return f
Expand Down
50 changes: 43 additions & 7 deletions yaormfilter/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,17 @@ func NotEquals(v interface{}) ValueFilter {

// In returns the correct filter according to the value sent
func In(values ...interface{}) ValueFilter {
var t reflect.Type
if tools.GetNonPtrValue(values).Len() == 0 {
return nil
}
t := tools.GetNonPtrValue(values[0]).Type()
for idx, v := range values {
underlyingValue := tools.GetNonPtrValue(v)
if idx == 0 {
t = underlyingValue.Type()
} else {
if underlyingValue.Type() != t {
panic(fmt.Errorf("Inconsistent values sent, got types: %+v and %+v", t, underlyingValue.Type()))
}
continue
}
underlyingValue := tools.GetNonPtrValue(v)
if underlyingValue.Type() != t {
panic(fmt.Errorf("Inconsistent values sent, got types: %+v and %+v", t, underlyingValue.Type()))
}
}
switch t.Kind() {
Expand Down Expand Up @@ -153,3 +152,40 @@ func Gte(v interface{}) ValueFilter {
}
panic(fmt.Errorf("Unknown type: %+v for value %+v in Gte filter", underlyingValue.Kind(), v))
}

// NotIn returns the correct filter according to the value sent
func NotIn(values ...interface{}) ValueFilter {
if tools.GetNonPtrValue(values).Len() == 0 {
return nil
}
t := tools.GetNonPtrValue(values[0]).Type()
for idx, v := range values {
if idx == 0 {
continue
}
underlyingValue := tools.GetNonPtrValue(v)
if underlyingValue.Type() != t {
panic(fmt.Errorf("Inconsistent values sent, got types: %+v and %+v", t, underlyingValue.Type()))
}
}
switch t.Kind() {
case reflect.Int64:
return NewInt64Filter().NotIn(values...)
case reflect.String:
return NewStringFilter().NotIn(values...)
case reflect.Bool:
return NewBoolFilter().NotIn(values...)
case reflect.Slice:
// if we receive a slice, we want to go through all the slices received an concat them inside one
data := []interface{}{}
for _, v := range values {
underlyingValue := tools.GetNonPtrValue(v)
for i := 0; i < underlyingValue.Len(); i++ {
cell := tools.GetNonPtrValue(underlyingValue.Index(i).Interface())
data = append(data, cell.Interface())
}
}
return NotIn(data...)
}
panic(fmt.Errorf("Unknown type: %v inside In filter", t.Kind()))
}
8 changes: 8 additions & 0 deletions yaormfilter/tools_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ func TestIn(t *testing.T) {
assert.IsType(t, &yaormfilter.BoolFilter{}, yaormfilter.In([]bool{true, false}))
}

func TestNotIn(t *testing.T) {
assert.IsType(t, &yaormfilter.StringFilter{}, yaormfilter.NotIn("abcdef", "bcderzzer"))
assert.IsType(t, &yaormfilter.Int64Filter{}, yaormfilter.NotIn(int64(12), int64(15)))
assert.IsType(t, &yaormfilter.StringFilter{}, yaormfilter.NotIn([]string{"abcdef", "bcderzzer"}))
assert.IsType(t, &yaormfilter.Int64Filter{}, yaormfilter.NotIn([]int64{int64(12), int64(15)}))
assert.IsType(t, &yaormfilter.BoolFilter{}, yaormfilter.NotIn([]bool{true, false}))
}

func TestLike(t *testing.T) {
assert.IsType(t, &yaormfilter.StringFilter{}, yaormfilter.Like("abcdef%"))
}
Expand Down
14 changes: 14 additions & 0 deletions yaormfilter/valuefilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type ValueFilter interface {
IsEquality() bool
GetEquality() interface{}
In(v ...interface{}) ValueFilter
NotIn(v ...interface{}) ValueFilter
}

type valuefilterimpl struct {
Expand All @@ -29,6 +30,8 @@ type valuefilterimpl struct {
shouldLike bool
in_ []interface{}
shouldIn bool
notIn_ []interface{}
shouldNotIn bool
lt_ interface{}
shouldLt bool
lte_ interface{}
Expand Down Expand Up @@ -86,6 +89,12 @@ func (f *valuefilterimpl) in(e []interface{}) *valuefilterimpl {
return f
}

func (f *valuefilterimpl) notIn(e []interface{}) *valuefilterimpl {
f.notIn_ = e
f.shouldNotIn = true
return f
}

func (f *valuefilterimpl) lte(e interface{}) *valuefilterimpl {
f.lte_ = e
f.shouldLte = true
Expand Down Expand Up @@ -143,6 +152,11 @@ func (f *valuefilterimpl) Apply(statement squirrel.SelectBuilder, tableName, fie
squirrel.Eq{computedField: f.in_},
)
}
if f.shouldNotIn {
statement = statement.Where(
squirrel.NotEq{computedField: f.notIn_},
)
}
if f.shouldLt {
statement = statement.Where(
squirrel.Lt{computedField: f.lt_},
Expand Down

0 comments on commit 478169a

Please sign in to comment.