github.com/KYVENetwork/cometbft/v38@v38.0.3/state/indexer/query_range.go (about) 1 package indexer 2 3 import ( 4 "math/big" 5 "time" 6 7 "github.com/KYVENetwork/cometbft/v38/libs/pubsub/query/syntax" 8 "github.com/KYVENetwork/cometbft/v38/types" 9 ) 10 11 // QueryRanges defines a mapping between a composite event key and a QueryRange. 12 // 13 // e.g.account.number => queryRange{lowerBound: 1, upperBound: 5} 14 type QueryRanges map[string]QueryRange 15 16 // QueryRange defines a range within a query condition. 17 type QueryRange struct { 18 LowerBound interface{} // int || time.Time 19 UpperBound interface{} // int || time.Time 20 Key string 21 IncludeLowerBound bool 22 IncludeUpperBound bool 23 } 24 25 // AnyBound returns either the lower bound if non-nil, otherwise the upper bound. 26 func (qr QueryRange) AnyBound() interface{} { 27 if qr.LowerBound != nil { 28 return qr.LowerBound 29 } 30 31 return qr.UpperBound 32 } 33 34 // LowerBoundValue returns the value for the lower bound. If the lower bound is 35 // nil, nil will be returned. 36 func (qr QueryRange) LowerBoundValue() interface{} { 37 if qr.LowerBound == nil { 38 return nil 39 } 40 41 if qr.IncludeLowerBound { 42 return qr.LowerBound 43 } 44 45 switch t := qr.LowerBound.(type) { 46 case int64: 47 return t + 1 48 case *big.Int: 49 tmp := new(big.Int) 50 return tmp.Add(t, big.NewInt(1)) 51 52 case *big.Float: 53 // For floats we cannot simply add one as the float to float 54 // comparison is more finegrained. 55 // When comparing to integers, adding one is also incorrect: 56 // example: x >100.2 ; x = 101 float increased to 101.2 and condition 57 // is not satisfied 58 return t 59 case time.Time: 60 return t.Unix() + 1 61 62 default: 63 panic("not implemented") 64 } 65 } 66 67 // UpperBoundValue returns the value for the upper bound. If the upper bound is 68 // nil, nil will be returned. 69 func (qr QueryRange) UpperBoundValue() interface{} { 70 if qr.UpperBound == nil { 71 return nil 72 } 73 74 if qr.IncludeUpperBound { 75 return qr.UpperBound 76 } 77 78 switch t := qr.UpperBound.(type) { 79 case int64: 80 return t - 1 81 case *big.Int: 82 tmp := new(big.Int) 83 return tmp.Sub(t, big.NewInt(1)) 84 case *big.Float: 85 return t 86 case time.Time: 87 return t.Unix() - 1 88 89 default: 90 panic("not implemented") 91 } 92 } 93 94 // LookForRangesWithHeight returns a mapping of QueryRanges and the matching indexes in 95 // the provided query conditions. 96 func LookForRangesWithHeight(conditions []syntax.Condition) (queryRange QueryRanges, indexes []int, heightRange QueryRange) { 97 queryRange = make(QueryRanges) 98 for i, c := range conditions { 99 if IsRangeOperation(c.Op) { 100 heightKey := c.Tag == types.BlockHeightKey || c.Tag == types.TxHeightKey 101 r, ok := queryRange[c.Tag] 102 if !ok { 103 r = QueryRange{Key: c.Tag} 104 if c.Tag == types.BlockHeightKey || c.Tag == types.TxHeightKey { 105 heightRange = QueryRange{Key: c.Tag} 106 } 107 } 108 109 switch c.Op { 110 case syntax.TGt: 111 if heightKey { 112 heightRange.LowerBound = conditionArg(c) 113 } 114 r.LowerBound = conditionArg(c) 115 116 case syntax.TGeq: 117 r.IncludeLowerBound = true 118 r.LowerBound = conditionArg(c) 119 if heightKey { 120 heightRange.IncludeLowerBound = true 121 heightRange.LowerBound = conditionArg(c) 122 } 123 124 case syntax.TLt: 125 r.UpperBound = conditionArg(c) 126 if heightKey { 127 heightRange.UpperBound = conditionArg(c) 128 } 129 130 case syntax.TLeq: 131 r.IncludeUpperBound = true 132 r.UpperBound = conditionArg(c) 133 if heightKey { 134 heightRange.IncludeUpperBound = true 135 heightRange.UpperBound = conditionArg(c) 136 } 137 } 138 139 queryRange[c.Tag] = r 140 indexes = append(indexes, i) 141 } 142 } 143 144 return queryRange, indexes, heightRange 145 } 146 147 // Deprecated: This function is not used anymore and will be replaced with LookForRangesWithHeight 148 func LookForRanges(conditions []syntax.Condition) (ranges QueryRanges, indexes []int) { 149 ranges = make(QueryRanges) 150 for i, c := range conditions { 151 if IsRangeOperation(c.Op) { 152 r, ok := ranges[c.Tag] 153 if !ok { 154 r = QueryRange{Key: c.Tag} 155 } 156 157 switch c.Op { 158 case syntax.TGt: 159 r.LowerBound = conditionArg(c) 160 161 case syntax.TGeq: 162 r.IncludeLowerBound = true 163 r.LowerBound = conditionArg(c) 164 165 case syntax.TLt: 166 r.UpperBound = conditionArg(c) 167 168 case syntax.TLeq: 169 r.IncludeUpperBound = true 170 r.UpperBound = conditionArg(c) 171 } 172 173 ranges[c.Tag] = r 174 indexes = append(indexes, i) 175 } 176 } 177 178 return ranges, indexes 179 } 180 181 // IsRangeOperation returns a boolean signifying if a query Operator is a range 182 // operation or not. 183 func IsRangeOperation(op syntax.Token) bool { 184 switch op { 185 case syntax.TGt, syntax.TGeq, syntax.TLt, syntax.TLeq: 186 return true 187 188 default: 189 return false 190 } 191 } 192 193 func conditionArg(c syntax.Condition) interface{} { 194 if c.Arg == nil { 195 return nil 196 } 197 switch c.Arg.Type { 198 case syntax.TNumber: 199 return c.Arg.Number() 200 case syntax.TTime, syntax.TDate: 201 return c.Arg.Time() 202 default: 203 return c.Arg.Value() // string 204 } 205 }