This repository has been archived by the owner on Oct 9, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 62
/
filters.go
325 lines (285 loc) · 9.42 KB
/
filters.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
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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
// Defines a common set of filters that can be applied in queries for supported databases types.
package common
import (
"context"
"fmt"
"github.com/lyft/flyteadmin/pkg/errors"
"github.com/lyft/flytestdlib/logger"
"google.golang.org/grpc/codes"
)
type FilterExpression int
// Container for arguments necessary to issue a GORM query.
type GormQueryExpr struct {
Query string
Args interface{}
}
// Complete set of filters available for database queries.
const (
Contains FilterExpression = iota
GreaterThan
GreaterThanOrEqual
LessThan
LessThanOrEqual
Equal
NotEqual
ValueIn
)
// String formats for various filter expression queries
const (
joinArgsFormat = "%s.%s"
containsQuery = "%s LIKE ?"
containsArgs = "%%%s%%"
greaterThanQuery = "%s > ?"
greaterThanOrEqualQuery = "%s >= ?"
lessThanQuery = "%s < ?"
lessThanOrEqualQuery = "%s <= ?"
equalQuery = "%s = ?"
notEqualQuery = "%s <> ?"
valueInQuery = "%s in (?)"
)
// Set of available filters which exclusively accept a single argument value.
var singleValueFilters = map[FilterExpression]bool{
Contains: true,
GreaterThan: true,
GreaterThanOrEqual: true,
LessThan: true,
LessThanOrEqual: true,
Equal: true,
NotEqual: true,
}
// Set of available filters which exclusively accept repeated argument values.
var repeatedValueFilters = map[FilterExpression]bool{
ValueIn: true,
}
const EqualExpression = "eq"
var filterNameMappings = map[string]FilterExpression{
"contains": Contains,
"gt": GreaterThan,
"gte": GreaterThanOrEqual,
"lt": LessThan,
"lte": LessThanOrEqual,
EqualExpression: Equal,
"ne": NotEqual,
"value_in": ValueIn,
}
var executionIdentifierFields = map[string]bool{
"project": true,
"domain": true,
"name": true,
}
const unrecognizedFilterFunction = "unrecognized filter function: %s"
const unsupportedFilterExpression = "unsupported filter expression: %s"
const invalidSingleValueFilter = "invalid single value filter expression: %s"
const invalidRepeatedValueFilter = "invalid repeated value filter expression: %s"
func getFilterExpressionName(expression FilterExpression) string {
switch expression {
case Contains:
return "contains"
case GreaterThan:
return "greater than"
case GreaterThanOrEqual:
return "greater than or equal"
case LessThan:
return "less than"
case LessThanOrEqual:
return "less than or equal"
case Equal:
return "equal"
case NotEqual:
return "not equal"
case ValueIn:
return "value in"
default:
return ""
}
}
func GetUnrecognizedFilterFunctionErr(function string) error {
return errors.NewFlyteAdminErrorf(codes.InvalidArgument, unrecognizedFilterFunction,
function)
}
func GetUnsupportedFilterExpressionErr(expression FilterExpression) error {
return errors.NewFlyteAdminErrorf(codes.InvalidArgument, unsupportedFilterExpression,
getFilterExpressionName(expression))
}
func GetInvalidSingleValueFilterErr(expression FilterExpression) error {
return errors.NewFlyteAdminErrorf(codes.InvalidArgument, invalidSingleValueFilter,
getFilterExpressionName(expression))
}
func GetInvalidRepeatedValueFilterErr(expression FilterExpression) error {
return errors.NewFlyteAdminErrorf(codes.InvalidArgument, invalidRepeatedValueFilter,
getFilterExpressionName(expression))
}
// Interface for a single filter expression.
type InlineFilter interface {
// Returns the entity for which this filter should be applied.
GetEntity() Entity
// Returns the column filtered on.
GetField() string
// Generates fields necessary to add a filter to a gorm database query.
GetGormQueryExpr() (GormQueryExpr, error)
// Generates fields necessary to add a filter on a gorm database join query.
GetGormJoinTableQueryExpr(tableName string) (GormQueryExpr, error)
// Include other db-specific methods here.
}
// FilterInterface implementation. Only one of value or repeatedValue should ever be populated (based on the function).
type inlineFilterImpl struct {
entity Entity
function FilterExpression
field string
value interface{}
repeatedValue interface{}
}
func (f *inlineFilterImpl) GetEntity() Entity {
return f.entity
}
func (f *inlineFilterImpl) GetField() string {
return f.field
}
func (f *inlineFilterImpl) getGormQueryExpr(formattedField string) (GormQueryExpr, error) {
// ValueIn is special because it uses repeating values.
if f.function == ValueIn {
queryStr := fmt.Sprintf(valueInQuery, formattedField)
return GormQueryExpr{
Query: queryStr,
Args: f.repeatedValue,
}, nil
}
switch f.function {
case Contains:
return GormQueryExpr{
// WHERE field LIKE %value%
Query: fmt.Sprintf(containsQuery, formattedField),
// args renders to something like: "%value%"
Args: fmt.Sprintf(containsArgs, f.value),
}, nil
case GreaterThan:
return GormQueryExpr{
// WHERE field > value
Query: fmt.Sprintf(greaterThanQuery, formattedField),
Args: f.value,
}, nil
case GreaterThanOrEqual:
return GormQueryExpr{
// WHERE field >= value
Query: fmt.Sprintf(greaterThanOrEqualQuery, formattedField),
Args: f.value,
}, nil
case LessThan:
return GormQueryExpr{
// WHERE field < value
Query: fmt.Sprintf(lessThanQuery, formattedField),
Args: f.value,
}, nil
case LessThanOrEqual:
return GormQueryExpr{
// WHERE field <= value
Query: fmt.Sprintf(lessThanOrEqualQuery, formattedField),
Args: f.value,
}, nil
case Equal:
return GormQueryExpr{
// WHERE field = value
Query: fmt.Sprintf(equalQuery, formattedField),
Args: f.value,
}, nil
case NotEqual:
return GormQueryExpr{
// WHERE field <> value
Query: fmt.Sprintf(notEqualQuery, formattedField),
Args: f.value,
}, nil
}
logger.Debugf(context.Background(), "can't create gorm query expr for %s", getFilterExpressionName(f.function))
return GormQueryExpr{}, GetUnsupportedFilterExpressionErr(f.function)
}
func (f *inlineFilterImpl) GetGormQueryExpr() (GormQueryExpr, error) {
return f.getGormQueryExpr(f.field)
}
func (f *inlineFilterImpl) GetGormJoinTableQueryExpr(tableName string) (GormQueryExpr, error) {
formattedField := fmt.Sprintf(joinArgsFormat, tableName, f.field)
return f.getGormQueryExpr(formattedField)
}
func customizeField(field string, entity Entity) string {
// Execution identifier fields have to be customized because we differ from convention in those column names.
if entity == Execution && executionIdentifierFields[field] {
return fmt.Sprintf("execution_%s", field)
}
return field
}
// Returns a filter which uses a single argument value.
func NewSingleValueFilter(entity Entity, function FilterExpression, field string, value interface{}) (InlineFilter, error) {
if _, ok := singleValueFilters[function]; !ok {
return nil, GetInvalidSingleValueFilterErr(function)
}
customizedField := customizeField(field, entity)
return &inlineFilterImpl{
entity: entity,
function: function,
field: customizedField,
value: value,
}, nil
}
// Returns a filter which uses a repeated argument value.
func NewRepeatedValueFilter(entity Entity, function FilterExpression, field string, repeatedValue interface{}) (InlineFilter, error) {
if _, ok := repeatedValueFilters[function]; !ok {
return nil, GetInvalidRepeatedValueFilterErr(function)
}
customizedField := customizeField(field, entity)
return &inlineFilterImpl{
entity: entity,
function: function,
field: customizedField,
repeatedValue: repeatedValue,
}, nil
}
func NewInlineFilter(entity Entity, function string, field string, value interface{}) (InlineFilter, error) {
expression, ok := filterNameMappings[function]
if !ok {
logger.Debugf(context.Background(), "can't create filter for unrecognized function: %s", function)
return nil, GetUnrecognizedFilterFunctionErr(function)
}
if isSingleValueFilter := singleValueFilters[expression]; isSingleValueFilter {
return NewSingleValueFilter(entity, expression, field, value)
}
return NewRepeatedValueFilter(entity, expression, field, value)
}
// Interface for a map filter expression.
type MapFilter interface {
GetFilter() map[string]interface{}
}
type mapFilterImpl struct {
filter map[string]interface{}
}
func (m mapFilterImpl) GetFilter() map[string]interface{} {
return m.filter
}
func NewMapFilter(filter map[string]interface{}) MapFilter {
return &mapFilterImpl{
filter: filter,
}
}
const queryWithDefaultFmt = "COALESCE(%s, %v)"
type withDefaultValueFilter struct {
inlineFilterImpl
defaultValue interface{}
}
func (f *withDefaultValueFilter) GetGormQueryExpr() (GormQueryExpr, error) {
formattedField := fmt.Sprintf(queryWithDefaultFmt, f.GetField(), f.defaultValue)
return f.getGormQueryExpr(formattedField)
}
func (f *withDefaultValueFilter) GetGormJoinTableQueryExpr(tableName string) (GormQueryExpr, error) {
formattedField := fmt.Sprintf(queryWithDefaultFmt, fmt.Sprintf(joinArgsFormat, tableName, f.GetField()), f.defaultValue)
return f.getGormQueryExpr(formattedField)
}
func NewWithDefaultValueFilter(defaultValue interface{}, filter InlineFilter) (InlineFilter, error) {
inlineFilter, ok := filter.(*inlineFilterImpl)
if !ok {
return nil, errors.NewFlyteAdminErrorf(codes.Internal,
"Unable to create default value filter for [%s] because the system encountered an unknown filter type",
filter.GetField())
}
return &withDefaultValueFilter{
inlineFilterImpl: *inlineFilter,
defaultValue: defaultValue,
}, nil
}