github.com/eris-ltd/erisdb@v0.25.0/event/query/builder.go (about) 1 package query 2 3 import ( 4 "bytes" 5 "encoding" 6 "fmt" 7 "reflect" 8 "strconv" 9 "strings" 10 "text/template" 11 "time" 12 ) 13 14 const ( 15 MultipleValueTagSeparator = ";" 16 17 // Operators 18 equalString = "=" 19 greaterThanString = ">" 20 lessThanString = "<" 21 greaterOrEqualString = ">=" 22 lessOrEqualString = "<=" 23 containsString = "CONTAINS" 24 andString = "AND" 25 26 // Values 27 trueString = "true" 28 falseString = "false" 29 emptyString = "empty" 30 timeString = "TIME" 31 dateString = "DATE" 32 ) 33 34 type Query interface { 35 Matches(tags Tagged) bool 36 String() string 37 } 38 39 type Queryable interface { 40 Query() (Query, error) 41 } 42 43 type parsedQuery struct { 44 query Query 45 } 46 47 func AsQueryable(query Query) parsedQuery { 48 return parsedQuery{query: query} 49 } 50 51 func (pq parsedQuery) Query() (Query, error) { 52 return pq.query, nil 53 } 54 55 // A yet-to-parsed query 56 type String string 57 58 func Must(qry Query, err error) Query { 59 if err != nil { 60 panic(fmt.Errorf("could not compile: %v", qry)) 61 } 62 return qry 63 } 64 65 func (qs String) Query() (Query, error) { 66 if isEmpty(string(qs)) { 67 return Empty{}, nil 68 } 69 return New(string(qs)) 70 } 71 72 func MatchAllQueryable() Queryable { 73 return Empty{} 74 } 75 76 // A fluent query builder 77 type Builder struct { 78 queryString string 79 condition 80 // reusable buffer for building queryString 81 bytes.Buffer 82 error 83 } 84 85 // Templates 86 type condition struct { 87 Tag string 88 Op string 89 Operand string 90 } 91 92 var conditionTemplate = template.Must(template.New("condition").Parse("{{.Tag}} {{.Op}} {{.Operand}}")) 93 94 // Creates a new query builder with a base query that is the conjunction of all queries passed 95 func NewBuilder(queries ...string) *Builder { 96 qb := new(Builder) 97 qb.queryString = qb.and(stringIterator(queries...)) 98 return qb 99 } 100 101 func (qb *Builder) String() string { 102 return qb.queryString 103 } 104 105 func (qb *Builder) Query() (Query, error) { 106 if qb.error != nil { 107 return nil, qb.error 108 } 109 return NewOrEmpty(qb.queryString) 110 } 111 112 func NewOrEmpty(queryString string) (Query, error) { 113 if isEmpty(queryString) { 114 return Empty{}, nil 115 } 116 return New(queryString) 117 } 118 119 // Creates the conjunction of Builder and rightQuery 120 func (qb *Builder) And(queryBuilders ...*Builder) *Builder { 121 return NewBuilder(qb.and(queryBuilderIterator(queryBuilders...))) 122 } 123 124 // Creates the conjunction of Builder and tag = operand 125 func (qb *Builder) AndEquals(tag string, operand interface{}) *Builder { 126 qb.condition.Tag = tag 127 qb.condition.Op = equalString 128 qb.condition.Operand = operandString(operand) 129 return NewBuilder(qb.and(stringIterator(qb.conditionString()))) 130 } 131 132 func (qb *Builder) AndGreaterThanOrEqual(tag string, operand interface{}) *Builder { 133 qb.condition.Tag = tag 134 qb.condition.Op = greaterOrEqualString 135 qb.condition.Operand = operandString(operand) 136 return NewBuilder(qb.and(stringIterator(qb.conditionString()))) 137 } 138 139 func (qb *Builder) AndLessThanOrEqual(tag string, operand interface{}) *Builder { 140 qb.condition.Tag = tag 141 qb.condition.Op = lessOrEqualString 142 qb.condition.Operand = operandString(operand) 143 return NewBuilder(qb.and(stringIterator(qb.conditionString()))) 144 } 145 146 func (qb *Builder) AndStrictlyGreaterThan(tag string, operand interface{}) *Builder { 147 qb.condition.Tag = tag 148 qb.condition.Op = greaterThanString 149 qb.condition.Operand = operandString(operand) 150 return NewBuilder(qb.and(stringIterator(qb.conditionString()))) 151 } 152 153 func (qb *Builder) AndStrictlyLessThan(tag string, operand interface{}) *Builder { 154 qb.condition.Tag = tag 155 qb.condition.Op = lessThanString 156 qb.condition.Operand = operandString(operand) 157 return NewBuilder(qb.and(stringIterator(qb.conditionString()))) 158 } 159 160 func (qb *Builder) AndContains(tag string, operand interface{}) *Builder { 161 qb.condition.Tag = tag 162 qb.condition.Op = containsString 163 qb.condition.Operand = operandString(operand) 164 return NewBuilder(qb.and(stringIterator(qb.conditionString()))) 165 } 166 167 func (qb *Builder) and(queryIterator func(func(string))) string { 168 defer qb.Buffer.Reset() 169 qb.Buffer.WriteString(qb.queryString) 170 queryIterator(func(q string) { 171 if !isEmpty(q) { 172 if qb.Buffer.Len() > 0 { 173 qb.Buffer.WriteByte(' ') 174 qb.Buffer.WriteString(andString) 175 qb.Buffer.WriteByte(' ') 176 } 177 qb.Buffer.WriteString(q) 178 } 179 }) 180 return qb.Buffer.String() 181 } 182 183 func operandString(value interface{}) string { 184 buf := new(bytes.Buffer) 185 switch v := value.(type) { 186 case string: 187 buf.WriteByte('\'') 188 buf.WriteString(v) 189 buf.WriteByte('\'') 190 return buf.String() 191 case fmt.Stringer: 192 return operandString(v.String()) 193 default: 194 return StringFromValue(v) 195 } 196 } 197 198 func StringFromValue(value interface{}) string { 199 rv := reflect.ValueOf(value) 200 if rv.Kind() == reflect.Ptr && rv.IsNil() { 201 return "nil" 202 } 203 switch v := value.(type) { 204 case string: 205 return v 206 case encoding.TextMarshaler: 207 bs, _ := v.MarshalText() 208 return string(bs) 209 case fmt.Stringer: 210 return v.String() 211 case bool: 212 if v { 213 return trueString 214 } 215 return falseString 216 case int: 217 return strconv.FormatInt(int64(v), 10) 218 case int32: 219 return strconv.FormatInt(int64(v), 10) 220 case int64: 221 return strconv.FormatInt(v, 10) 222 case uint: 223 return strconv.FormatUint(uint64(v), 10) 224 case uint32: 225 return strconv.FormatUint(uint64(v), 10) 226 case uint64: 227 return strconv.FormatUint(v, 10) 228 case float32: 229 return strconv.FormatFloat(float64(v), 'f', -1, 32) 230 case float64: 231 return strconv.FormatFloat(float64(v), 'f', -1, 64) 232 case time.Time: 233 return timeString + " " + v.Format(time.RFC3339) 234 default: 235 if rv.Kind() == reflect.Slice { 236 values := make([]string, rv.Len()) 237 for i := 0; i < rv.Len(); i++ { 238 values[i] = StringFromValue(rv.Index(i).Interface()) 239 } 240 return strings.Join(values, MultipleValueTagSeparator) 241 } 242 return fmt.Sprintf("%v", v) 243 } 244 } 245 246 func (qb *Builder) conditionString() string { 247 defer qb.Buffer.Reset() 248 err := conditionTemplate.Execute(&qb.Buffer, qb.condition) 249 if err != nil && qb.error == nil { 250 qb.error = err 251 } 252 return qb.Buffer.String() 253 } 254 255 func isEmpty(queryString string) bool { 256 return queryString == "" || queryString == emptyString 257 } 258 259 // Iterators over some strings 260 func stringIterator(strs ...string) func(func(string)) { 261 return func(callback func(string)) { 262 for _, s := range strs { 263 callback(s) 264 } 265 } 266 } 267 268 func queryBuilderIterator(qbs ...*Builder) func(func(string)) { 269 return func(callback func(string)) { 270 for _, qb := range qbs { 271 callback(qb.String()) 272 } 273 } 274 }