1/** 2 * Copyright 2016 IBM Corp. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17// See reference at https://sldn.softlayer.com/article/object-filters. 18// Examples in the README.md file and in the examples directory. 19package filter 20 21import ( 22 "encoding/json" 23 "fmt" 24 "strings" 25) 26 27type Filter struct { 28 Path string 29 Op string 30 Opts map[string]interface{} 31 Val interface{} 32} 33 34type Filters []Filter 35 36// Returns an array of Filters that you can later call .Build() on. 37func New(args ...Filter) Filters { 38 return args 39} 40 41// This is like calling New().Build(). 42// Returns a JSON string that can be used as the object filter. 43func Build(args ...Filter) string { 44 filters := Filters{} 45 46 for _, arg := range args { 47 filters = append(filters, arg) 48 } 49 50 return filters.Build() 51} 52 53// This creates a new Filter. The path is a dot-delimited path down 54// to the attribute this filter is for. The second value parameter 55// is optional. 56func Path(path string, val ...interface{}) Filter { 57 if len(val) > 0 { 58 return Filter{Path: path, Val: val[0]} 59 } 60 61 return Filter{Path: path} 62} 63 64// Builds the filter string in JSON format 65func (fs Filters) Build() string { 66 // Loops around filters, 67 // splitting path on '.' and looping around path pieces. 68 // Idea is to create a map/tree like map[string]interface{}. 69 // Every component in the path is a node to create in the tree. 70 // Once we get to the leaf, we set the operation. 71 // map[string]interface{}{"operation": op+" "+value} 72 // If Op is "", then just map[string]interface{}{"operation": value}. 73 // Afterwards, the Opts are traversed; []map[string]interface{}{} 74 // For every entry in Opts, we create one map, and append it to an array of maps. 75 // At the end, json.Marshal the whole thing. 76 result := map[string]interface{}{} 77 for _, filter := range fs { 78 if filter.Path == "" { 79 continue 80 } 81 82 cursor := result 83 nodes := strings.Split(filter.Path, ".") 84 for len(nodes) > 1 { 85 branch := nodes[0] 86 if _, ok := cursor[branch]; !ok { 87 cursor[branch] = map[string]interface{}{} 88 } 89 cursor = cursor[branch].(map[string]interface{}) 90 nodes = nodes[1:len(nodes)] 91 } 92 93 leaf := nodes[0] 94 if filter.Val != nil { 95 operation := filter.Val 96 if filter.Op != "" { 97 var format string 98 switch filter.Val.(type) { 99 case int: 100 format = "%d" 101 default: 102 format = "%s" 103 } 104 operation = filter.Op + " " + fmt.Sprintf(format, filter.Val) 105 } 106 107 cursor[leaf] = map[string]interface{}{ 108 "operation": operation, 109 } 110 } 111 112 if filter.Opts == nil { 113 continue 114 } 115 116 options := []map[string]interface{}{} 117 for name, value := range filter.Opts { 118 options = append(options, map[string]interface{}{ 119 "name": name, 120 "value": value, 121 }) 122 } 123 124 cursor[leaf] = map[string]interface{}{ 125 "operation": filter.Op, 126 "options": options, 127 } 128 } 129 130 jsonStr, _ := json.Marshal(result) 131 return string(jsonStr) 132} 133 134// Builds the filter string in JSON format 135func (f Filter) Build() string { 136 return Build(f) 137} 138 139// Add options to the filter. Can be chained for multiple options. 140func (f Filter) Opt(name string, value interface{}) Filter { 141 if f.Opts == nil { 142 f.Opts = map[string]interface{}{} 143 } 144 145 f.Opts[name] = value 146 return f 147} 148 149// Set this filter to test if property is equal to the value 150func (f Filter) Eq(val interface{}) Filter { 151 f.Op = "" 152 f.Val = val 153 return f 154} 155 156// Set this filter to test if property is not equal to the value 157func (f Filter) NotEq(val interface{}) Filter { 158 f.Op = "!=" 159 f.Val = val 160 return f 161} 162 163// Set this filter to test if property is like the value 164func (f Filter) Like(val interface{}) Filter { 165 f.Op = "~" 166 f.Val = val 167 return f 168} 169 170// Set this filter to test if property is unlike value 171func (f Filter) NotLike(val interface{}) Filter { 172 f.Op = "!~" 173 f.Val = val 174 return f 175} 176 177// Set this filter to test if property is less than value 178func (f Filter) LessThan(val interface{}) Filter { 179 f.Op = "<" 180 f.Val = val 181 return f 182} 183 184// Set this filter to test if property is less than or equal to the value 185func (f Filter) LessThanOrEqual(val interface{}) Filter { 186 f.Op = "<=" 187 f.Val = val 188 return f 189} 190 191// Set this filter to test if property is greater than value 192func (f Filter) GreaterThan(val interface{}) Filter { 193 f.Op = ">" 194 f.Val = val 195 return f 196} 197 198// Set this filter to test if property is greater than or equal to value 199func (f Filter) GreaterThanOrEqual(val interface{}) Filter { 200 f.Op = ">=" 201 f.Val = val 202 return f 203} 204 205// Set this filter to test if property is null 206func (f Filter) IsNull() Filter { 207 f.Op = "" 208 f.Val = "is null" 209 return f 210} 211 212// Set this filter to test if property is not null 213func (f Filter) NotNull() Filter { 214 f.Op = "" 215 f.Val = "not null" 216 return f 217} 218 219// Set this filter to test if property contains the value 220func (f Filter) Contains(val interface{}) Filter { 221 f.Op = "*=" 222 f.Val = val 223 return f 224} 225 226// Set this filter to test if property does not contain the value 227func (f Filter) NotContains(val interface{}) Filter { 228 f.Op = "!*=" 229 f.Val = val 230 return f 231} 232 233// Set this filter to test if property starts with the value 234func (f Filter) StartsWith(val interface{}) Filter { 235 f.Op = "^=" 236 f.Val = val 237 return f 238} 239 240// Set this filter to test if property does not start with the value 241func (f Filter) NotStartsWith(val interface{}) Filter { 242 f.Op = "!^=" 243 f.Val = val 244 return f 245} 246 247// Set this filter to test if property ends with the value 248func (f Filter) EndsWith(val interface{}) Filter { 249 f.Op = "$=" 250 f.Val = val 251 return f 252} 253 254// Set this filter to test if property does not end with the value 255func (f Filter) NotEndsWith(val interface{}) Filter { 256 f.Op = "!$=" 257 f.Val = val 258 return f 259} 260 261// Set this filter to test if property is one of the values in args. 262func (f Filter) In(args ...interface{}) Filter { 263 f.Op = "in" 264 values := []interface{}{} 265 for _, arg := range args { 266 values = append(values, arg) 267 } 268 269 return f.Opt("data", values) 270} 271 272// Set this filter to test if property has a date older than the value in days. 273func (f Filter) DaysPast(val interface{}) Filter { 274 f.Op = ">= currentDate -" 275 f.Val = val 276 return f 277} 278 279// Set this filter to test if property has the exact date as the value. 280func (f Filter) Date(date string) Filter { 281 f.Op = "isDate" 282 f.Val = nil 283 return f.Opt("date", []string{date}) 284} 285 286// Set this filter to test if property has a date before the value. 287func (f Filter) DateBefore(date string) Filter { 288 f.Op = "lessThanDate" 289 f.Val = nil 290 return f.Opt("date", []string{date}) 291} 292 293// Set this filter to test if property has a date after the value. 294func (f Filter) DateAfter(date string) Filter { 295 f.Op = "greaterThanDate" 296 f.Val = nil 297 return f.Opt("date", []string{date}) 298} 299 300// Set this filter to test if property has a date between the values. 301func (f Filter) DateBetween(start string, end string) Filter { 302 f.Op = "betweenDate" 303 f.Val = nil 304 return f.Opt("startDate", []string{start}).Opt("endDate", []string{end}) 305} 306