moved filter logic into query
This commit is contained in:
parent
15d372ac55
commit
3b458f6ff1
4 changed files with 94 additions and 70 deletions
14
README.md
14
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}}}]
|
||||
```
|
||||
|
|
|
|||
14
cmd/main.go
14
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)
|
||||
}
|
||||
|
|
|
|||
68
filter.go
68
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: <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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue