1 // Package query provides a parser for a custom query format:
3 // abci.invoice.number=22 AND abci.invoice.owner=Ivan
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
8 // It has a support for numbers (integer and floating point), dates and times.
19 // Query holds the query string and the query parser.
25 // New parses the given string and returns a query or error if the string is
27 func New(s string) (*Query, error) {
28 p := &QueryParser{Buffer: fmt.Sprintf(`"%s"`, s)}
30 if err := p.Parse(); err != nil {
33 return &Query{str: s, parser: p}, nil
36 // MustParse turns the given string into a query or panics; for tests or others
37 // cases where you know the string is valid.
38 func MustParse(s string) *Query {
41 panic(fmt.Sprintf("failed to parse %s: %v", s, err))
46 // String returns the original string.
47 func (q *Query) String() string {
54 opLessEqual operator = iota
62 // Matches returns true if the query matches the given set of tags, false otherwise.
64 // For example, query "name=John" matches tags = {"name": "John"}. More
65 // examples could be found in parser_test.go and query_test.go.
66 func (q *Query) Matches(tags map[string]interface{}) bool {
71 buffer, begin, end := q.parser.Buffer, 0, 0
76 // tokens must be in the following order: tag ("tx.gas") -> operator ("=") -> operand ("7")
77 for _, token := range q.parser.Tokens() {
78 switch token.pegRule {
81 begin, end = int(token.begin), int(token.end)
83 tag = buffer[begin:end]
97 // strip single quotes from value (i.e. "'NewBlock'" -> "NewBlock")
98 valueWithoutSingleQuotes := buffer[begin+1 : end-1]
100 // see if the triplet (tag, operator, operand) matches any tag
101 // "tx.gas", "=", "7", { "tx.gas": 7, "tx.ID": "4AE393495334" }
102 if !match(tag, op, reflect.ValueOf(valueWithoutSingleQuotes), tags) {
106 number := buffer[begin:end]
107 if strings.Contains(number, ".") { // if it looks like a floating-point number
108 value, err := strconv.ParseFloat(number, 64)
110 panic(fmt.Sprintf("got %v while trying to parse %s as float64 (should never happen if the grammar is correct)", err, number))
112 if !match(tag, op, reflect.ValueOf(value), tags) {
116 value, err := strconv.ParseInt(number, 10, 64)
118 panic(fmt.Sprintf("got %v while trying to parse %s as int64 (should never happen if the grammar is correct)", err, number))
120 if !match(tag, op, reflect.ValueOf(value), tags) {
125 value, err := time.Parse(time.RFC3339, buffer[begin:end])
127 panic(fmt.Sprintf("got %v while trying to parse %s as time.Time / RFC3339 (should never happen if the grammar is correct)", err, buffer[begin:end]))
129 if !match(tag, op, reflect.ValueOf(value), tags) {
133 value, err := time.Parse("2006-01-02", buffer[begin:end])
135 panic(fmt.Sprintf("got %v while trying to parse %s as time.Time / '2006-01-02' (should never happen if the grammar is correct)", err, buffer[begin:end]))
137 if !match(tag, op, reflect.ValueOf(value), tags) {
146 // match returns true if the given triplet (tag, operator, operand) matches any tag.
148 // First, it looks up the tag in tags and if it finds one, tries to compare the
149 // value from it to the operand using the operator.
151 // "tx.gas", "=", "7", { "tx.gas": 7, "tx.ID": "4AE393495334" }
152 func match(tag string, op operator, operand reflect.Value, tags map[string]interface{}) bool {
153 // look up the tag from the query in tags
154 value, ok := tags[tag]
158 switch operand.Kind() {
159 case reflect.Struct: // time
160 operandAsTime := operand.Interface().(time.Time)
161 v, ok := value.(time.Time)
162 if !ok { // if value from tags is not time.Time
167 return v.Before(operandAsTime) || v.Equal(operandAsTime)
169 return v.Equal(operandAsTime) || v.After(operandAsTime)
171 return v.Before(operandAsTime)
173 return v.After(operandAsTime)
175 return v.Equal(operandAsTime)
177 case reflect.Float64:
178 operandFloat64 := operand.Interface().(float64)
180 // try our best to convert value from tags to float64
181 switch vt := value.(type) {
196 default: // fail for all other types
197 panic(fmt.Sprintf("Incomparable types: %T (%v) vs float64 (%v)", value, value, operandFloat64))
201 return v <= operandFloat64
203 return v >= operandFloat64
205 return v < operandFloat64
207 return v > operandFloat64
209 return v == operandFloat64
212 operandInt := operand.Interface().(int64)
214 // try our best to convert value from tags to int64
215 switch vt := value.(type) {
230 default: // fail for all other types
231 panic(fmt.Sprintf("Incomparable types: %T (%v) vs int64 (%v)", value, value, operandInt))
235 return v <= operandInt
237 return v >= operandInt
239 return v < operandInt
241 return v > operandInt
243 return v == operandInt
246 v, ok := value.(string)
247 if !ok { // if value from tags is not string
252 return v == operand.String()
254 return strings.Contains(v, operand.String())
257 panic(fmt.Sprintf("Unknown kind of operand %v", operand.Kind()))