github.com/badrootd/celestia-core@v0.0.0-20240305091328-aa4207a4b25d/state/indexer/query_range.go (about)

     1  package indexer
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/badrootd/celestia-core/libs/pubsub/query"
     7  	"github.com/badrootd/celestia-core/types"
     8  )
     9  
    10  // QueryRanges defines a mapping between a composite event key and a QueryRange.
    11  //
    12  // e.g.account.number => queryRange{lowerBound: 1, upperBound: 5}
    13  type QueryRanges map[string]QueryRange
    14  
    15  // QueryRange defines a range within a query condition.
    16  type QueryRange struct {
    17  	LowerBound        interface{} // int || time.Time
    18  	UpperBound        interface{} // int || time.Time
    19  	Key               string
    20  	IncludeLowerBound bool
    21  	IncludeUpperBound bool
    22  }
    23  
    24  // AnyBound returns either the lower bound if non-nil, otherwise the upper bound.
    25  func (qr QueryRange) AnyBound() interface{} {
    26  	if qr.LowerBound != nil {
    27  		return qr.LowerBound
    28  	}
    29  
    30  	return qr.UpperBound
    31  }
    32  
    33  // LowerBoundValue returns the value for the lower bound. If the lower bound is
    34  // nil, nil will be returned.
    35  func (qr QueryRange) LowerBoundValue() interface{} {
    36  	if qr.LowerBound == nil {
    37  		return nil
    38  	}
    39  
    40  	if qr.IncludeLowerBound {
    41  		return qr.LowerBound
    42  	}
    43  
    44  	switch t := qr.LowerBound.(type) {
    45  	case int64:
    46  		return t + 1
    47  
    48  	case time.Time:
    49  		return t.Unix() + 1
    50  
    51  	default:
    52  		panic("not implemented")
    53  	}
    54  }
    55  
    56  // UpperBoundValue returns the value for the upper bound. If the upper bound is
    57  // nil, nil will be returned.
    58  func (qr QueryRange) UpperBoundValue() interface{} {
    59  	if qr.UpperBound == nil {
    60  		return nil
    61  	}
    62  
    63  	if qr.IncludeUpperBound {
    64  		return qr.UpperBound
    65  	}
    66  
    67  	switch t := qr.UpperBound.(type) {
    68  	case int64:
    69  		return t - 1
    70  
    71  	case time.Time:
    72  		return t.Unix() - 1
    73  
    74  	default:
    75  		panic("not implemented")
    76  	}
    77  }
    78  
    79  // LookForRangesWithHeight returns a mapping of QueryRanges and the matching indexes in
    80  // the provided query conditions. If we are matching attributes within events
    81  // we need to remember the height range from the condition
    82  func LookForRangesWithHeight(conditions []query.Condition) (ranges QueryRanges, indexes []int, heightRange QueryRange) {
    83  	ranges = make(QueryRanges)
    84  	for i, c := range conditions {
    85  		heightKey := false
    86  		if IsRangeOperation(c.Op) {
    87  			r, ok := ranges[c.CompositeKey]
    88  			if !ok {
    89  				r = QueryRange{Key: c.CompositeKey}
    90  				if c.CompositeKey == types.BlockHeightKey || c.CompositeKey == types.TxHeightKey {
    91  					heightRange = QueryRange{Key: c.CompositeKey}
    92  					heightKey = true
    93  				}
    94  			}
    95  
    96  			switch c.Op {
    97  			case query.OpGreater:
    98  				if heightKey {
    99  					heightRange.LowerBound = c.Operand
   100  				}
   101  				r.LowerBound = c.Operand
   102  
   103  			case query.OpGreaterEqual:
   104  				r.IncludeLowerBound = true
   105  				r.LowerBound = c.Operand
   106  				if heightKey {
   107  					heightRange.IncludeLowerBound = true
   108  					heightRange.LowerBound = c.Operand
   109  				}
   110  
   111  			case query.OpLess:
   112  				r.UpperBound = c.Operand
   113  				if heightKey {
   114  					heightRange.UpperBound = c.Operand
   115  				}
   116  
   117  			case query.OpLessEqual:
   118  				r.IncludeUpperBound = true
   119  				r.UpperBound = c.Operand
   120  				if heightKey {
   121  					heightRange.IncludeUpperBound = true
   122  					heightRange.UpperBound = c.Operand
   123  				}
   124  			}
   125  
   126  			ranges[c.CompositeKey] = r
   127  			indexes = append(indexes, i)
   128  		}
   129  	}
   130  
   131  	return ranges, indexes, heightRange
   132  }
   133  
   134  // LookForRanges returns a mapping of QueryRanges and the matching indexes in
   135  // the provided query conditions.
   136  //
   137  // Deprecated: This function will be replaced with LookForRangesWithHeight
   138  func LookForRanges(conditions []query.Condition) (ranges QueryRanges, indexes []int) {
   139  	ranges = make(QueryRanges)
   140  	for i, c := range conditions {
   141  		if IsRangeOperation(c.Op) {
   142  			r, ok := ranges[c.CompositeKey]
   143  			if !ok {
   144  				r = QueryRange{Key: c.CompositeKey}
   145  			}
   146  
   147  			switch c.Op {
   148  			case query.OpGreater:
   149  				r.LowerBound = c.Operand
   150  
   151  			case query.OpGreaterEqual:
   152  				r.IncludeLowerBound = true
   153  				r.LowerBound = c.Operand
   154  
   155  			case query.OpLess:
   156  				r.UpperBound = c.Operand
   157  
   158  			case query.OpLessEqual:
   159  				r.IncludeUpperBound = true
   160  				r.UpperBound = c.Operand
   161  			}
   162  
   163  			ranges[c.CompositeKey] = r
   164  			indexes = append(indexes, i)
   165  		}
   166  	}
   167  
   168  	return ranges, indexes
   169  }
   170  
   171  // IsRangeOperation returns a boolean signifying if a query Operator is a range
   172  // operation or not.
   173  func IsRangeOperation(op query.Operator) bool {
   174  	switch op {
   175  	case query.OpGreater, query.OpGreaterEqual, query.OpLess, query.OpLessEqual:
   176  		return true
   177  
   178  	default:
   179  		return false
   180  	}
   181  }