github.com/vipernet-xyz/tendermint-core@v0.32.0/libs/pubsub/query/query.go (about) 1 // Package query provides a parser for a custom query format: 2 // 3 // abci.invoice.number=22 AND abci.invoice.owner=Ivan 4 // 5 // See query.peg for the grammar, which is a https://en.wikipedia.org/wiki/Parsing_expression_grammar. 6 // More: https://github.com/PhilippeSigaud/Pegged/wiki/PEG-Basics 7 // 8 // It has a support for numbers (integer and floating point), dates and times. 9 package query 10 11 import ( 12 "fmt" 13 "reflect" 14 "regexp" 15 "strconv" 16 "strings" 17 "time" 18 19 "github.com/pkg/errors" 20 ) 21 22 var ( 23 numRegex = regexp.MustCompile(`([0-9\.]+)`) 24 ) 25 26 // Query holds the query string and the query parser. 27 type Query struct { 28 str string 29 parser *QueryParser 30 Pagination *Page 31 } 32 type Page struct { 33 Size int 34 Skip int 35 Sort string 36 } 37 38 // Condition represents a single condition within a query and consists of composite key 39 // (e.g. "tx.gas"), operator (e.g. "=") and operand (e.g. "7"). 40 type Condition struct { 41 CompositeKey string 42 Op Operator 43 Operand interface{} 44 } 45 46 // New parses the given string and returns a query or error if the string is 47 // invalid. 48 func New(s string) (*Query, error) { 49 p := &QueryParser{Buffer: fmt.Sprintf(`"%s"`, s)} 50 p.Init() 51 if err := p.Parse(); err != nil { 52 return nil, err 53 } 54 return &Query{str: s, parser: p}, nil 55 } 56 57 // MustParse turns the given string into a query or panics; for tests or others 58 // cases where you know the string is valid. 59 func MustParse(s string) *Query { 60 q, err := New(s) 61 if err != nil { 62 panic(fmt.Sprintf("failed to parse %s: %v", s, err)) 63 } 64 return q 65 } 66 func (q *Query) AddPage(size int, skip int, sort string) { 67 q.Pagination = &Page{size, skip, sort} 68 } 69 70 // String returns the original string. 71 func (q *Query) String() string { 72 return q.str 73 } 74 75 // Operator is an operator that defines some kind of relation between composite key and 76 // operand (equality, etc.). 77 type Operator uint8 78 79 const ( 80 // "<=" 81 OpLessEqual Operator = iota 82 // ">=" 83 OpGreaterEqual 84 // "<" 85 OpLess 86 // ">" 87 OpGreater 88 // "=" 89 OpEqual 90 // "CONTAINS"; used to check if a string contains a certain sub string. 91 OpContains 92 // "EXISTS"; used to check if a certain event attribute is present. 93 OpExists 94 ) 95 96 const ( 97 // DateLayout defines a layout for all dates (`DATE date`) 98 DateLayout = "2006-01-02" 99 // TimeLayout defines a layout for all times (`TIME time`) 100 TimeLayout = time.RFC3339 101 ) 102 103 // Conditions returns a list of conditions. It returns an error if there is any 104 // error with the provided grammar in the Query. 105 func (q *Query) Condition() (Condition, error) { 106 var ( 107 eventAttr string 108 op Operator 109 ) 110 111 conditions := make([]Condition, 0) 112 buffer, begin, end := q.parser.Buffer, 0, 0 113 114 // tokens must be in the following order: tag ("tx.gas") -> operator ("=") -> operand ("7") 115 for token := range q.parser.Tokens() { 116 switch token.pegRule { 117 case rulePegText: 118 begin, end = int(token.begin), int(token.end) 119 120 case ruletag: 121 eventAttr = buffer[begin:end] 122 123 case rulele: 124 op = OpLessEqual 125 126 case rulege: 127 op = OpGreaterEqual 128 129 case rulel: 130 op = OpLess 131 132 case ruleg: 133 op = OpGreater 134 135 case ruleequal: 136 op = OpEqual 137 138 case rulecontains: 139 op = OpContains 140 141 case ruleexists: 142 op = OpExists 143 conditions = append(conditions, Condition{eventAttr, op, nil}) 144 break 145 146 case rulevalue: 147 // strip single quotes from value (i.e. "'NewBlock'" -> "NewBlock") 148 valueWithoutSingleQuotes := buffer[begin+1 : end-1] 149 conditions = append(conditions, Condition{eventAttr, op, valueWithoutSingleQuotes}) 150 break 151 152 case rulenumber: 153 number := buffer[begin:end] 154 if strings.ContainsAny(number, ".") { // if it looks like a floating-point number 155 value, err := strconv.ParseFloat(number, 64) 156 if err != nil { 157 err = fmt.Errorf( 158 "got %v while trying to parse %s as float64 (should never happen if the grammar is correct)", 159 err, number, 160 ) 161 return Condition{}, err 162 } 163 164 conditions = append(conditions, Condition{eventAttr, op, value}) 165 } else { 166 value, err := strconv.ParseInt(number, 10, 64) 167 if err != nil { 168 err = fmt.Errorf( 169 "got %v while trying to parse %s as int64 (should never happen if the grammar is correct)", 170 err, number, 171 ) 172 return Condition{}, err 173 } 174 175 conditions = append(conditions, Condition{eventAttr, op, value}) 176 break 177 } 178 179 case ruletime: 180 value, err := time.Parse(TimeLayout, buffer[begin:end]) 181 if err != nil { 182 err = fmt.Errorf( 183 "got %v while trying to parse %s as time.Time / RFC3339 (should never happen if the grammar is correct)", 184 err, buffer[begin:end], 185 ) 186 return Condition{}, err 187 } 188 189 conditions = append(conditions, Condition{eventAttr, op, value}) 190 break 191 192 case ruledate: 193 value, err := time.Parse("2006-01-02", buffer[begin:end]) 194 if err != nil { 195 err = fmt.Errorf( 196 "got %v while trying to parse %s as time.Time / '2006-01-02' (should never happen if the grammar is correct)", 197 err, buffer[begin:end], 198 ) 199 return Condition{}, err 200 } 201 202 conditions = append(conditions, Condition{eventAttr, op, value}) 203 break 204 } 205 } 206 207 return conditions[0], nil 208 } 209 210 // Conditions returns a list of conditions. It returns an error if there is any 211 // error with the provided grammar in the Query. 212 func (q *Query) Conditions() ([]Condition, error) { 213 var ( 214 eventAttr string 215 op Operator 216 ) 217 218 conditions := make([]Condition, 0) 219 buffer, begin, end := q.parser.Buffer, 0, 0 220 221 // tokens must be in the following order: tag ("tx.gas") -> operator ("=") -> operand ("7") 222 for token := range q.parser.Tokens() { 223 switch token.pegRule { 224 case rulePegText: 225 begin, end = int(token.begin), int(token.end) 226 227 case ruletag: 228 eventAttr = buffer[begin:end] 229 230 case rulele: 231 op = OpLessEqual 232 233 case rulege: 234 op = OpGreaterEqual 235 236 case rulel: 237 op = OpLess 238 239 case ruleg: 240 op = OpGreater 241 242 case ruleequal: 243 op = OpEqual 244 245 case rulecontains: 246 op = OpContains 247 248 case ruleexists: 249 op = OpExists 250 conditions = append(conditions, Condition{eventAttr, op, nil}) 251 252 case rulevalue: 253 // strip single quotes from value (i.e. "'NewBlock'" -> "NewBlock") 254 valueWithoutSingleQuotes := buffer[begin+1 : end-1] 255 conditions = append(conditions, Condition{eventAttr, op, valueWithoutSingleQuotes}) 256 257 case rulenumber: 258 number := buffer[begin:end] 259 if strings.ContainsAny(number, ".") { // if it looks like a floating-point number 260 value, err := strconv.ParseFloat(number, 64) 261 if err != nil { 262 err = fmt.Errorf( 263 "got %v while trying to parse %s as float64 (should never happen if the grammar is correct)", 264 err, number, 265 ) 266 return nil, err 267 } 268 269 conditions = append(conditions, Condition{eventAttr, op, value}) 270 } else { 271 value, err := strconv.ParseInt(number, 10, 64) 272 if err != nil { 273 err = fmt.Errorf( 274 "got %v while trying to parse %s as int64 (should never happen if the grammar is correct)", 275 err, number, 276 ) 277 return nil, err 278 } 279 280 conditions = append(conditions, Condition{eventAttr, op, value}) 281 } 282 283 case ruletime: 284 value, err := time.Parse(TimeLayout, buffer[begin:end]) 285 if err != nil { 286 err = fmt.Errorf( 287 "got %v while trying to parse %s as time.Time / RFC3339 (should never happen if the grammar is correct)", 288 err, buffer[begin:end], 289 ) 290 return nil, err 291 } 292 293 conditions = append(conditions, Condition{eventAttr, op, value}) 294 295 case ruledate: 296 value, err := time.Parse("2006-01-02", buffer[begin:end]) 297 if err != nil { 298 err = fmt.Errorf( 299 "got %v while trying to parse %s as time.Time / '2006-01-02' (should never happen if the grammar is correct)", 300 err, buffer[begin:end], 301 ) 302 return nil, err 303 } 304 305 conditions = append(conditions, Condition{eventAttr, op, value}) 306 } 307 } 308 309 return conditions, nil 310 } 311 312 // Matches returns true if the query matches against any event in the given set 313 // of events, false otherwise. For each event, a match exists if the query is 314 // matched against *any* value in a slice of values. An error is returned if 315 // any attempted event match returns an error. 316 // 317 // For example, query "name=John" matches events = {"name": ["John", "Eric"]}. 318 // More examples could be found in parser_test.go and query_test.go. 319 func (q *Query) Matches(events map[string][]string) (bool, error) { 320 if len(events) == 0 { 321 return false, nil 322 } 323 324 var ( 325 eventAttr string 326 op Operator 327 ) 328 329 buffer, begin, end := q.parser.Buffer, 0, 0 330 331 // tokens must be in the following order: 332 333 // tag ("tx.gas") -> operator ("=") -> operand ("7") 334 for token := range q.parser.Tokens() { 335 switch token.pegRule { 336 case rulePegText: 337 begin, end = int(token.begin), int(token.end) 338 339 case ruletag: 340 eventAttr = buffer[begin:end] 341 342 case rulele: 343 op = OpLessEqual 344 345 case rulege: 346 op = OpGreaterEqual 347 348 case rulel: 349 op = OpLess 350 351 case ruleg: 352 op = OpGreater 353 354 case ruleequal: 355 op = OpEqual 356 357 case rulecontains: 358 op = OpContains 359 case ruleexists: 360 op = OpExists 361 if strings.Contains(eventAttr, ".") { 362 // Searching for a full "type.attribute" event. 363 _, ok := events[eventAttr] 364 if !ok { 365 return false, nil 366 } 367 } else { 368 foundEvent := false 369 370 loop: 371 for compositeKey := range events { 372 if strings.Index(compositeKey, eventAttr) == 0 { 373 foundEvent = true 374 break loop 375 } 376 } 377 if !foundEvent { 378 return false, nil 379 } 380 } 381 382 case rulevalue: 383 // strip single quotes from value (i.e. "'NewBlock'" -> "NewBlock") 384 valueWithoutSingleQuotes := buffer[begin+1 : end-1] 385 386 // see if the triplet (event attribute, operator, operand) matches any event 387 // "tx.gas", "=", "7", { "tx.gas": 7, "tx.ID": "4AE393495334" } 388 match, err := match(eventAttr, op, reflect.ValueOf(valueWithoutSingleQuotes), events) 389 if err != nil { 390 return false, err 391 } 392 393 if !match { 394 return false, nil 395 } 396 397 case rulenumber: 398 number := buffer[begin:end] 399 if strings.ContainsAny(number, ".") { // if it looks like a floating-point number 400 value, err := strconv.ParseFloat(number, 64) 401 if err != nil { 402 err = fmt.Errorf( 403 "got %v while trying to parse %s as float64 (should never happen if the grammar is correct)", 404 err, number, 405 ) 406 return false, err 407 } 408 409 match, err := match(eventAttr, op, reflect.ValueOf(value), events) 410 if err != nil { 411 return false, err 412 } 413 414 if !match { 415 return false, nil 416 } 417 } else { 418 value, err := strconv.ParseInt(number, 10, 64) 419 if err != nil { 420 err = fmt.Errorf( 421 "got %v while trying to parse %s as int64 (should never happen if the grammar is correct)", 422 err, number, 423 ) 424 return false, err 425 } 426 427 match, err := match(eventAttr, op, reflect.ValueOf(value), events) 428 if err != nil { 429 return false, err 430 } 431 432 if !match { 433 return false, nil 434 } 435 } 436 437 case ruletime: 438 value, err := time.Parse(TimeLayout, buffer[begin:end]) 439 if err != nil { 440 err = fmt.Errorf( 441 "got %v while trying to parse %s as time.Time / RFC3339 (should never happen if the grammar is correct)", 442 err, buffer[begin:end], 443 ) 444 return false, err 445 } 446 447 match, err := match(eventAttr, op, reflect.ValueOf(value), events) 448 if err != nil { 449 return false, err 450 } 451 452 if !match { 453 return false, nil 454 } 455 456 case ruledate: 457 value, err := time.Parse("2006-01-02", buffer[begin:end]) 458 if err != nil { 459 err = fmt.Errorf( 460 "got %v while trying to parse %s as time.Time / '2006-01-02' (should never happen if the grammar is correct)", 461 err, buffer[begin:end], 462 ) 463 return false, err 464 } 465 466 match, err := match(eventAttr, op, reflect.ValueOf(value), events) 467 if err != nil { 468 return false, err 469 } 470 471 if !match { 472 return false, nil 473 } 474 } 475 } 476 477 return true, nil 478 } 479 480 // match returns true if the given triplet (attribute, operator, operand) matches 481 // any value in an event for that attribute. If any match fails with an error, 482 // that error is returned. 483 // 484 // First, it looks up the key in the events and if it finds one, tries to compare 485 // all the values from it to the operand using the operator. 486 // 487 // "tx.gas", "=", "7", {"tx": [{"gas": 7, "ID": "4AE393495334"}]} 488 func match(attr string, op Operator, operand reflect.Value, events map[string][]string) (bool, error) { 489 // look up the tag from the query in tags 490 values, ok := events[attr] 491 if !ok { 492 return false, nil 493 } 494 495 for _, value := range values { 496 // return true if any value in the set of the event's values matches 497 match, err := matchValue(value, op, operand) 498 if err != nil { 499 return false, err 500 } 501 502 if match { 503 return true, nil 504 } 505 } 506 507 return false, nil 508 } 509 510 // matchValue will attempt to match a string value against an operator an 511 // operand. A boolean is returned representing the match result. It will return 512 // an error if the value cannot be parsed and matched against the operand type. 513 func matchValue(value string, op Operator, operand reflect.Value) (bool, error) { 514 switch operand.Kind() { 515 case reflect.Struct: // time 516 operandAsTime := operand.Interface().(time.Time) 517 518 // try our best to convert value from events to time.Time 519 var ( 520 v time.Time 521 err error 522 ) 523 524 if strings.ContainsAny(value, "T") { 525 v, err = time.Parse(TimeLayout, value) 526 } else { 527 v, err = time.Parse(DateLayout, value) 528 } 529 if err != nil { 530 return false, errors.Wrapf(err, "failed to convert value %v from event attribute to time.Time", value) 531 } 532 533 switch op { 534 case OpLessEqual: 535 return (v.Before(operandAsTime) || v.Equal(operandAsTime)), nil 536 case OpGreaterEqual: 537 return (v.Equal(operandAsTime) || v.After(operandAsTime)), nil 538 case OpLess: 539 return v.Before(operandAsTime), nil 540 case OpGreater: 541 return v.After(operandAsTime), nil 542 case OpEqual: 543 return v.Equal(operandAsTime), nil 544 } 545 546 case reflect.Float64: 547 var v float64 548 549 operandFloat64 := operand.Interface().(float64) 550 filteredValue := numRegex.FindString(value) 551 552 // try our best to convert value from tags to float64 553 v, err := strconv.ParseFloat(filteredValue, 64) 554 if err != nil { 555 return false, errors.Wrapf(err, "failed to convert value %v from event attribute to float64", filteredValue) 556 } 557 558 switch op { 559 case OpLessEqual: 560 return v <= operandFloat64, nil 561 case OpGreaterEqual: 562 return v >= operandFloat64, nil 563 case OpLess: 564 return v < operandFloat64, nil 565 case OpGreater: 566 return v > operandFloat64, nil 567 case OpEqual: 568 return v == operandFloat64, nil 569 } 570 571 case reflect.Int64: 572 var v int64 573 574 operandInt := operand.Interface().(int64) 575 filteredValue := numRegex.FindString(value) 576 577 // if value looks like float, we try to parse it as float 578 if strings.ContainsAny(filteredValue, ".") { 579 v1, err := strconv.ParseFloat(filteredValue, 64) 580 if err != nil { 581 return false, errors.Wrapf(err, "failed to convert value %v from event attribute to float64", filteredValue) 582 } 583 584 v = int64(v1) 585 } else { 586 var err error 587 // try our best to convert value from tags to int64 588 v, err = strconv.ParseInt(filteredValue, 10, 64) 589 if err != nil { 590 return false, errors.Wrapf(err, "failed to convert value %v from event attribute to int64", filteredValue) 591 } 592 } 593 594 switch op { 595 case OpLessEqual: 596 return v <= operandInt, nil 597 case OpGreaterEqual: 598 return v >= operandInt, nil 599 case OpLess: 600 return v < operandInt, nil 601 case OpGreater: 602 return v > operandInt, nil 603 case OpEqual: 604 return v == operandInt, nil 605 } 606 607 case reflect.String: 608 switch op { 609 case OpEqual: 610 return value == operand.String(), nil 611 case OpContains: 612 return strings.Contains(value, operand.String()), nil 613 } 614 615 default: 616 return false, fmt.Errorf("unknown kind of operand %v", operand.Kind()) 617 } 618 619 return false, nil 620 }