@ -22,6 +22,14 @@ type Query struct {
parser * QueryParser
}
// Condition represents a single condition within a query and consists of tag
// (e.g. "tx.gas"), operator (e.g. "=") and operand (e.g. "7").
type Condition struct {
Tag string
Op Operator
Operand interface { }
}
// New parses the given string and returns a query or error if the string is
// invalid.
func New ( s string ) ( * Query , error ) {
@ -48,17 +56,91 @@ func (q *Query) String() string {
return q . str
}
type operator uint8
// Operator is an operator that defines some kind of relation between tag and
// operand (equality, etc.).
type Operator uint8
const (
opLessEqual operator = iota
opGreaterEqual
opLess
opGreater
opEqual
opContains
// "<="
OpLessEqual Operator = iota
// ">="
OpGreaterEqual
// "<"
OpLess
// ">"
OpGreater
// "="
OpEqual
// "CONTAINS"; used to check if a string contains a certain sub string.
OpContains
)
// Conditions returns a list of conditions.
func ( q * Query ) Conditions ( ) [ ] Condition {
conditions := make ( [ ] Condition , 0 )
buffer , begin , end := q . parser . Buffer , 0 , 0
var tag string
var op Operator
// tokens must be in the following order: tag ("tx.gas") -> operator ("=") -> operand ("7")
for _ , token := range q . parser . Tokens ( ) {
switch token . pegRule {
case rulePegText :
begin , end = int ( token . begin ) , int ( token . end )
case ruletag :
tag = buffer [ begin : end ]
case rulele :
op = OpLessEqual
case rulege :
op = OpGreaterEqual
case rulel :
op = OpLess
case ruleg :
op = OpGreater
case ruleequal :
op = OpEqual
case rulecontains :
op = OpContains
case rulevalue :
// strip single quotes from value (i.e. "'NewBlock'" -> "NewBlock")
valueWithoutSingleQuotes := buffer [ begin + 1 : end - 1 ]
conditions = append ( conditions , Condition { tag , op , valueWithoutSingleQuotes } )
case rulenumber :
number := buffer [ begin : end ]
if strings . Contains ( number , "." ) { // if it looks like a floating-point number
value , err := strconv . ParseFloat ( number , 64 )
if err != nil {
panic ( fmt . Sprintf ( "got %v while trying to parse %s as float64 (should never happen if the grammar is correct)" , err , number ) )
}
conditions = append ( conditions , Condition { tag , op , value } )
} else {
value , err := strconv . ParseInt ( number , 10 , 64 )
if err != nil {
panic ( fmt . Sprintf ( "got %v while trying to parse %s as int64 (should never happen if the grammar is correct)" , err , number ) )
}
conditions = append ( conditions , Condition { tag , op , value } )
}
case ruletime :
value , err := time . Parse ( time . RFC3339 , buffer [ begin : end ] )
if err != nil {
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 ] ) )
}
conditions = append ( conditions , Condition { tag , op , value } )
case ruledate :
value , err := time . Parse ( "2006-01-02" , buffer [ begin : end ] )
if err != nil {
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 ] ) )
}
conditions = append ( conditions , Condition { tag , op , value } )
}
}
return conditions
}
// Matches returns true if the query matches the given set of tags, false otherwise.
//
// For example, query "name=John" matches tags = {"name": "John"}. More
@ -71,7 +153,7 @@ func (q *Query) Matches(tags map[string]interface{}) bool {
buffer , begin , end := q . parser . Buffer , 0 , 0
var tag string
var op o perator
var op O perator
// tokens must be in the following order: tag ("tx.gas") -> operator ("=") -> operand ("7")
for _ , token := range q . parser . Tokens ( ) {
@ -82,17 +164,17 @@ func (q *Query) Matches(tags map[string]interface{}) bool {
case ruletag :
tag = buffer [ begin : end ]
case rulele :
op = o pLessEqual
op = O pLessEqual
case rulege :
op = o pGreaterEqual
op = O pGreaterEqual
case rulel :
op = o pLess
op = O pLess
case ruleg :
op = o pGreater
op = O pGreater
case ruleequal :
op = o pEqual
op = O pEqual
case rulecontains :
op = o pContains
op = O pContains
case rulevalue :
// strip single quotes from value (i.e. "'NewBlock'" -> "NewBlock")
valueWithoutSingleQuotes := buffer [ begin + 1 : end - 1 ]
@ -149,7 +231,7 @@ func (q *Query) Matches(tags map[string]interface{}) bool {
// value from it to the operand using the operator.
//
// "tx.gas", "=", "7", { "tx.gas": 7, "tx.ID": "4AE393495334" }
func match ( tag string , op o perator, operand reflect . Value , tags map [ string ] interface { } ) bool {
func match ( tag string , op O perator, operand reflect . Value , tags map [ string ] interface { } ) bool {
// look up the tag from the query in tags
value , ok := tags [ tag ]
if ! ok {
@ -163,15 +245,15 @@ func match(tag string, op operator, operand reflect.Value, tags map[string]inter
return false
}
switch op {
case o pLessEqual:
case O pLessEqual:
return v . Before ( operandAsTime ) || v . Equal ( operandAsTime )
case o pGreaterEqual:
case O pGreaterEqual:
return v . Equal ( operandAsTime ) || v . After ( operandAsTime )
case o pLess:
case O pLess:
return v . Before ( operandAsTime )
case o pGreater:
case O pGreater:
return v . After ( operandAsTime )
case o pEqual:
case O pEqual:
return v . Equal ( operandAsTime )
}
case reflect . Float64 :
@ -197,15 +279,15 @@ func match(tag string, op operator, operand reflect.Value, tags map[string]inter
panic ( fmt . Sprintf ( "Incomparable types: %T (%v) vs float64 (%v)" , value , value , operandFloat64 ) )
}
switch op {
case o pLessEqual:
case O pLessEqual:
return v <= operandFloat64
case o pGreaterEqual:
case O pGreaterEqual:
return v >= operandFloat64
case o pLess:
case O pLess:
return v < operandFloat64
case o pGreater:
case O pGreater:
return v > operandFloat64
case o pEqual:
case O pEqual:
return v == operandFloat64
}
case reflect . Int64 :
@ -231,15 +313,15 @@ func match(tag string, op operator, operand reflect.Value, tags map[string]inter
panic ( fmt . Sprintf ( "Incomparable types: %T (%v) vs int64 (%v)" , value , value , operandInt ) )
}
switch op {
case o pLessEqual:
case O pLessEqual:
return v <= operandInt
case o pGreaterEqual:
case O pGreaterEqual:
return v >= operandInt
case o pLess:
case O pLess:
return v < operandInt
case o pGreater:
case O pGreater:
return v > operandInt
case o pEqual:
case O pEqual:
return v == operandInt
}
case reflect . String :
@ -248,9 +330,9 @@ func match(tag string, op operator, operand reflect.Value, tags map[string]inter
return false
}
switch op {
case o pEqual:
case O pEqual:
return v == operand . String ( )
case o pContains:
case O pContains:
return strings . Contains ( v , operand . String ( ) )
}
default :