added sort support
This commit is contained in:
parent
558d6ae7d0
commit
1674abae73
5 changed files with 377 additions and 66 deletions
93
filter.go
93
filter.go
|
|
@ -6,6 +6,7 @@ package filter
|
|||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
|
@ -27,25 +28,39 @@ type match struct {
|
|||
bf func(bool) bool
|
||||
}
|
||||
|
||||
type sorter struct {
|
||||
enabled bool
|
||||
only bool
|
||||
desc bool
|
||||
fields []string
|
||||
}
|
||||
|
||||
const (
|
||||
desc = "desc"
|
||||
asc = "asc"
|
||||
)
|
||||
|
||||
// Logic is the filter logic
|
||||
type Logic int
|
||||
|
||||
const (
|
||||
// AND all of the queries have to match
|
||||
// AND all statements have to match
|
||||
AND Logic = iota
|
||||
// OR only one query has to match
|
||||
// OR only one statement has to match
|
||||
OR
|
||||
// NOT none of the queries have to match
|
||||
// NOT none of the statements have to match
|
||||
NOT
|
||||
)
|
||||
|
||||
// Query is the filter query
|
||||
type Query struct {
|
||||
q string
|
||||
m []match
|
||||
l Logic
|
||||
s sorter
|
||||
}
|
||||
|
||||
// NewQuery parses the filter query and match logic
|
||||
// NewQuery parses the filter/sort query and match logic
|
||||
// Examples:
|
||||
// Explicit match: <struct field>:<struct value>
|
||||
// Wildcard match (use ** to match the '*' character):
|
||||
|
|
@ -55,14 +70,21 @@ type Query struct {
|
|||
// Nested structs:
|
||||
// <struct field>.<struct field>:<struct value>
|
||||
// Multiple matches:
|
||||
// Multiple statemenmts are seperated by a single whitespace character: ' '
|
||||
// <struct field>:<struct value> <struct field>:<struct value>*
|
||||
// Supported data types are: string, int, float, bool
|
||||
// Bool does not support the wildcard operator
|
||||
// Sorting:
|
||||
// The sort statement consists of the literal string 'sort', the field name (nesting like above is supported) and the literal strings 'asc' or 'desc'.
|
||||
// Examples:
|
||||
// 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{l: l}
|
||||
q = &Query{q: s, l: l}
|
||||
if s == "" {
|
||||
err = errors.New("query is empty")
|
||||
return
|
||||
|
|
@ -73,6 +95,28 @@ func NewQuery(s string, l Logic) (q *Query, err error) {
|
|||
if v == "" {
|
||||
continue
|
||||
}
|
||||
if !q.s.enabled {
|
||||
s := strings.SplitN(v, ":", 3)
|
||||
if len(s) == 3 {
|
||||
if s[0] == "sort" {
|
||||
switch s[2] {
|
||||
case asc, desc:
|
||||
q.s.fields = strings.Split(s[1], ".")
|
||||
if len(q.s.fields) > 0 {
|
||||
if s[2] == desc {
|
||||
q.s.desc = true
|
||||
}
|
||||
q.s.enabled = true
|
||||
}
|
||||
continue
|
||||
default:
|
||||
err = errors.New("invalid sort: " + v)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
m := strings.SplitN(v, ":", 2)
|
||||
if len(m) != 2 {
|
||||
err = errors.New("invalid match: " + v)
|
||||
|
|
@ -91,9 +135,19 @@ func NewQuery(s string, l Logic) (q *Query, err error) {
|
|||
boolFunc(m[1]),
|
||||
})
|
||||
}
|
||||
if len(q.m) == 0 {
|
||||
q.s.only = true
|
||||
}
|
||||
if len(q.m) == 1 && q.l != NOT {
|
||||
q.l = OR
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (q *Query) String() string {
|
||||
return q.q
|
||||
}
|
||||
|
||||
func (q *Query) check(oks []bool) (b bool) {
|
||||
b = true
|
||||
switch q.l {
|
||||
|
|
@ -164,6 +218,7 @@ func (q *Query) match(v reflect.Value) (ok bool) {
|
|||
|
||||
// GenericFilter takes any slice of structs value and returns the same slice type containing only the filter matches.
|
||||
// Returns nil if interface{} is not a slice.
|
||||
// GenericFilter does not support sorting.
|
||||
func (q *Query) GenericFilter(i interface{}) interface{} {
|
||||
v := reflect.ValueOf(i)
|
||||
if v.Kind() != reflect.Slice {
|
||||
|
|
@ -179,9 +234,29 @@ func (q *Query) GenericFilter(i interface{}) interface{} {
|
|||
return ret.Interface()
|
||||
}
|
||||
|
||||
func (q Query) sort(f Filter) {
|
||||
if f.Len() == 0 {
|
||||
return
|
||||
}
|
||||
l := makeLess(f, q.s.fields)
|
||||
if q.s.desc {
|
||||
l = reverse(l)
|
||||
}
|
||||
sort.SliceStable(reflect.Indirect(reflect.ValueOf(f)).Interface(), l)
|
||||
}
|
||||
|
||||
// Filter takes a pointer to the Filter interface as an argument.
|
||||
// The original slice is modified in the filtering process.
|
||||
func (q Query) Filter(f Filter) {
|
||||
// The original slice is modified in the filtering/sorting process.
|
||||
func (q Query) Filter(f Filter) error {
|
||||
v := reflect.ValueOf(f)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
return errors.New("filter: non-pointer " + v.Type().String())
|
||||
}
|
||||
|
||||
if q.s.enabled && q.s.only {
|
||||
q.sort(f)
|
||||
return nil
|
||||
}
|
||||
count := 0
|
||||
i := 0
|
||||
for ; i < f.Len(); i++ {
|
||||
|
|
@ -198,4 +273,8 @@ func (q Query) Filter(f Filter) {
|
|||
if count > 0 {
|
||||
f.Remove(i-count, i)
|
||||
}
|
||||
if q.s.enabled {
|
||||
q.sort(f)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue