forked from cosmos/cosmos-sdk
/
filtered_pagination.go
115 lines (90 loc) · 2.54 KB
/
filtered_pagination.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package query
import (
"fmt"
"github.com/cosmos/cosmos-sdk/store/types"
)
// FilteredPaginate does pagination of all the results in the PrefixStore based on the
// provided PageRequest. onResult should be used to do actual unmarshaling and filter the results.
// If key is provided, the pagination uses the optimized querying.
// If offset is used, the pagination uses lazy filtering i.e., searches through all the records.
// The accumulate parameter represents if the response is valid based on the offset given.
// It will be false for the results (filtered) < offset and true for `offset > accumulate <= end`.
// When accumulate is set to true the current result should be appended to the result set returned
// to the client.
func FilteredPaginate(
prefixStore types.KVStore,
pageRequest *PageRequest,
onResult func(key []byte, value []byte, accumulate bool) (bool, error),
) (*PageResponse, error) {
// if the PageRequest is nil, use default PageRequest
if pageRequest == nil {
pageRequest = &PageRequest{}
}
offset := pageRequest.Offset
key := pageRequest.Key
limit := pageRequest.Limit
countTotal := pageRequest.CountTotal
reverse := pageRequest.Reverse
if offset > 0 && key != nil {
return nil, fmt.Errorf("invalid request, either offset or key is expected, got both")
}
if limit == 0 {
limit = DefaultLimit
// count total results when the limit is zero/not supplied
countTotal = true
}
if len(key) != 0 {
iterator := getIterator(prefixStore, key, reverse)
defer iterator.Close()
var numHits uint64
var nextKey []byte
for ; iterator.Valid(); iterator.Next() {
if numHits == limit {
nextKey = iterator.Key()
break
}
if iterator.Error() != nil {
return nil, iterator.Error()
}
hit, err := onResult(iterator.Key(), iterator.Value(), true)
if err != nil {
return nil, err
}
if hit {
numHits++
}
}
return &PageResponse{
NextKey: nextKey,
}, nil
}
iterator := getIterator(prefixStore, nil, reverse)
defer iterator.Close()
end := offset + limit
var numHits uint64
var nextKey []byte
for ; iterator.Valid(); iterator.Next() {
if iterator.Error() != nil {
return nil, iterator.Error()
}
accumulate := numHits >= offset && numHits < end
hit, err := onResult(iterator.Key(), iterator.Value(), accumulate)
if err != nil {
return nil, err
}
if hit {
numHits++
}
if numHits == end+1 {
nextKey = iterator.Key()
if !countTotal {
break
}
}
}
res := &PageResponse{NextKey: nextKey}
if countTotal {
res.Total = numHits
}
return res, nil
}