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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
|
package mapr
import (
"errors"
"fmt"
"strconv"
"strings"
"github.com/mimecast/dtail/internal/io/dlog"
)
// QueryOperation determines the mapreduce operation.
type QueryOperation int
// The possible mapreduce operation.s
const (
UndefQueryOperation QueryOperation = iota
StringEq QueryOperation = iota
StringNe QueryOperation = iota
StringContains QueryOperation = iota
StringNotContains QueryOperation = iota
StringHasPrefix QueryOperation = iota
StringNotHasPrefix QueryOperation = iota
StringHasSuffix QueryOperation = iota
StringNotHasSuffix QueryOperation = iota
FloatOperation QueryOperation = iota
FloatEq QueryOperation = iota
FloatNe QueryOperation = iota
FloatLt QueryOperation = iota
FloatLe QueryOperation = iota
FloatGt QueryOperation = iota
FloatGe QueryOperation = iota
)
// Represent a parsed "where" clause, used by mapr.Query
type whereCondition struct {
lType fieldType
lString string
lFloat float64
Operation QueryOperation
rType fieldType
rString string
rFloat float64
}
func (wc *whereCondition) String() string {
return fmt.Sprintf("whereCondition(Operation:%v,lString:%s,lFloat:%v,"+
"lType:%s,rString:%s,rFloat:%v,rType:%s)",
wc.Operation, wc.lString, wc.lFloat, wc.lType.String(), wc.rString,
wc.rFloat, wc.rType.String())
}
func makeWhereConditions(tokens []token) (where []whereCondition, err error) {
// Helper to parse a where condition.
parse := func(tokens []token) (whereCondition, []token, error) {
var wc whereCondition
if len(tokens) < 3 {
return wc, nil, errors.New(invalidQuery + "Not enough arguments in 'where' clause")
}
whereOp := strings.ToLower(tokens[1].str)
switch whereOp {
case "==":
wc.Operation = FloatEq
case "!=":
wc.Operation = FloatNe
case "<":
wc.Operation = FloatLt
case "<=":
wc.Operation = FloatLe
case "=<":
wc.Operation = FloatLe
case ">":
wc.Operation = FloatGt
case ">=":
wc.Operation = FloatGe
case "=>":
wc.Operation = FloatGe
case "eq":
wc.Operation = StringEq
case "ne":
wc.Operation = StringNe
case "contains":
wc.Operation = StringContains
case "lacks":
fallthrough
case "ncontains":
wc.Operation = StringNotContains
case "hasprefix":
wc.Operation = StringHasPrefix
case "nhasprefix":
wc.Operation = StringNotHasPrefix
case "hassuffix":
wc.Operation = StringHasSuffix
case "nhassuffix":
wc.Operation = StringNotHasSuffix
default:
return wc, nil, errors.New(invalidQuery +
"Unknown operation in 'where' clause: " + whereOp)
}
var err error
tokens, err = wc.fill(tokens)
return wc, tokens, err
}
// Consume all where conditions.
for len(tokens) > 0 {
var wc whereCondition
var err error
wc, tokens, err = parse(tokens)
if err != nil {
return nil, err
}
where = append(where, wc)
tokens = tokensConsumeOptional(tokens, "and")
}
return
}
// Fill a where condition.
func (wc *whereCondition) fill(tokens []token) ([]token, error) {
wc.lString = tokens[0].str
wc.rString = tokens[2].str
if wc.Operation > FloatOperation {
if !tokens[0].isBareword {
return nil, errors.New(invalidQuery +
"Expected bareword at 'where' clause's lValue: " + tokens[0].str)
}
if f, err := strconv.ParseFloat(wc.lString, 64); err == nil {
wc.lFloat = f
wc.lType = Float
} else {
wc.lType = Field
}
if !tokens[2].isBareword {
return nil, errors.New(invalidQuery +
"Expected bareword at 'where' clause's rValue: " + tokens[2].str)
}
if f, err := strconv.ParseFloat(wc.rString, 64); err == nil {
wc.rFloat = f
wc.rType = Float
} else {
wc.rType = Field
}
return tokens[3:], nil
}
if tokens[0].isBareword {
wc.lType = Field
} else {
wc.lType = String
}
if tokens[2].isBareword {
wc.rType = Field
} else {
wc.rType = String
}
return tokens[3:], nil
}
func (wc *whereCondition) floatClause(lValue float64, rValue float64) bool {
switch wc.Operation {
case FloatEq:
return lValue == rValue
case FloatNe:
return lValue != rValue
case FloatLt:
return lValue < rValue
case FloatLe:
return lValue <= rValue
case FloatGt:
return lValue > rValue
case FloatGe:
return lValue >= rValue
default:
dlog.Common.Error("Unknown float operation", lValue, wc.Operation, rValue)
}
return false
}
func (wc *whereCondition) stringClause(lValue string, rValue string) bool {
switch wc.Operation {
case StringEq:
return lValue == rValue
case StringNe:
return lValue != rValue
case StringContains:
return strings.Contains(lValue, rValue)
case StringNotContains:
return !strings.Contains(lValue, rValue)
case StringHasPrefix:
return strings.HasPrefix(lValue, rValue)
case StringNotHasPrefix:
return !strings.HasPrefix(lValue, rValue)
case StringHasSuffix:
return strings.HasSuffix(lValue, rValue)
case StringNotHasSuffix:
return !strings.HasSuffix(lValue, rValue)
default:
dlog.Common.Error("Unknown string operation", lValue, wc.Operation, rValue)
}
return false
}
|