package indexer
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/tendermint/tendermint/internal/pubsub/query/syntax"
|
|
)
|
|
|
|
// QueryRanges defines a mapping between a composite event key and a QueryRange.
|
|
//
|
|
// e.g.account.number => queryRange{lowerBound: 1, upperBound: 5}
|
|
type QueryRanges map[string]QueryRange
|
|
|
|
// QueryRange defines a range within a query condition.
|
|
type QueryRange struct {
|
|
LowerBound interface{} // int || time.Time
|
|
UpperBound interface{} // int || time.Time
|
|
Key string
|
|
IncludeLowerBound bool
|
|
IncludeUpperBound bool
|
|
}
|
|
|
|
// AnyBound returns either the lower bound if non-nil, otherwise the upper bound.
|
|
func (qr QueryRange) AnyBound() interface{} {
|
|
if qr.LowerBound != nil {
|
|
return qr.LowerBound
|
|
}
|
|
|
|
return qr.UpperBound
|
|
}
|
|
|
|
// LowerBoundValue returns the value for the lower bound. If the lower bound is
|
|
// nil, nil will be returned.
|
|
func (qr QueryRange) LowerBoundValue() interface{} {
|
|
if qr.LowerBound == nil {
|
|
return nil
|
|
}
|
|
|
|
if qr.IncludeLowerBound {
|
|
return qr.LowerBound
|
|
}
|
|
|
|
switch t := qr.LowerBound.(type) {
|
|
case int64:
|
|
return t + 1
|
|
|
|
case time.Time:
|
|
return t.Unix() + 1
|
|
|
|
default:
|
|
panic("not implemented")
|
|
}
|
|
}
|
|
|
|
// UpperBoundValue returns the value for the upper bound. If the upper bound is
|
|
// nil, nil will be returned.
|
|
func (qr QueryRange) UpperBoundValue() interface{} {
|
|
if qr.UpperBound == nil {
|
|
return nil
|
|
}
|
|
|
|
if qr.IncludeUpperBound {
|
|
return qr.UpperBound
|
|
}
|
|
|
|
switch t := qr.UpperBound.(type) {
|
|
case int64:
|
|
return t - 1
|
|
|
|
case time.Time:
|
|
return t.Unix() - 1
|
|
|
|
default:
|
|
panic("not implemented")
|
|
}
|
|
}
|
|
|
|
// LookForRanges returns a mapping of QueryRanges and the matching indexes in
|
|
// the provided query conditions.
|
|
func LookForRanges(conditions []syntax.Condition) (ranges QueryRanges, indexes []int) {
|
|
ranges = make(QueryRanges)
|
|
for i, c := range conditions {
|
|
if IsRangeOperation(c.Op) {
|
|
r, ok := ranges[c.Tag]
|
|
if !ok {
|
|
r = QueryRange{Key: c.Tag}
|
|
}
|
|
|
|
switch c.Op {
|
|
case syntax.TGt:
|
|
r.LowerBound = conditionArg(c)
|
|
|
|
case syntax.TGeq:
|
|
r.IncludeLowerBound = true
|
|
r.LowerBound = conditionArg(c)
|
|
|
|
case syntax.TLt:
|
|
r.UpperBound = conditionArg(c)
|
|
|
|
case syntax.TLeq:
|
|
r.IncludeUpperBound = true
|
|
r.UpperBound = conditionArg(c)
|
|
}
|
|
|
|
ranges[c.Tag] = r
|
|
indexes = append(indexes, i)
|
|
}
|
|
}
|
|
|
|
return ranges, indexes
|
|
}
|
|
|
|
// IsRangeOperation returns a boolean signifying if a query Operator is a range
|
|
// operation or not.
|
|
func IsRangeOperation(op syntax.Token) bool {
|
|
switch op {
|
|
case syntax.TGt, syntax.TGeq, syntax.TLt, syntax.TLeq:
|
|
return true
|
|
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func conditionArg(c syntax.Condition) interface{} {
|
|
if c.Arg == nil {
|
|
return nil
|
|
}
|
|
switch c.Arg.Type {
|
|
case syntax.TNumber:
|
|
return int64(c.Arg.Number())
|
|
case syntax.TTime, syntax.TDate:
|
|
return c.Arg.Time()
|
|
default:
|
|
return c.Arg.Value() // string
|
|
}
|
|
}
|