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