summaryrefslogtreecommitdiff
path: root/internal/mapr/wherecondition.go
blob: ff1b489ef8dbd2949b3f0523957c2758d55d2f4c (plain)
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
package mapr

import (
	"errors"
	"fmt"
	"strconv"
	"strings"

	"github.com/mimecast/dtail/internal/io/logger"
)

// 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
	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) {
	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":
			wc.Operation = StringNotContains
		default:
			return wc, nil, errors.New(invalidQuery + "Unknown operation in 'where' clause: " + whereOp)
		}

		wc.lString = tokens[0].str
		wc.rString = tokens[2].str

		if wc.Operation > FloatOperation {
			if !tokens[0].isBareword {
				return wc, 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 wc, 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 wc, 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 wc, tokens[3:], nil
	}

	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
}

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:
		logger.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)
	default:
		logger.Error("Unknown string operation", lValue, wc.Operation, rValue)
	}
	return false
}