diff --git a/README.md b/README.md index ed7926c..6a2b248 100644 --- a/README.md +++ b/README.md @@ -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}}}] ``` diff --git a/cmd/main.go b/cmd/main.go index be87247..07bddea 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -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) } diff --git a/filter.go b/filter.go index ea4a54d..dc2bf94 100644 --- a/filter.go +++ b/filter.go @@ -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: : // Wildcard match (use ** to match the '*' character): @@ -80,11 +94,8 @@ type Query struct { // sort::asc // sort::desc // sort:.: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 } diff --git a/filter_test.go b/filter_test.go index 96dc2e1..6f3b340 100644 --- a/filter_test.go +++ b/filter_test.go @@ -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) }