/
filter.go
175 lines (155 loc) · 3.95 KB
/
filter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
package main
import (
"strings"
"strconv"
"fmt"
"rand"
"log"
"os"
)
func PassesAllFilters(line JSONData, filters []Expression) (result bool, err os.Error) {
for _, filter := range filters {
passes, err := filter.Evaluate(line)
if err != nil {
return false, err
}
passes, ok := passes.(bool)
if !ok {
return false, fmt.Errorf("Expected a boolean for %T, got %T", filter, passes)
}
if !passes.(bool) {
return false, nil
}
}
return true, nil
}
/*
* RandomSample(float64)
*
* Returns a boolean true with probability given by the first and only argument.
*/
type RandomSample struct {
rate Expression
}
func (f *RandomSample) Setup(fname string, args []Expression) (err os.Error) {
if len(args) != 1 {
return fmt.Errorf("RandomSample takes a single argument, a float between 0 and 1")
}
f.rate = args[0]
return
}
func (f *RandomSample) Evaluate(data JSONData) (result interface{}, err os.Error) {
sampleRate, err := f.rate.Evaluate(data)
if err != nil {
return false, err
}
if sampleRate, ok := sampleRate.(float64); !ok {
return false, fmt.Errorf("RandomSample takes a single argument, a float between 0 and 1. Got %v", sampleRate)
}
return rand.Float64() < sampleRate.(float64), nil
}
func (f *RandomSample) String() string {
return fmt.Sprintf("RandomSample(%v)", f.rate)
}
/*
* EveryNth(int)
*
* Returns a boolean true every nth time it's evaluated.
*/
type EveryNth struct {
rate Expression
counter int
}
func (f *EveryNth) Setup(fname string, args []Expression) (err os.Error) {
if len(args) != 1 {
return fmt.Errorf("RandomSample takes a single argument, an int between 0 and 1")
}
f.rate = args[0]
return
}
func (f *EveryNth) Evaluate(data JSONData) (result interface{}, err os.Error) {
rate, err := f.rate.Evaluate(data)
if err != nil {
return false, err
}
if rate, ok := rate.(int); !ok {
return false, fmt.Errorf("RandomSample takes a single argument, a positive integer. Got %v", rate)
}
f.counter++
if f.counter >= rate.(int) {
f.counter = 0
return true, nil
}
return false, nil
}
func (f *EveryNth) String() string {
return fmt.Sprintf("EveryNth(%v)", f.rate)
}
/*
* Comparison Filter
*
* XXX: Broken with the new parser. They need to be rewritten to the Expression interface.
*/
type ComparisonFilter struct {
key string
operator func(a, b interface{}) bool
rhs interface{}
}
var comparisonOperators = map[string](func(a, b interface{}) bool){
"==": func(a, b interface{}) bool { return a == b },
"!=": func(a, b interface{}) bool { return a != b },
">=": func(a, b interface{}) bool { return a.(float64) >= b.(float64) },
"<=": func(a, b interface{}) bool { return a.(float64) <= b.(float64) },
"<": func(a, b interface{}) bool { return a.(float64) < b.(float64) },
">": func(a, b interface{}) bool { return a.(float64) > b.(float64) },
}
func NewComparisonFilter(query string) (f *ComparisonFilter, ok bool) {
f = new(ComparisonFilter)
ok, applicable, msg := f.Parse(query)
if applicable {
log.Print(msg)
} else {
ok = false
}
return
}
func (f *ComparisonFilter) Parse(query string) (ok bool, applicable bool, msg string) {
fields := strings.Split(query, " ")
applicable = true
ok = true
if len(fields) == 3 {
key := fields[0]
opStr := fields[1]
rhs := fields[2]
// Strip off quotes if there's a matching pair (e.g. allows checks for == "")
if len(rhs) >= 2 && strings.HasPrefix(rhs, "\"") && strings.HasSuffix(rhs, "\"") {
rhs = rhs[1 : len(rhs)-1]
}
operator, operatorPresent := comparisonOperators[opStr]
if !operatorPresent {
ok = false
msg = "%v is not a valid operator."
return
} else {
f.operator = operator
}
rhsFloat, err := strconv.Atof64(rhs)
if err == nil {
f.rhs = rhsFloat
} else {
f.rhs = rhs
}
f.key = key
} else {
applicable = false
ok = false
}
return
}
func (f ComparisonFilter) Predicate(line JSONData) bool {
lhs, ok := GetDeep(f.key, line)
if ok {
return f.operator(lhs, f.rhs)
}
return false
}