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 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 := prefixStore.Iterator(key, nil) 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 := prefixStore.Iterator(nil, nil) 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 }