Commit dcfc40497987d8c2eceff79446bf29135ced7b10
1 parent
edd7c4f4d7
Exists in
master
improved filtering
Showing
1 changed file
with
47 additions
and
23 deletions
Show diff stats
filtering.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "fmt" | 4 | "fmt" |
5 | "net/http" | 5 | "net/http" |
6 | "net/url" | 6 | "net/url" |
7 | "strings" | 7 | "strings" |
8 | ) | 8 | ) |
9 | 9 | ||
10 | type Filter map[string]string | 10 | type Filter map[string][]string |
11 | 11 | ||
12 | func (f *Filter) Size() int { | 12 | func (f Filter) Get(key string) (values []string, ok bool) { |
13 | return len(*f) | 13 | values, ok = f[key] |
14 | return values, ok | ||
14 | } | 15 | } |
15 | 16 | ||
16 | func (f *Filter) Valid() bool { | 17 | func (f Filter) Size() int { |
17 | return len(*f) > 0 | 18 | return len(f) |
19 | } | ||
20 | |||
21 | func (f Filter) IsNotEmpty() bool { | ||
22 | return len(f) > 0 | ||
18 | } | 23 | } |
19 | 24 | ||
20 | func (fs Filter) validate(validFilters []string) (Filter, bool) { | 25 | func (fs Filter) validate(validFilters []string) (Filter, bool) { |
21 | goodFilters := make(map[string]string) | 26 | goodFilters := make(Filter) |
22 | cnt := 0 | 27 | cnt, len := 0, 0 |
23 | len := 0 | ||
24 | for f, _ := range fs { | 28 | for f, _ := range fs { |
25 | len++ | 29 | len++ |
26 | for _, v := range validFilters { | 30 | for _, v := range validFilters { |
27 | if f == v { | 31 | if f == v { |
28 | cnt++ | 32 | cnt++ |
29 | goodFilters[f] = fs[f] | 33 | goodFilters[f] = fs[f] |
30 | } | 34 | } |
31 | } | 35 | } |
32 | } | 36 | } |
33 | 37 | ||
34 | result := true | 38 | result := true |
35 | if len > 0 && cnt == 0 { | 39 | if len > 0 && cnt == 0 { |
36 | // if no valid filters are found declare filtering request as invalid | 40 | // if no valid filters are found declare filtering request as invalid |
37 | result = false | 41 | result = false |
38 | } | 42 | } |
39 | 43 | ||
40 | return goodFilters, result | 44 | return goodFilters, result |
41 | } | 45 | } |
42 | 46 | ||
43 | // requires input in format: "param1::value1|param2::value2..." | 47 | // requires input in format: "param1::value1|param2::value2..." |
44 | func ParseFilters(req *http.Request, header string) (filters Filter) { | 48 | func ParseFilters(req *http.Request, header string) (filters Filter) { |
45 | q := req.FormValue(header) | 49 | q := req.FormValue(header) |
46 | q = strings.Trim(q, "\"") | 50 | q = strings.Trim(q, "\"") |
47 | kvp := strings.Split(q, "|") | 51 | kvp := strings.Split(q, "|") |
48 | filters = make(map[string]string, len(kvp)) | 52 | filters = make(Filter, len(kvp)) |
49 | 53 | ||
50 | for i, _ := range kvp { | 54 | for i, _ := range kvp { |
51 | kv := strings.Split(kvp[i], "::") | 55 | kv := strings.Split(kvp[i], "::") |
52 | if len(kv) == 2 { | 56 | if len(kv) == 2 { |
53 | key, _ := url.QueryUnescape(kv[0]) | 57 | key, _ := url.QueryUnescape(kv[0]) |
54 | val, _ := url.QueryUnescape(kv[1]) | 58 | |
55 | filters[key] = val | 59 | // get values (if more than 1) |
60 | vals := strings.Split(kv[1], ",") | ||
61 | for _, v := range vals { | ||
62 | u, _ := url.QueryUnescape(v) | ||
63 | filters[key] = append(filters[key], u) | ||
64 | } | ||
56 | } | 65 | } |
57 | } | 66 | } |
58 | 67 | ||
59 | return filters | 68 | return filters |
60 | } | 69 | } |
61 | 70 | ||
62 | // TODO(marko): very dodgy, needs more robustness | 71 | // TODO(marko): very dodgy, needs more robustness |
63 | func MakeFilterString(prefix string, filters Filter, validFilters []string) (res string, ok bool) { | 72 | func MakeFilterString(prefix string, filters Filter, validFilters []string) (res string, ok bool) { |
64 | if prefix != "" { | 73 | if prefix != "" { |
65 | prefix += "." | 74 | prefix += "." |
66 | } | 75 | } |
67 | if len(filters) == 0 { | 76 | |
77 | if !filters.IsNotEmpty() { | ||
68 | return "", true | 78 | return "", true |
69 | } | 79 | } |
70 | 80 | ||
71 | filters, ok = filters.validate(validFilters) | 81 | filters, ok = filters.validate(validFilters) |
72 | if !ok { | 82 | if !ok { |
73 | return "", ok | 83 | return "", false |
74 | } | 84 | } |
75 | 85 | ||
76 | symbol := "=" | 86 | for k, filter := range filters { |
77 | for k, v := range filters { | 87 | first := true |
78 | if res != "" { | 88 | symbol := "=" |
89 | |||
90 | if first { | ||
79 | res += " and " | 91 | res += " and " |
92 | first = false | ||
80 | } else { | 93 | } else { |
81 | res += " where " | 94 | res += " where " |
82 | } | 95 | } |
83 | c := string(v[0]) | ||
84 | if c == "<" || c == ">" { | ||
85 | symbol = "" // examples: >3, >=3 | ||
86 | } else { | ||
87 | symbol = "=" | ||
88 | } | ||
89 | 96 | ||
90 | res += fmt.Sprintf("%s%s %s '%s'", prefix, k, symbol, v) | 97 | res += "(" |
98 | for i, f := range filter { | ||
99 | if strings.HasPrefix(f, "<") || strings.HasPrefix(f, ">") { | ||
100 | symbol = string(f[0]) | ||
101 | f = strings.TrimLeft(f, "<>") | ||
102 | if strings.HasPrefix(f, "=") { | ||
103 | f = strings.TrimLeft(f, "=") | ||
104 | symbol += "=" | ||
105 | } | ||
106 | } | ||
107 | |||
108 | res += fmt.Sprintf("%s%s %s '%s'", prefix, k, symbol, f) | ||
109 | |||
110 | if i < len(filter)-1 { | ||
111 | res += " or " | ||
112 | } |