github.com/badrootd/nibiru-cometbft@v0.37.5-0.20240307173500-2a75559eee9b/state/indexer/query_range.go (about)

     1  package indexer
     2  
     3  import (
     4  	"math/big"
     5  	"time"
     6  
     7  	"github.com/badrootd/nibiru-cometbft/libs/pubsub/query"
     8  	"github.com/badrootd/nibiru-cometbft/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 time.Time:
    53  		return t.Unix() + 1
    54  
    55  	default:
    56  		panic("not implemented")
    57  	}
    58  }
    59  
    60  // UpperBoundValue returns the value for the upper bound. If the upper bound is
    61  // nil, nil will be returned.
    62  func (qr QueryRange) UpperBoundValue() interface{} {
    63  	if qr.UpperBound == nil {
    64  		return nil
    65  	}
    66  
    67  	if qr.IncludeUpperBound {
    68  		return qr.UpperBound
    69  	}
    70  
    71  	switch t := qr.UpperBound.(type) {
    72  	case int64:
    73  		return t - 1
    74  	case *big.Int:
    75  		tmp := new(big.Int)
    76  		return tmp.Sub(t, big.NewInt(1))
    77  	case time.Time:
    78  		return t.Unix() - 1
    79  
    80  	default:
    81  		panic("not implemented")
    82  	}
    83  }
    84  
    85  // LookForRangesWithHeight returns a mapping of QueryRanges and the matching indexes in
    86  // the provided query conditions.
    87  func LookForRangesWithHeight(conditions []query.Condition) (queryRange QueryRanges, indexes []int, heightRange QueryRange) {
    88  	queryRange = make(QueryRanges)
    89  	for i, c := range conditions {
    90  		if IsRangeOperation(c.Op) {
    91  			heightKey := c.CompositeKey == types.BlockHeightKey || c.CompositeKey == types.TxHeightKey
    92  			r, ok := queryRange[c.CompositeKey]
    93  			if !ok {
    94  				r = QueryRange{Key: c.CompositeKey}
    95  				if c.CompositeKey == types.BlockHeightKey || c.CompositeKey == types.TxHeightKey {
    96  					heightRange = QueryRange{Key: c.CompositeKey}
    97  				}
    98  			}
    99  
   100  			switch c.Op {
   101  			case query.OpGreater:
   102  				if heightKey {
   103  					heightRange.LowerBound = c.Operand
   104  				}
   105  				r.LowerBound = c.Operand
   106  
   107  			case query.OpGreaterEqual:
   108  				r.IncludeLowerBound = true
   109  				r.LowerBound = c.Operand
   110  				if heightKey {
   111  					heightRange.IncludeLowerBound = true
   112  					heightRange.LowerBound = c.Operand
   113  				}
   114  
   115  			case query.OpLess:
   116  				r.UpperBound = c.Operand
   117  				if heightKey {
   118  					heightRange.UpperBound = c.Operand
   119  				}
   120  
   121  			case query.OpLessEqual:
   122  				r.IncludeUpperBound = true
   123  				r.UpperBound = c.Operand
   124  				if heightKey {
   125  					heightRange.IncludeUpperBound = true
   126  					heightRange.UpperBound = c.Operand
   127  				}
   128  			}
   129  
   130  			queryRange[c.CompositeKey] = r
   131  			indexes = append(indexes, i)
   132  		}
   133  	}
   134  
   135  	return queryRange, indexes, heightRange
   136  }
   137  
   138  // Deprecated: This function is not used anymore and will be replaced with LookForRangesWithHeight
   139  func LookForRanges(conditions []query.Condition) (ranges QueryRanges, indexes []int) {
   140  	ranges = make(QueryRanges)
   141  	for i, c := range conditions {
   142  		if IsRangeOperation(c.Op) {
   143  			r, ok := ranges[c.CompositeKey]
   144  			if !ok {
   145  				r = QueryRange{Key: c.CompositeKey}
   146  			}
   147  
   148  			switch c.Op {
   149  			case query.OpGreater:
   150  				r.LowerBound = c.Operand
   151  
   152  			case query.OpGreaterEqual:
   153  				r.IncludeLowerBound = true
   154  				r.LowerBound = c.Operand
   155  
   156  			case query.OpLess:
   157  				r.UpperBound = c.Operand
   158  
   159  			case query.OpLessEqual:
   160  				r.IncludeUpperBound = true
   161  				r.UpperBound = c.Operand
   162  			}
   163  
   164  			ranges[c.CompositeKey] = r
   165  			indexes = append(indexes, i)
   166  		}
   167  	}
   168  
   169  	return ranges, indexes
   170  }
   171  
   172  // IsRangeOperation returns a boolean signifying if a query Operator is a range
   173  // operation or not.
   174  func IsRangeOperation(op query.Operator) bool {
   175  	switch op {
   176  	case query.OpGreater, query.OpGreaterEqual, query.OpLess, query.OpLessEqual:
   177  		return true
   178  
   179  	default:
   180  		return false
   181  	}
   182  }