You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 lines
2.8 KiB

  1. package indexer
  2. import (
  3. "time"
  4. "github.com/tendermint/tendermint/internal/pubsub/query/syntax"
  5. )
  6. // QueryRanges defines a mapping between a composite event key and a QueryRange.
  7. //
  8. // e.g.account.number => queryRange{lowerBound: 1, upperBound: 5}
  9. type QueryRanges map[string]QueryRange
  10. // QueryRange defines a range within a query condition.
  11. type QueryRange struct {
  12. LowerBound interface{} // int || time.Time
  13. UpperBound interface{} // int || time.Time
  14. Key string
  15. IncludeLowerBound bool
  16. IncludeUpperBound bool
  17. }
  18. // AnyBound returns either the lower bound if non-nil, otherwise the upper bound.
  19. func (qr QueryRange) AnyBound() interface{} {
  20. if qr.LowerBound != nil {
  21. return qr.LowerBound
  22. }
  23. return qr.UpperBound
  24. }
  25. // LowerBoundValue returns the value for the lower bound. If the lower bound is
  26. // nil, nil will be returned.
  27. func (qr QueryRange) LowerBoundValue() interface{} {
  28. if qr.LowerBound == nil {
  29. return nil
  30. }
  31. if qr.IncludeLowerBound {
  32. return qr.LowerBound
  33. }
  34. switch t := qr.LowerBound.(type) {
  35. case int64:
  36. return t + 1
  37. case time.Time:
  38. return t.Unix() + 1
  39. default:
  40. panic("not implemented")
  41. }
  42. }
  43. // UpperBoundValue returns the value for the upper bound. If the upper bound is
  44. // nil, nil will be returned.
  45. func (qr QueryRange) UpperBoundValue() interface{} {
  46. if qr.UpperBound == nil {
  47. return nil
  48. }
  49. if qr.IncludeUpperBound {
  50. return qr.UpperBound
  51. }
  52. switch t := qr.UpperBound.(type) {
  53. case int64:
  54. return t - 1
  55. case time.Time:
  56. return t.Unix() - 1
  57. default:
  58. panic("not implemented")
  59. }
  60. }
  61. // LookForRanges returns a mapping of QueryRanges and the matching indexes in
  62. // the provided query conditions.
  63. func LookForRanges(conditions []syntax.Condition) (ranges QueryRanges, indexes []int) {
  64. ranges = make(QueryRanges)
  65. for i, c := range conditions {
  66. if IsRangeOperation(c.Op) {
  67. r, ok := ranges[c.Tag]
  68. if !ok {
  69. r = QueryRange{Key: c.Tag}
  70. }
  71. switch c.Op {
  72. case syntax.TGt:
  73. r.LowerBound = conditionArg(c)
  74. case syntax.TGeq:
  75. r.IncludeLowerBound = true
  76. r.LowerBound = conditionArg(c)
  77. case syntax.TLt:
  78. r.UpperBound = conditionArg(c)
  79. case syntax.TLeq:
  80. r.IncludeUpperBound = true
  81. r.UpperBound = conditionArg(c)
  82. }
  83. ranges[c.Tag] = r
  84. indexes = append(indexes, i)
  85. }
  86. }
  87. return ranges, indexes
  88. }
  89. // IsRangeOperation returns a boolean signifying if a query Operator is a range
  90. // operation or not.
  91. func IsRangeOperation(op syntax.Token) bool {
  92. switch op {
  93. case syntax.TGt, syntax.TGeq, syntax.TLt, syntax.TLeq:
  94. return true
  95. default:
  96. return false
  97. }
  98. }
  99. func conditionArg(c syntax.Condition) interface{} {
  100. if c.Arg == nil {
  101. return nil
  102. }
  103. switch c.Arg.Type {
  104. case syntax.TNumber:
  105. return int64(c.Arg.Number())
  106. case syntax.TTime, syntax.TDate:
  107. return c.Arg.Time()
  108. default:
  109. return c.Arg.Value() // string
  110. }
  111. }