moved filter logic into query

This commit is contained in:
ston1th 2017-11-12 14:33:39 +01:00
commit 3b458f6ff1
4 changed files with 94 additions and 70 deletions

View file

@ -51,7 +51,7 @@ For nested structs:
Supported logical matching:
* `AND` - all statements have to match
* `AND` - all statements have to match (if no logic is provided, this is the default)
* `OR` - only one statement has to match
* `NOT` - none of the statements have to match
@ -93,7 +93,7 @@ var data = Filter{
### Explicit match
```
filter.NewQuery("Bool:true", filter.OR).Filter(&data)
filter.NewQuery("OR Bool:true").Filter(&data)
data = [{str1 42 true {s1 {1}}} {str2 42 true {s2 {2}}}]
```
@ -104,7 +104,7 @@ Wildcard operator: `*`
To match the `'*'` character use: `**`
```
filter.NewQuery("String:s*", filter.OR).Filter(&data)
filter.NewQuery("OR String:s*").Filter(&data)
data = [{str1 42 true {s1 {1}}} {str3 42 false {s3 {3}}}]
```
@ -112,7 +112,7 @@ data = [{str1 42 true {s1 {1}}} {str3 42 false {s3 {3}}}]
### Nested struct match
```
filter.NewQuery("Struct.Struct.Int:3", filter.OR).Filter(&data)
filter.NewQuery("OR Struct.Struct.Int:3").Filter(&data)
data = [{str3 42 false {s3 {3}}}]
```
@ -120,7 +120,7 @@ data = [{str3 42 false {s3 {3}}}]
### Multiple matches
```
filter.NewQuery("Struct.String:*2 Bool:true", filter.AND).Filter(&data)
filter.NewQuery("Struct.String:*2 Bool:true").Filter(&data)
data = [{2str 42 true {s2 {2}}}]
```
@ -132,7 +132,7 @@ The `GenericFilter` does not support sorting.
#### Sort
```
filter.NewQuery("sort:Bool:desc", filter.AND).Filter(&data)
filter.NewQuery("sort:Bool:desc").Filter(&data)
data = [{str1 42 false {s1 {1}}} {2str 42 true {s2 {2}}} {str3 42 true {s3 {3}}}]
```
@ -140,7 +140,7 @@ data = [{str1 42 false {s1 {1}}} {2str 42 true {s2 {2}}} {str3 42 true {s3 {3}}}
#### Sort and Filter
```
filter.NewQuery("sort:Bool:desc String:str*", filter.AND).Filter(&data)
filter.NewQuery("sort:Bool:desc String:str*").Filter(&data)
data = [{str1 42 false {s1 {1}}} {str3 42 true {s3 {3}}}]
```

View file

@ -43,22 +43,28 @@ var data = Filter{
}
func main() {
f, _ := filter.NewQuery("String:s*", filter.AND)
f, err := filter.NewQuery("AND String:s*")
if err != nil {
fmt.Println(err)
}
d := f.GenericFilter(data)
if d != nil {
for _, v := range d.(Filter) {
fmt.Println("Generic:", v)
}
}
f, _ = filter.NewQuery("Struct.Struct.Int:1 Struct.Struct.Int:2 String::", filter.OR)
err := f.Filter(&data)
f, err = filter.NewQuery("OR Struct.Struct.Int:1 Struct.Struct.Int:2 String::")
if err != nil {
fmt.Println(err)
}
err = f.Filter(&data)
if err != nil {
fmt.Println(err)
}
for _, v := range data {
fmt.Println("Interface:", v)
}
s, err := filter.NewQuery("sort:Bool:desc", filter.AND)
s, err := filter.NewQuery("sort:Bool:desc")
if err != nil {
fmt.Println(err)
}

View file

@ -40,27 +40,41 @@ const (
asc = "asc"
)
// Logic is the filter logic
type Logic int
type logic int
const (
// AND all statements have to match
AND Logic = iota
// OR only one statement has to match
OR
// NOT none of the statements have to match
NOT
undefined logic = iota
and
or
not
)
func parseLogic(s string) (l logic, ok bool) {
ok = true
switch s {
case "AND":
l = and
case "OR":
l = or
case "NOT":
l = not
default:
ok = false
l = and
}
return
}
// Query is the filter query
type Query struct {
q string
m []match
l Logic
l logic
s sorter
}
// NewQuery parses the filter/sort query and match logic
// If the query does not start with a filter logic (AND, OR, NOT), it defaults to AND.
// Examples:
// Explicit match: <struct field>:<struct value>
// Wildcard match (use ** to match the '*' character):
@ -80,11 +94,8 @@ type Query struct {
// sort:<struct field>:asc
// sort:<struct field>:desc
// sort:<struct field>.<struct field>:desc
func NewQuery(s string, l Logic) (q *Query, err error) {
if l > 2 || l < 0 {
l = AND
}
q = &Query{q: s, l: l}
func NewQuery(s string) (q *Query, err error) {
q = &Query{q: s}
if s == "" {
err = errors.New("query is empty")
return
@ -95,6 +106,13 @@ func NewQuery(s string, l Logic) (q *Query, err error) {
if v == "" {
continue
}
if q.l == undefined {
l, ok := parseLogic(v)
q.l = l
if ok {
continue
}
}
if !q.s.enabled {
s := strings.SplitN(v, ":", 3)
if len(s) == 3 {
@ -125,19 +143,19 @@ func NewQuery(s string, l Logic) (q *Query, err error) {
err = errors.New("invalid match: " + v)
return
}
if l == AND {
if q.l == and {
if _, ok := qm[m[0]]; ok {
err = errors.New("field for AND matching used more than once: " + m[0])
return
}
qm[m[0]] = struct{}{}
}
mf := strings.Split(m[0], ".")
if err = checkFields(mf, m[0]); err != nil {
f := strings.Split(m[0], ".")
if err = checkFields(f, m[0]); err != nil {
return
}
q.m = append(q.m, match{
mf,
f,
matchFunc(m[1]),
boolFunc(m[1]),
})
@ -145,8 +163,8 @@ func NewQuery(s string, l Logic) (q *Query, err error) {
if len(q.m) == 0 {
q.s.only = true
}
if len(q.m) == 1 && q.l != NOT {
q.l = OR
if len(q.m) == 1 && q.l != not {
q.l = or
}
return
}
@ -158,16 +176,16 @@ func (q *Query) String() string {
func (q *Query) check(oks []bool) (b bool) {
b = true
switch q.l {
case AND:
case and:
for _, ok := range oks {
if !ok {
b = false
return
}
}
case OR:
case or:
b = false
case NOT:
case not:
for _, ok := range oks {
if ok {
b = false
@ -190,7 +208,7 @@ func (q *Query) match(v reflect.Value) (ok bool) {
oks []bool
match bool
)
if q.l != OR {
if q.l != or {
oks = make([]bool, ql)
}
for i, qv := range q.m {
@ -212,7 +230,7 @@ func (q *Query) match(v reflect.Value) (ok bool) {
default:
match = qv.mf(s)
}
if q.l == OR {
if q.l == or {
if match {
return true
}

View file

@ -55,17 +55,17 @@ var (
)
func TestNewQuery(t *testing.T) {
_, err := NewQuery("String:1 String:2", AND)
_, err := NewQuery("String:1 String:2")
if err == nil {
t.Error("multiple AND fields")
}
_, err = NewQuery("String:1 sdcs Int:1", AND)
_, err = NewQuery("String:1 sdcs Int:1")
if err == nil {
t.Error("invalid match")
}
_, err = NewQuery("String:1 sort:Bool:ASC", AND)
_, err = NewQuery("String:1 sort:Bool:ASC")
if err == nil {
t.Error("invalid sort")
}
@ -78,7 +78,7 @@ const (
func TestFilter(t *testing.T) {
f1 := makefilter(data)
q1, err := NewQuery(filter1, AND)
q1, err := NewQuery("AND " + filter1)
if err != nil {
t.Error(err)
}
@ -94,7 +94,7 @@ func TestFilter(t *testing.T) {
}
f2 := makefilter(data)
q2, err := NewQuery(filter2, OR)
q2, err := NewQuery("OR " + filter2)
if err != nil {
t.Error(err)
}
@ -110,7 +110,7 @@ func TestFilter(t *testing.T) {
}
f3 := makefilter(data)
q3, err := NewQuery(filter2, NOT)
q3, err := NewQuery("NOT " + filter2)
if err != nil {
t.Error(err)
}
@ -130,7 +130,7 @@ const (
func TestSortFilter(t *testing.T) {
f1 := makefilter(benchData)
q1, err := NewQuery(sort1, AND)
q1, err := NewQuery(sort1)
if err != nil {
t.Error(err)
}
@ -146,7 +146,7 @@ func TestSortFilter(t *testing.T) {
}
f2 := makefilter(benchData)
q2, err := NewQuery(sort2, OR)
q2, err := NewQuery("OR " + sort2)
if err != nil {
t.Error(err)
}
@ -162,7 +162,7 @@ func TestSortFilter(t *testing.T) {
}
f3 := makefilter(benchData)
q3, err := NewQuery(sort2, NOT)
q3, err := NewQuery("NOT " + sort2)
if err != nil {
t.Error(err)
}
@ -179,7 +179,7 @@ func TestSortFilter(t *testing.T) {
}
func TestGenericFilter(t *testing.T) {
q1, err := NewQuery(filter1, AND)
q1, err := NewQuery(filter1)
if err != nil {
t.Error(err)
}
@ -198,7 +198,7 @@ func TestGenericFilter(t *testing.T) {
t.Error("f1[1] != data[1]")
}
q2, err := NewQuery(filter2, OR)
q2, err := NewQuery("OR " + filter2)
if err != nil {
t.Error(err)
}
@ -217,7 +217,7 @@ func TestGenericFilter(t *testing.T) {
t.Error("f2[1] != data[2]")
}
q3, err := NewQuery(filter2, NOT)
q3, err := NewQuery("NOT " + filter2)
if err != nil {
t.Error(err)
}
@ -235,67 +235,67 @@ func TestGenericFilter(t *testing.T) {
}
const (
simple = "Bool:true"
simple = "OR Bool:true"
comp = "String:*st* Bool:false Struct.Struct.Int:1"
compOr = "Int:47 Bool:false Struct.String:s1*"
sortOnly = "sort:Int:desc"
simpleSort = "sort:Int:desc Bool:true"
compOr = "OR Int:47 Bool:false Struct.String:s1*"
sortOnly = "OR sort:Int:desc"
simpleSort = "OR sort:Int:desc Bool:true"
compSort = "sort:Int:desc String:*st* Bool:false Struct.Struct.Int:1"
compOrSort = "sort:Int:desc Int:47 Bool:false Struct.String:s1*"
compOrSort = "OR sort:Int:desc Int:47 Bool:false Struct.String:s1*"
)
func BenchmarkNewQuerySimple(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
NewQuery(simple, OR)
NewQuery(simple)
}
}
func BenchmarkNewQueryComplex(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
NewQuery(comp, AND)
NewQuery(comp)
}
}
func BenchmarkNewQueryComplexOr(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
NewQuery(compOr, OR)
NewQuery(compOr)
}
}
func BenchmarkNewQuerySort(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
NewQuery(sortOnly, OR)
NewQuery(sortOnly)
}
}
func BenchmarkNewQuerySortSimple(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
NewQuery(simpleSort, OR)
NewQuery(simpleSort)
}
}
func BenchmarkNewQuerySortComplex(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
NewQuery(compSort, AND)
NewQuery(compSort)
}
}
func BenchmarkNewQuerySortComplexOr(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
NewQuery(compOrSort, OR)
NewQuery(compOrSort)
}
}
func BenchmarkFilterSimple(b *testing.B) {
b.ReportAllocs()
q, err := NewQuery(simple, OR)
q, err := NewQuery(simple)
if err != nil {
b.Error(err)
}
@ -307,7 +307,7 @@ func BenchmarkFilterSimple(b *testing.B) {
func BenchmarkFilterComplex(b *testing.B) {
b.ReportAllocs()
q, err := NewQuery(comp, AND)
q, err := NewQuery(comp)
if err != nil {
b.Error(err)
}
@ -319,7 +319,7 @@ func BenchmarkFilterComplex(b *testing.B) {
func BenchmarkFilterComplexOr(b *testing.B) {
b.ReportAllocs()
q, err := NewQuery(compOr, OR)
q, err := NewQuery(compOr)
if err != nil {
b.Error(err)
}
@ -331,7 +331,7 @@ func BenchmarkFilterComplexOr(b *testing.B) {
func BenchmarkSort(b *testing.B) {
b.ReportAllocs()
q, err := NewQuery(sortOnly, OR)
q, err := NewQuery(sortOnly)
if err != nil {
b.Error(err)
}
@ -343,7 +343,7 @@ func BenchmarkSort(b *testing.B) {
func BenchmarkSortFilterSimple(b *testing.B) {
b.ReportAllocs()
q, err := NewQuery(simpleSort, OR)
q, err := NewQuery(simpleSort)
if err != nil {
b.Error(err)
}
@ -355,7 +355,7 @@ func BenchmarkSortFilterSimple(b *testing.B) {
func BenchmarkSortFilterComplex(b *testing.B) {
b.ReportAllocs()
q, err := NewQuery(compSort, AND)
q, err := NewQuery(compSort)
if err != nil {
b.Error(err)
}
@ -367,7 +367,7 @@ func BenchmarkSortFilterComplex(b *testing.B) {
func BenchmarkSortFilterComplexOr(b *testing.B) {
b.ReportAllocs()
q, err := NewQuery(compOrSort, OR)
q, err := NewQuery(compOrSort)
if err != nil {
b.Error(err)
}
@ -379,7 +379,7 @@ func BenchmarkSortFilterComplexOr(b *testing.B) {
func BenchmarkGenericFilterSimple(b *testing.B) {
b.ReportAllocs()
q, err := NewQuery(simple, OR)
q, err := NewQuery(simple)
if err != nil {
b.Error(err)
}
@ -390,7 +390,7 @@ func BenchmarkGenericFilterSimple(b *testing.B) {
func BenchmarkGenericFilterComplex(b *testing.B) {
b.ReportAllocs()
q, err := NewQuery(comp, AND)
q, err := NewQuery(comp)
if err != nil {
b.Error(err)
}
@ -401,7 +401,7 @@ func BenchmarkGenericFilterComplex(b *testing.B) {
func BenchmarkGenericFilterComplexOr(b *testing.B) {
b.ReportAllocs()
q, err := NewQuery(compOr, OR)
q, err := NewQuery(compOr)
if err != nil {
b.Error(err)
}