feat(orm)!: tweak API to allow pagination in generated code (#11079)
* WIP on fixing issues and expanding tests with codegen * fixes * fix tests * feat(orm)!: pagination codegen and API cleanup * WIP on refactoring list/paginate * fixes * fixes * tests pass * codegen * codegen * Update orm/model/ormtable/index.go Co-authored-by: Marko <marbar3778@yahoo.com> * codegen * Update orm/model/ormtable/filter.go Co-authored-by: Tyler <48813565+technicallyty@users.noreply.github.com> * revert Co-authored-by: Marko <marbar3778@yahoo.com> Co-authored-by: Tyler <48813565+technicallyty@users.noreply.github.com>
This commit is contained in:
co-authored by
Marko
Tyler
parent
894969bace
commit
5126ec3c79
@@ -3,13 +3,14 @@ package queryv1beta1
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
io "io"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
|
||||
runtime "github.com/cosmos/cosmos-proto/runtime"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoiface "google.golang.org/protobuf/runtime/protoiface"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
io "io"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -1223,7 +1224,8 @@ type PageResponse struct {
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// next_key is the key to be passed to PageRequest.key to
|
||||
// query the next page most efficiently
|
||||
// query the next page most efficiently. It will be empty if
|
||||
// there are no more results.
|
||||
NextKey []byte `protobuf:"bytes,1,opt,name=next_key,json=nextKey,proto3" json:"next_key,omitempty"`
|
||||
// total is total number of results available if PageRequest.count_total
|
||||
// was set, its value is undefined otherwise
|
||||
|
||||
@@ -3,15 +3,17 @@ package signingv1beta1
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
io "io"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
|
||||
runtime "github.com/cosmos/cosmos-proto/runtime"
|
||||
v1beta1 "github.com/cosmos/cosmos-sdk/api/cosmos/crypto/multisig/v1beta1"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoiface "google.golang.org/protobuf/runtime/protoiface"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
anypb "google.golang.org/protobuf/types/known/anypb"
|
||||
io "io"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
|
||||
v1beta1 "github.com/cosmos/cosmos-sdk/api/cosmos/crypto/multisig/v1beta1"
|
||||
)
|
||||
|
||||
var _ protoreflect.List = (*_SignatureDescriptors_1_list)(nil)
|
||||
|
||||
@@ -13,8 +13,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
contextPkg = protogen.GoImportPath("context")
|
||||
|
||||
contextPkg = protogen.GoImportPath("context")
|
||||
ormListPkg = protogen.GoImportPath("github.com/cosmos/cosmos-sdk/orm/model/ormlist")
|
||||
ormdbPkg = protogen.GoImportPath("github.com/cosmos/cosmos-sdk/orm/model/ormdb")
|
||||
ormErrPkg = protogen.GoImportPath("github.com/cosmos/cosmos-sdk/orm/types/ormerrors")
|
||||
|
||||
@@ -231,16 +231,14 @@ func (t tableGen) genStoreImpl() {
|
||||
|
||||
// List
|
||||
t.P(receiver, "List(ctx ", contextPkg.Ident("Context"), ", prefixKey ", t.indexKeyInterfaceName(), ", opts ...", ormListPkg.Ident("Option"), ") (", t.iteratorName(), ", error) {")
|
||||
t.P("opts = append(opts, ", ormListPkg.Ident("Prefix"), "(prefixKey.values()...))")
|
||||
t.P("it, err := ", receiverVar, ".table.GetIndexByID(prefixKey.id()).Iterator(ctx, opts...)")
|
||||
t.P("it, err := ", receiverVar, ".table.GetIndexByID(prefixKey.id()).List(ctx, prefixKey.values(), opts...)")
|
||||
t.P("return ", t.iteratorName(), "{it}, err")
|
||||
t.P("}")
|
||||
t.P()
|
||||
|
||||
// ListRange
|
||||
t.P(receiver, "ListRange(ctx ", contextPkg.Ident("Context"), ", from, to ", t.indexKeyInterfaceName(), ", opts ...", ormListPkg.Ident("Option"), ") (", t.iteratorName(), ", error) {")
|
||||
t.P("opts = append(opts, ", ormListPkg.Ident("Start"), "(from.values()...), ", ormListPkg.Ident("End"), "(to.values()...))")
|
||||
t.P("it, err := ", receiverVar, ".table.GetIndexByID(from.id()).Iterator(ctx, opts...)")
|
||||
t.P("it, err := ", receiverVar, ".table.GetIndexByID(from.id()).ListRange(ctx, from.values(), to.values(), opts...)")
|
||||
t.P("return ", t.iteratorName(), "{it}, err")
|
||||
t.P("}")
|
||||
t.P()
|
||||
|
||||
@@ -3,23 +3,24 @@ package listinternal
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// Options is the internal list options struct.
|
||||
type Options struct {
|
||||
Start, End, Prefix []protoreflect.Value
|
||||
Reverse bool
|
||||
Cursor []byte
|
||||
Reverse bool
|
||||
Cursor []byte
|
||||
Filter func(proto.Message) bool
|
||||
Offset, Limit uint64
|
||||
CountTotal bool
|
||||
}
|
||||
|
||||
func (o Options) Validate() error {
|
||||
if o.Start != nil || o.End != nil {
|
||||
if o.Prefix != nil {
|
||||
return fmt.Errorf("can either use Start/End or Prefix, not both")
|
||||
if len(o.Cursor) != 0 {
|
||||
if o.Offset > 0 {
|
||||
return fmt.Errorf("can only specify one of cursor or offset")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -108,14 +108,12 @@ func (this balanceStore) Get(ctx context.Context, address string, denom string)
|
||||
}
|
||||
|
||||
func (this balanceStore) List(ctx context.Context, prefixKey BalanceIndexKey, opts ...ormlist.Option) (BalanceIterator, error) {
|
||||
opts = append(opts, ormlist.Prefix(prefixKey.values()...))
|
||||
it, err := this.table.GetIndexByID(prefixKey.id()).Iterator(ctx, opts...)
|
||||
it, err := this.table.GetIndexByID(prefixKey.id()).List(ctx, prefixKey.values(), opts...)
|
||||
return BalanceIterator{it}, err
|
||||
}
|
||||
|
||||
func (this balanceStore) ListRange(ctx context.Context, from, to BalanceIndexKey, opts ...ormlist.Option) (BalanceIterator, error) {
|
||||
opts = append(opts, ormlist.Start(from.values()...), ormlist.End(to.values()...))
|
||||
it, err := this.table.GetIndexByID(from.id()).Iterator(ctx, opts...)
|
||||
it, err := this.table.GetIndexByID(from.id()).ListRange(ctx, from.values(), to.values(), opts...)
|
||||
return BalanceIterator{it}, err
|
||||
}
|
||||
|
||||
@@ -210,14 +208,12 @@ func (this supplyStore) Get(ctx context.Context, denom string) (*Supply, error)
|
||||
}
|
||||
|
||||
func (this supplyStore) List(ctx context.Context, prefixKey SupplyIndexKey, opts ...ormlist.Option) (SupplyIterator, error) {
|
||||
opts = append(opts, ormlist.Prefix(prefixKey.values()...))
|
||||
it, err := this.table.GetIndexByID(prefixKey.id()).Iterator(ctx, opts...)
|
||||
it, err := this.table.GetIndexByID(prefixKey.id()).List(ctx, prefixKey.values(), opts...)
|
||||
return SupplyIterator{it}, err
|
||||
}
|
||||
|
||||
func (this supplyStore) ListRange(ctx context.Context, from, to SupplyIndexKey, opts ...ormlist.Option) (SupplyIterator, error) {
|
||||
opts = append(opts, ormlist.Start(from.values()...), ormlist.End(to.values()...))
|
||||
it, err := this.table.GetIndexByID(from.id()).Iterator(ctx, opts...)
|
||||
it, err := this.table.GetIndexByID(from.id()).ListRange(ctx, from.values(), to.values(), opts...)
|
||||
return SupplyIterator{it}, err
|
||||
}
|
||||
|
||||
|
||||
@@ -175,14 +175,12 @@ func (this exampleTableStore) GetByU64Str(ctx context.Context, u64 uint64, str s
|
||||
}
|
||||
|
||||
func (this exampleTableStore) List(ctx context.Context, prefixKey ExampleTableIndexKey, opts ...ormlist.Option) (ExampleTableIterator, error) {
|
||||
opts = append(opts, ormlist.Prefix(prefixKey.values()...))
|
||||
it, err := this.table.GetIndexByID(prefixKey.id()).Iterator(ctx, opts...)
|
||||
it, err := this.table.GetIndexByID(prefixKey.id()).List(ctx, prefixKey.values(), opts...)
|
||||
return ExampleTableIterator{it}, err
|
||||
}
|
||||
|
||||
func (this exampleTableStore) ListRange(ctx context.Context, from, to ExampleTableIndexKey, opts ...ormlist.Option) (ExampleTableIterator, error) {
|
||||
opts = append(opts, ormlist.Start(from.values()...), ormlist.End(to.values()...))
|
||||
it, err := this.table.GetIndexByID(from.id()).Iterator(ctx, opts...)
|
||||
it, err := this.table.GetIndexByID(from.id()).ListRange(ctx, from.values(), to.values(), opts...)
|
||||
return ExampleTableIterator{it}, err
|
||||
}
|
||||
|
||||
@@ -314,14 +312,12 @@ func (this exampleAutoIncrementTableStore) GetByX(ctx context.Context, x string)
|
||||
}
|
||||
|
||||
func (this exampleAutoIncrementTableStore) List(ctx context.Context, prefixKey ExampleAutoIncrementTableIndexKey, opts ...ormlist.Option) (ExampleAutoIncrementTableIterator, error) {
|
||||
opts = append(opts, ormlist.Prefix(prefixKey.values()...))
|
||||
it, err := this.table.GetIndexByID(prefixKey.id()).Iterator(ctx, opts...)
|
||||
it, err := this.table.GetIndexByID(prefixKey.id()).List(ctx, prefixKey.values(), opts...)
|
||||
return ExampleAutoIncrementTableIterator{it}, err
|
||||
}
|
||||
|
||||
func (this exampleAutoIncrementTableStore) ListRange(ctx context.Context, from, to ExampleAutoIncrementTableIndexKey, opts ...ormlist.Option) (ExampleAutoIncrementTableIterator, error) {
|
||||
opts = append(opts, ormlist.Start(from.values()...), ormlist.End(to.values()...))
|
||||
it, err := this.table.GetIndexByID(from.id()).Iterator(ctx, opts...)
|
||||
it, err := this.table.GetIndexByID(from.id()).ListRange(ctx, from.values(), to.values(), opts...)
|
||||
return ExampleAutoIncrementTableIterator{it}, err
|
||||
}
|
||||
|
||||
|
||||
@@ -2,61 +2,16 @@
|
||||
package ormlist
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/orm/encoding/encodeutil"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
queryv1beta1 "github.com/cosmos/cosmos-sdk/api/cosmos/base/query/v1beta1"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/orm/internal/listinternal"
|
||||
)
|
||||
|
||||
// Option represents a list option.
|
||||
type Option = listinternal.Option
|
||||
|
||||
// Start defines the values to use to start range iteration. It cannot be
|
||||
// combined with Prefix.
|
||||
//
|
||||
// Values must correspond in type to the index's fields and the number of values
|
||||
// provided cannot exceed the number of fields in the index, although fewer
|
||||
// values can be provided.
|
||||
//
|
||||
// Range iteration can only be done for start and end values which are
|
||||
// well-ordered, meaning that any unordered components must be equal. Ex.
|
||||
// the bytes type is considered unordered, so a range iterator is created
|
||||
// over an index with a bytes field, both start and end must have the same
|
||||
// value for bytes.
|
||||
func Start(values ...interface{}) Option {
|
||||
return listinternal.FuncOption(func(options *listinternal.Options) {
|
||||
options.Start = encodeutil.ValuesOf(values...)
|
||||
})
|
||||
}
|
||||
|
||||
// End defines the values to use to end range iteration. It cannot be
|
||||
// combined with Prefix.
|
||||
//
|
||||
// Values must correspond in type to the index's fields and the number of values
|
||||
// provided cannot exceed the number of fields in the index, although fewer
|
||||
// values can be provided.
|
||||
//
|
||||
// Range iteration can only be done for start and end values which are
|
||||
// well-ordered, meaning that any unordered components must be equal. Ex.
|
||||
// the bytes type is considered unordered, so a range iterator is created
|
||||
// over an index with a bytes field, both start and end must have the same
|
||||
// value for bytes.
|
||||
func End(values ...interface{}) Option {
|
||||
return listinternal.FuncOption(func(options *listinternal.Options) {
|
||||
options.End = encodeutil.ValuesOf(values...)
|
||||
})
|
||||
}
|
||||
|
||||
// Prefix defines values to use for prefix iteration. It cannot be used
|
||||
// together with Start or End.
|
||||
//
|
||||
// Values must correspond in type to the index's fields and the number of values
|
||||
// provided cannot exceed the number of fields in the index, although fewer
|
||||
// values can be provided.
|
||||
func Prefix(values ...interface{}) Option {
|
||||
return listinternal.FuncOption(func(options *listinternal.Options) {
|
||||
options.Prefix = encodeutil.ValuesOf(values...)
|
||||
})
|
||||
}
|
||||
|
||||
// Reverse reverses the direction of iteration. If Reverse is
|
||||
// provided twice, iteration will happen in the forward direction.
|
||||
func Reverse() Option {
|
||||
@@ -65,6 +20,14 @@ func Reverse() Option {
|
||||
})
|
||||
}
|
||||
|
||||
// Filter returns an option which applies a filter function to each item
|
||||
// and skips over it when the filter function returns false.
|
||||
func Filter(filterFn func(message proto.Message) bool) Option {
|
||||
return listinternal.FuncOption(func(options *listinternal.Options) {
|
||||
options.Filter = filterFn
|
||||
})
|
||||
}
|
||||
|
||||
// Cursor specifies a cursor after which to restart iteration. Cursor values
|
||||
// are returned by iterators and in pagination results.
|
||||
func Cursor(cursor CursorT) Option {
|
||||
@@ -73,5 +36,23 @@ func Cursor(cursor CursorT) Option {
|
||||
})
|
||||
}
|
||||
|
||||
// Paginate paginates iterator output based on the provided page request.
|
||||
// The Iterator.PageRequest value on the returned iterator will be non-nil
|
||||
// after Iterator.Next() returns false when this option is provided.
|
||||
// Care should be taken when using Paginate together with Reverse and/or Cursor
|
||||
// and generally this should be avoided.
|
||||
func Paginate(pageRequest *queryv1beta1.PageRequest) Option {
|
||||
return listinternal.FuncOption(func(options *listinternal.Options) {
|
||||
if pageRequest.Reverse {
|
||||
options.Reverse = !options.Reverse
|
||||
}
|
||||
|
||||
options.Cursor = pageRequest.Key
|
||||
options.Offset = pageRequest.Offset
|
||||
options.Limit = pageRequest.Limit
|
||||
options.CountTotal = pageRequest.CountTotal
|
||||
})
|
||||
}
|
||||
|
||||
// CursorT defines a cursor type.
|
||||
type CursorT []byte
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package ormtable
|
||||
|
||||
import "google.golang.org/protobuf/proto"
|
||||
|
||||
type filterIterator struct {
|
||||
Iterator
|
||||
filter func(proto.Message) bool
|
||||
msg proto.Message
|
||||
}
|
||||
|
||||
func (f *filterIterator) Next() bool {
|
||||
for f.Iterator.Next() {
|
||||
msg, err := f.Iterator.GetMessage()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if f.filter(msg) {
|
||||
f.msg = msg
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f filterIterator) GetMessage() (proto.Message, error) {
|
||||
return f.msg, nil
|
||||
}
|
||||
@@ -16,8 +16,27 @@ import (
|
||||
// to index methods.
|
||||
type Index interface {
|
||||
|
||||
// Iterator returns an iterator for this index with the provided list options.
|
||||
Iterator(ctx context.Context, options ...ormlist.Option) (Iterator, error)
|
||||
// List does iteration over the index with the provided prefix key and options.
|
||||
// Prefix key values must correspond in type to the index's fields and the
|
||||
// number of values provided cannot exceed the number of fields in the index,
|
||||
// although fewer values can be provided.
|
||||
List(ctx context.Context, prefixKey []interface{}, options ...ormlist.Option) (Iterator, error)
|
||||
|
||||
// ListRange does range iteration over the index with the provided from and to
|
||||
// values and options.
|
||||
//
|
||||
// From and to values must correspond in type to the index's fields and the number of values
|
||||
// provided cannot exceed the number of fields in the index, although fewer
|
||||
// values can be provided.
|
||||
//
|
||||
// Range iteration can only be done for from and to values which are
|
||||
// well-ordered, meaning that any unordered components must be equal. Ex.
|
||||
// the bytes type is considered unordered, so a range iterator is created
|
||||
// over an index with a bytes field, both start and end must have the same
|
||||
// value for bytes.
|
||||
//
|
||||
// Range iteration is inclusive at both ends.
|
||||
ListRange(ctx context.Context, from, to []interface{}, options ...ormlist.Option) (Iterator, error)
|
||||
|
||||
// MessageType returns the protobuf message type of the index.
|
||||
MessageType() protoreflect.MessageType
|
||||
|
||||
@@ -24,13 +24,22 @@ type indexKeyIndex struct {
|
||||
getReadBackend func(context.Context) (ReadBackend, error)
|
||||
}
|
||||
|
||||
func (i indexKeyIndex) Iterator(ctx context.Context, options ...ormlist.Option) (Iterator, error) {
|
||||
func (i indexKeyIndex) List(ctx context.Context, prefixKey []interface{}, options ...ormlist.Option) (Iterator, error) {
|
||||
backend, err := i.getReadBackend(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return iterator(backend, backend.IndexStoreReader(), i, i.KeyCodec, options)
|
||||
return prefixIterator(backend.IndexStoreReader(), backend, i, i.KeyCodec, prefixKey, options)
|
||||
}
|
||||
|
||||
func (i indexKeyIndex) ListRange(ctx context.Context, from, to []interface{}, options ...ormlist.Option) (Iterator, error) {
|
||||
backend, err := i.getReadBackend(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rangeIterator(backend.IndexStoreReader(), backend, i, i.KeyCodec, from, to, options)
|
||||
}
|
||||
|
||||
var _ indexer = &indexKeyIndex{}
|
||||
|
||||
@@ -4,6 +4,10 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/orm/encoding/encodeutil"
|
||||
|
||||
queryv1beta1 "github.com/cosmos/cosmos-sdk/api/cosmos/base/query/v1beta1"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/orm/encoding/ormkv"
|
||||
"github.com/cosmos/cosmos-sdk/orm/internal/listinternal"
|
||||
"github.com/cosmos/cosmos-sdk/orm/model/kv"
|
||||
@@ -33,6 +37,10 @@ type Iterator interface {
|
||||
// and can be used to restart iteration right after this position.
|
||||
Cursor() ormlist.CursorT
|
||||
|
||||
// PageResponse returns a non-nil page response after Next() returns false
|
||||
// if pagination was requested in list options.
|
||||
PageResponse() *queryv1beta1.PageResponse
|
||||
|
||||
// Close closes the iterator and must always be called when done using
|
||||
// the iterator. The defer keyword should generally be used for this.
|
||||
Close()
|
||||
@@ -40,136 +48,149 @@ type Iterator interface {
|
||||
doNotImplement()
|
||||
}
|
||||
|
||||
func iterator(
|
||||
backend ReadBackend,
|
||||
reader kv.ReadonlyStore,
|
||||
index concreteIndex,
|
||||
codec *ormkv.KeyCodec,
|
||||
options []listinternal.Option,
|
||||
) (Iterator, error) {
|
||||
opts := &listinternal.Options{}
|
||||
listinternal.ApplyOptions(opts, options)
|
||||
if err := opts.Validate(); err != nil {
|
||||
func prefixIterator(iteratorStore kv.ReadonlyStore, backend ReadBackend, index concreteIndex, codec *ormkv.KeyCodec, prefix []interface{}, opts []listinternal.Option) (Iterator, error) {
|
||||
options := &listinternal.Options{}
|
||||
listinternal.ApplyOptions(options, opts)
|
||||
if err := options.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts.Start != nil || opts.End != nil {
|
||||
err := codec.CheckValidRangeIterationKeys(opts.Start, opts.End)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
startBz, err := codec.EncodeKey(opts.Start)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
endBz, err := codec.EncodeKey(opts.End)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fullEndKey := len(codec.GetFieldNames()) == len(opts.End)
|
||||
|
||||
return rangeIterator(reader, backend, index, startBz, endBz, fullEndKey, opts)
|
||||
} else {
|
||||
prefixBz, err := codec.EncodeKey(opts.Prefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return prefixIterator(reader, backend, index, prefixBz, opts)
|
||||
var prefixBz []byte
|
||||
prefixBz, err := codec.EncodeKey(encodeutil.ValuesOf(prefix...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func prefixIterator(iteratorStore kv.ReadonlyStore, backend ReadBackend, index concreteIndex, prefix []byte, options *listinternal.Options) (Iterator, error) {
|
||||
var res Iterator
|
||||
if !options.Reverse {
|
||||
var start []byte
|
||||
if len(options.Cursor) != 0 {
|
||||
// must start right after cursor
|
||||
start = append(options.Cursor, 0x0)
|
||||
} else {
|
||||
start = prefix
|
||||
start = prefixBz
|
||||
}
|
||||
end := prefixEndBytes(prefix)
|
||||
end := prefixEndBytes(prefixBz)
|
||||
it, err := iteratorStore.Iterator(start, end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &indexIterator{
|
||||
res = &indexIterator{
|
||||
index: index,
|
||||
store: backend,
|
||||
iterator: it,
|
||||
started: false,
|
||||
}, nil
|
||||
}
|
||||
} else {
|
||||
var end []byte
|
||||
if len(options.Cursor) != 0 {
|
||||
// end bytes is already exclusive by default
|
||||
end = options.Cursor
|
||||
} else {
|
||||
end = prefixEndBytes(prefix)
|
||||
end = prefixEndBytes(prefixBz)
|
||||
}
|
||||
it, err := iteratorStore.ReverseIterator(prefix, end)
|
||||
it, err := iteratorStore.ReverseIterator(prefixBz, end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &indexIterator{
|
||||
res = &indexIterator{
|
||||
index: index,
|
||||
store: backend,
|
||||
iterator: it,
|
||||
started: false,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return applyCommonIteratorOptions(res, options)
|
||||
}
|
||||
|
||||
// NOTE: fullEndKey indicates whether the end key contained all the fields of the key,
|
||||
// if it did then we need to use inclusive end bytes, otherwise we prefix the end bytes
|
||||
func rangeIterator(iteratorStore kv.ReadonlyStore, reader ReadBackend, index concreteIndex, start, end []byte, fullEndKey bool, options *listinternal.Options) (Iterator, error) {
|
||||
func rangeIterator(iteratorStore kv.ReadonlyStore, reader ReadBackend, index concreteIndex, codec *ormkv.KeyCodec, start, end []interface{}, opts []listinternal.Option) (Iterator, error) {
|
||||
options := &listinternal.Options{}
|
||||
listinternal.ApplyOptions(options, opts)
|
||||
if err := options.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
startValues := encodeutil.ValuesOf(start...)
|
||||
endValues := encodeutil.ValuesOf(end...)
|
||||
err := codec.CheckValidRangeIterationKeys(startValues, endValues)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
startBz, err := codec.EncodeKey(startValues)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
endBz, err := codec.EncodeKey(endValues)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// NOTE: fullEndKey indicates whether the end key contained all the fields of the key,
|
||||
// if it did then we need to use inclusive end bytes, otherwise we prefix the end bytes
|
||||
fullEndKey := len(codec.GetFieldNames()) == len(end)
|
||||
|
||||
var res Iterator
|
||||
if !options.Reverse {
|
||||
if len(options.Cursor) != 0 {
|
||||
start = append(options.Cursor, 0)
|
||||
startBz = append(options.Cursor, 0)
|
||||
}
|
||||
|
||||
if fullEndKey {
|
||||
end = inclusiveEndBytes(end)
|
||||
endBz = inclusiveEndBytes(endBz)
|
||||
} else {
|
||||
end = prefixEndBytes(end)
|
||||
endBz = prefixEndBytes(endBz)
|
||||
}
|
||||
|
||||
it, err := iteratorStore.Iterator(start, end)
|
||||
it, err := iteratorStore.Iterator(startBz, endBz)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &indexIterator{
|
||||
res = &indexIterator{
|
||||
index: index,
|
||||
store: reader,
|
||||
iterator: it,
|
||||
started: false,
|
||||
}, nil
|
||||
}
|
||||
} else {
|
||||
if len(options.Cursor) != 0 {
|
||||
end = options.Cursor
|
||||
endBz = options.Cursor
|
||||
} else {
|
||||
if fullEndKey {
|
||||
end = inclusiveEndBytes(end)
|
||||
endBz = inclusiveEndBytes(endBz)
|
||||
} else {
|
||||
end = prefixEndBytes(end)
|
||||
endBz = prefixEndBytes(endBz)
|
||||
}
|
||||
}
|
||||
it, err := iteratorStore.ReverseIterator(start, end)
|
||||
it, err := iteratorStore.ReverseIterator(startBz, endBz)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &indexIterator{
|
||||
res = &indexIterator{
|
||||
index: index,
|
||||
store: reader,
|
||||
iterator: it,
|
||||
started: false,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return applyCommonIteratorOptions(res, options)
|
||||
}
|
||||
|
||||
func applyCommonIteratorOptions(iterator Iterator, options *listinternal.Options) (Iterator, error) {
|
||||
if options.Filter != nil {
|
||||
iterator = &filterIterator{Iterator: iterator, filter: options.Filter}
|
||||
}
|
||||
|
||||
if options.CountTotal || options.Limit != 0 || options.Offset != 0 {
|
||||
iterator = paginate(iterator, options)
|
||||
}
|
||||
|
||||
return iterator, nil
|
||||
}
|
||||
|
||||
type indexIterator struct {
|
||||
@@ -183,6 +204,10 @@ type indexIterator struct {
|
||||
started bool
|
||||
}
|
||||
|
||||
func (i *indexIterator) PageResponse() *queryv1beta1.PageResponse {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *indexIterator) Next() bool {
|
||||
if !i.started {
|
||||
i.started = true
|
||||
@@ -232,8 +257,6 @@ func (i indexIterator) Close() {
|
||||
}
|
||||
}
|
||||
|
||||
func (indexIterator) doNotImplement() {
|
||||
|
||||
}
|
||||
func (indexIterator) doNotImplement() {}
|
||||
|
||||
var _ Iterator = &indexIterator{}
|
||||
|
||||
+64
-103
@@ -1,124 +1,85 @@
|
||||
package ormtable
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
"github.com/cosmos/cosmos-sdk/orm/internal/listinternal"
|
||||
|
||||
queryv1beta1 "github.com/cosmos/cosmos-sdk/api/cosmos/base/query/v1beta1"
|
||||
"github.com/cosmos/cosmos-sdk/orm/model/ormlist"
|
||||
)
|
||||
|
||||
// PaginationRequest is a request to the Paginate function and extends the
|
||||
// options in query.PageRequest.
|
||||
type PaginationRequest struct {
|
||||
*queryv1beta1.PageRequest
|
||||
|
||||
// Filter is an optional filter function that can be used to filter
|
||||
// the results in the underlying iterator and should return true to include
|
||||
// an item in the result.
|
||||
Filter func(message proto.Message) bool
|
||||
}
|
||||
|
||||
// PaginationResponse is a response from the Paginate function and extends the
|
||||
// options in query.PageResponse.
|
||||
type PaginationResponse struct {
|
||||
*queryv1beta1.PageResponse
|
||||
|
||||
// HaveMore indicates whether there are more pages.
|
||||
HaveMore bool
|
||||
|
||||
// Cursors returns a cursor for each item and can be used to implement
|
||||
// GraphQL connection edges.
|
||||
Cursors []ormlist.CursorT
|
||||
}
|
||||
|
||||
// Paginate retrieves a "page" of data from the provided index and context.
|
||||
func Paginate(
|
||||
index Index,
|
||||
ctx context.Context,
|
||||
request *PaginationRequest,
|
||||
onItem func(proto.Message),
|
||||
options ...ormlist.Option,
|
||||
) (*PaginationResponse, error) {
|
||||
offset := int(request.Offset)
|
||||
if len(request.Key) != 0 {
|
||||
if offset > 0 {
|
||||
return nil, fmt.Errorf("can only specify one of cursor or offset")
|
||||
}
|
||||
|
||||
options = append(options, ormlist.Cursor(request.Key))
|
||||
}
|
||||
|
||||
if request.Reverse {
|
||||
options = append(options, ormlist.Reverse())
|
||||
}
|
||||
|
||||
it, err := index.Iterator(ctx, options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer it.Close()
|
||||
|
||||
limit := int(request.Limit)
|
||||
if limit == 0 {
|
||||
return nil, fmt.Errorf("limit not specified")
|
||||
}
|
||||
func paginate(it Iterator, options *listinternal.Options) Iterator {
|
||||
offset := int(options.Offset)
|
||||
limit := int(options.Limit)
|
||||
|
||||
i := 0
|
||||
if offset != 0 {
|
||||
for ; i < offset; i++ {
|
||||
if !it.Next() {
|
||||
return &PaginationResponse{
|
||||
PageResponse: &queryv1beta1.PageResponse{Total: uint64(i)},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
haveMore := false
|
||||
cursors := make([]ormlist.CursorT, 0, limit)
|
||||
done := limit + offset
|
||||
for it.Next() {
|
||||
if i == done {
|
||||
haveMore = true
|
||||
if request.CountTotal {
|
||||
for {
|
||||
i++
|
||||
if !it.Next() {
|
||||
break
|
||||
}
|
||||
return &paginationIterator{
|
||||
Iterator: it,
|
||||
pageRes: &queryv1beta1.PageResponse{Total: uint64(i)},
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
message, err := it.GetMessage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if request.Filter != nil && !request.Filter(message) {
|
||||
continue
|
||||
}
|
||||
|
||||
i++
|
||||
cursors = append(cursors, it.Cursor())
|
||||
onItem(message)
|
||||
}
|
||||
|
||||
pageRes := &queryv1beta1.PageResponse{}
|
||||
if request.CountTotal {
|
||||
pageRes.Total = uint64(i)
|
||||
var done int
|
||||
if limit != 0 {
|
||||
done = limit + offset
|
||||
} else {
|
||||
done = math.MaxInt
|
||||
}
|
||||
n := len(cursors)
|
||||
if n != 0 {
|
||||
pageRes.NextKey = cursors[n-1]
|
||||
|
||||
return &paginationIterator{
|
||||
Iterator: it,
|
||||
pageRes: nil,
|
||||
countTotal: options.CountTotal,
|
||||
i: i,
|
||||
done: done,
|
||||
}
|
||||
return &PaginationResponse{
|
||||
PageResponse: pageRes,
|
||||
HaveMore: haveMore,
|
||||
Cursors: cursors,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type paginationIterator struct {
|
||||
Iterator
|
||||
pageRes *queryv1beta1.PageResponse
|
||||
countTotal bool
|
||||
i int
|
||||
done int
|
||||
}
|
||||
|
||||
func (it *paginationIterator) Next() bool {
|
||||
if it.i >= it.done {
|
||||
it.pageRes = &queryv1beta1.PageResponse{}
|
||||
cursor := it.Cursor()
|
||||
if it.Iterator.Next() {
|
||||
it.pageRes.NextKey = cursor
|
||||
it.i++
|
||||
}
|
||||
if it.countTotal {
|
||||
for {
|
||||
if !it.Iterator.Next() {
|
||||
it.pageRes.Total = uint64(it.i)
|
||||
return false
|
||||
}
|
||||
it.i++
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
ok := it.Iterator.Next()
|
||||
if ok {
|
||||
it.i++
|
||||
return true
|
||||
} else {
|
||||
it.pageRes = &queryv1beta1.PageResponse{
|
||||
Total: uint64(it.i),
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (it paginationIterator) PageResponse() *queryv1beta1.PageResponse {
|
||||
return it.pageRes
|
||||
}
|
||||
|
||||
@@ -24,13 +24,22 @@ type primaryKeyIndex struct {
|
||||
getReadBackend func(context.Context) (ReadBackend, error)
|
||||
}
|
||||
|
||||
func (p primaryKeyIndex) Iterator(ctx context.Context, options ...ormlist.Option) (Iterator, error) {
|
||||
func (p primaryKeyIndex) List(ctx context.Context, prefixKey []interface{}, options ...ormlist.Option) (Iterator, error) {
|
||||
backend, err := p.getReadBackend(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return iterator(backend, backend.CommitmentStoreReader(), p, p.KeyCodec, options)
|
||||
return prefixIterator(backend.CommitmentStoreReader(), backend, p, p.KeyCodec, prefixKey, options)
|
||||
}
|
||||
|
||||
func (p primaryKeyIndex) ListRange(ctx context.Context, from, to []interface{}, options ...ormlist.Option) (Iterator, error) {
|
||||
backend, err := p.getReadBackend(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rangeIterator(backend.CommitmentStoreReader(), backend, p, p.KeyCodec, from, to, options)
|
||||
}
|
||||
|
||||
func (p primaryKeyIndex) doNotImplement() {}
|
||||
@@ -71,23 +80,23 @@ func (p primaryKeyIndex) get(backend ReadBackend, message proto.Message, values
|
||||
return p.getByKeyBytes(backend, key, values, message)
|
||||
}
|
||||
|
||||
func (t primaryKeyIndex) DeleteByKey(ctx context.Context, primaryKeyValues ...interface{}) error {
|
||||
return t.doDeleteByKey(ctx, encodeutil.ValuesOf(primaryKeyValues...))
|
||||
func (p primaryKeyIndex) DeleteByKey(ctx context.Context, primaryKeyValues ...interface{}) error {
|
||||
return p.doDeleteByKey(ctx, encodeutil.ValuesOf(primaryKeyValues...))
|
||||
}
|
||||
|
||||
func (t primaryKeyIndex) doDeleteByKey(ctx context.Context, primaryKeyValues []protoreflect.Value) error {
|
||||
backend, err := t.getBackend(ctx)
|
||||
func (p primaryKeyIndex) doDeleteByKey(ctx context.Context, primaryKeyValues []protoreflect.Value) error {
|
||||
backend, err := p.getBackend(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pk, err := t.EncodeKey(primaryKeyValues)
|
||||
pk, err := p.EncodeKey(primaryKeyValues)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := t.MessageType().New().Interface()
|
||||
found, err := t.getByKeyBytes(backend, pk, primaryKeyValues, msg)
|
||||
msg := p.MessageType().New().Interface()
|
||||
found, err := p.getByKeyBytes(backend, pk, primaryKeyValues, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -114,7 +123,7 @@ func (t primaryKeyIndex) doDeleteByKey(ctx context.Context, primaryKeyValues []p
|
||||
// clear indexes
|
||||
mref := msg.ProtoReflect()
|
||||
indexStoreWriter := writer.IndexStore()
|
||||
for _, idx := range t.indexers {
|
||||
for _, idx := range p.indexers {
|
||||
err := idx.onDelete(indexStoreWriter, mref)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -303,7 +303,7 @@ func (t tableImpl) doExportJSON(ctx context.Context, writer io.Writer) error {
|
||||
}
|
||||
|
||||
var err error
|
||||
it, _ := t.Iterator(ctx)
|
||||
it, _ := t.List(ctx, nil)
|
||||
start := true
|
||||
for {
|
||||
found := it.Next()
|
||||
|
||||
@@ -183,6 +183,23 @@ func runTestScenario(t *testing.T, table ormtable.Table, backend ormtable.Backen
|
||||
)
|
||||
assertIteratorItems(it, 2, 0)
|
||||
|
||||
// try filtering
|
||||
it, err = store.List(ctx, testpb.ExampleTablePrimaryKey{}, ormlist.Filter(func(message proto.Message) bool {
|
||||
ex := message.(*testpb.ExampleTable)
|
||||
return ex.U64 != 10
|
||||
}))
|
||||
assert.NilError(t, err)
|
||||
assertIteratorItems(it, 0, 1, 2, 3, 4, 6, 7, 8)
|
||||
|
||||
// try a cursor
|
||||
it, err = store.List(ctx, testpb.ExampleTablePrimaryKey{})
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, it.Next())
|
||||
assert.Assert(t, it.Next())
|
||||
it, err = store.List(ctx, testpb.ExampleTablePrimaryKey{}, ormlist.Cursor(it.Cursor()))
|
||||
assert.NilError(t, err)
|
||||
assertIteratorItems(it, 2, 3, 4, 5, 6, 7, 8, 9)
|
||||
|
||||
// try an unique index
|
||||
found, err := store.HasByU64Str(ctx, 12, "abc")
|
||||
assert.NilError(t, err)
|
||||
@@ -192,170 +209,115 @@ func runTestScenario(t *testing.T, table ormtable.Table, backend ormtable.Backen
|
||||
assert.DeepEqual(t, data[8], a, protocmp.Transform())
|
||||
|
||||
// let's try paginating some stuff
|
||||
|
||||
// first create a function to test what we got from pagination
|
||||
assertGotItems := func(items []proto.Message, xs ...int) {
|
||||
n := len(xs)
|
||||
assert.Equal(t, n, len(items))
|
||||
for i := 0; i < n; i++ {
|
||||
j := xs[i]
|
||||
//t.Logf("data[%d] %v == %v", j, data[j], items[i])
|
||||
assert.DeepEqual(t, data[j], items[i], protocmp.Transform())
|
||||
}
|
||||
}
|
||||
|
||||
// now do some pagination
|
||||
var items []proto.Message
|
||||
onItem := func(item proto.Message) {
|
||||
items = append(items, item)
|
||||
}
|
||||
res, err := ormtable.Paginate(table, ctx, &ormtable.PaginationRequest{
|
||||
PageRequest: &queryv1beta1.PageRequest{
|
||||
Limit: 4,
|
||||
CountTotal: true,
|
||||
}}, onItem)
|
||||
it, err = store.List(ctx, testpb.ExampleTablePrimaryKey{}, ormlist.Paginate(&queryv1beta1.PageRequest{
|
||||
Limit: 4,
|
||||
CountTotal: true,
|
||||
}))
|
||||
assert.NilError(t, err)
|
||||
assertIteratorItems(it, 0, 1, 2, 3)
|
||||
res := it.PageResponse()
|
||||
assert.Assert(t, res != nil)
|
||||
assert.Equal(t, uint64(10), res.Total)
|
||||
assert.Assert(t, res.NextKey != nil)
|
||||
assert.Assert(t, res.HaveMore)
|
||||
assert.Equal(t, 4, len(res.Cursors))
|
||||
assertGotItems(items, 0, 1, 2, 3)
|
||||
|
||||
// read another page
|
||||
items = nil
|
||||
res, err = ormtable.Paginate(table, ctx, &ormtable.PaginationRequest{
|
||||
PageRequest: &queryv1beta1.PageRequest{
|
||||
Key: res.NextKey,
|
||||
Limit: 4,
|
||||
}}, onItem)
|
||||
it, err = store.List(ctx, testpb.ExampleTablePrimaryKey{}, ormlist.Paginate(&queryv1beta1.PageRequest{
|
||||
Key: res.NextKey,
|
||||
Limit: 4,
|
||||
}))
|
||||
assert.NilError(t, err)
|
||||
assertIteratorItems(it, 4, 5, 6, 7)
|
||||
res = it.PageResponse()
|
||||
assert.Assert(t, res != nil)
|
||||
assert.Assert(t, res.NextKey != nil)
|
||||
assert.Assert(t, res.HaveMore)
|
||||
assert.Equal(t, 4, len(res.Cursors))
|
||||
assertGotItems(items, 4, 5, 6, 7)
|
||||
|
||||
// and the last page
|
||||
items = nil
|
||||
res, err = ormtable.Paginate(table, ctx, &ormtable.PaginationRequest{
|
||||
PageRequest: &queryv1beta1.PageRequest{
|
||||
Key: res.NextKey,
|
||||
Limit: 4,
|
||||
}}, onItem)
|
||||
it, err = store.List(ctx, testpb.ExampleTablePrimaryKey{}, ormlist.Paginate(&queryv1beta1.PageRequest{
|
||||
Key: res.NextKey,
|
||||
Limit: 4,
|
||||
}))
|
||||
assert.NilError(t, err)
|
||||
assertIteratorItems(it, 8, 9)
|
||||
res = it.PageResponse()
|
||||
assert.Assert(t, res != nil)
|
||||
assert.Assert(t, res.NextKey != nil)
|
||||
assert.Assert(t, !res.HaveMore)
|
||||
assert.Equal(t, 2, len(res.Cursors))
|
||||
assertGotItems(items, 8, 9)
|
||||
assert.Assert(t, res.NextKey == nil)
|
||||
|
||||
// let's go backwards
|
||||
items = nil
|
||||
res, err = ormtable.Paginate(table, ctx, &ormtable.PaginationRequest{
|
||||
PageRequest: &queryv1beta1.PageRequest{
|
||||
Limit: 2,
|
||||
CountTotal: true,
|
||||
Reverse: true,
|
||||
}}, onItem)
|
||||
it, err = store.List(ctx, testpb.ExampleTablePrimaryKey{}, ormlist.Paginate(&queryv1beta1.PageRequest{
|
||||
Limit: 2,
|
||||
CountTotal: true,
|
||||
Reverse: true,
|
||||
}))
|
||||
assert.NilError(t, err)
|
||||
assertIteratorItems(it, 9, 8)
|
||||
res = it.PageResponse()
|
||||
assert.Assert(t, res != nil)
|
||||
assert.Assert(t, res.NextKey != nil)
|
||||
assert.Equal(t, uint64(10), res.Total)
|
||||
assert.Assert(t, res.HaveMore)
|
||||
assert.Equal(t, 2, len(res.Cursors))
|
||||
assertGotItems(items, 9, 8)
|
||||
|
||||
// a bit more
|
||||
items = nil
|
||||
res, err = ormtable.Paginate(table, ctx, &ormtable.PaginationRequest{
|
||||
PageRequest: &queryv1beta1.PageRequest{
|
||||
Key: res.NextKey,
|
||||
Limit: 2,
|
||||
Reverse: true,
|
||||
}}, onItem)
|
||||
it, err = store.List(ctx, testpb.ExampleTablePrimaryKey{}, ormlist.Paginate(&queryv1beta1.PageRequest{
|
||||
Key: res.NextKey,
|
||||
Limit: 2,
|
||||
Reverse: true,
|
||||
}))
|
||||
assert.NilError(t, err)
|
||||
assertIteratorItems(it, 7, 6)
|
||||
res = it.PageResponse()
|
||||
assert.Assert(t, res != nil)
|
||||
assert.Assert(t, res.NextKey != nil)
|
||||
assert.Assert(t, res.HaveMore)
|
||||
assert.Equal(t, 2, len(res.Cursors))
|
||||
assertGotItems(items, 7, 6)
|
||||
|
||||
// range query
|
||||
items = nil
|
||||
res, err = ormtable.Paginate(table, ctx,
|
||||
&ormtable.PaginationRequest{
|
||||
PageRequest: &queryv1beta1.PageRequest{
|
||||
Limit: 10,
|
||||
},
|
||||
},
|
||||
onItem,
|
||||
ormlist.Start(uint32(4), int64(-1), "abc"),
|
||||
ormlist.End(uint32(7), int64(-2), "abe"),
|
||||
)
|
||||
it, err = store.ListRange(ctx,
|
||||
testpb.ExampleTablePrimaryKey{}.WithU32I64Str(4, -1, "abc"),
|
||||
testpb.ExampleTablePrimaryKey{}.WithU32I64Str(7, -2, "abe"),
|
||||
ormlist.Paginate(&queryv1beta1.PageRequest{
|
||||
Limit: 10,
|
||||
}))
|
||||
assert.NilError(t, err)
|
||||
assertIteratorItems(it, 2, 3, 4, 5)
|
||||
res = it.PageResponse()
|
||||
assert.Assert(t, res != nil)
|
||||
assert.Assert(t, !res.HaveMore)
|
||||
assert.Equal(t, 4, len(res.Cursors))
|
||||
assertGotItems(items, 2, 3, 4, 5)
|
||||
assert.Assert(t, res.NextKey == nil)
|
||||
|
||||
// let's try an offset
|
||||
items = nil
|
||||
res, err = ormtable.Paginate(table, ctx, &ormtable.PaginationRequest{
|
||||
PageRequest: &queryv1beta1.PageRequest{
|
||||
Limit: 2,
|
||||
CountTotal: true,
|
||||
Offset: 3,
|
||||
}}, onItem)
|
||||
it, err = store.List(ctx, testpb.ExampleTablePrimaryKey{}, ormlist.Paginate(&queryv1beta1.PageRequest{
|
||||
Limit: 2,
|
||||
CountTotal: true,
|
||||
Offset: 3,
|
||||
}))
|
||||
assert.NilError(t, err)
|
||||
assertIteratorItems(it, 3, 4)
|
||||
res = it.PageResponse()
|
||||
assert.Assert(t, res != nil)
|
||||
assert.Assert(t, res.NextKey != nil)
|
||||
assert.Equal(t, uint64(10), res.Total)
|
||||
assert.Assert(t, res.HaveMore)
|
||||
assert.Equal(t, 2, len(res.Cursors))
|
||||
assertGotItems(items, 3, 4)
|
||||
|
||||
// and reverse
|
||||
items = nil
|
||||
res, err = ormtable.Paginate(table, ctx, &ormtable.PaginationRequest{
|
||||
PageRequest: &queryv1beta1.PageRequest{
|
||||
Limit: 3,
|
||||
CountTotal: true,
|
||||
Offset: 5,
|
||||
Reverse: true,
|
||||
}}, onItem)
|
||||
it, err = store.List(ctx, testpb.ExampleTablePrimaryKey{}, ormlist.Paginate(&queryv1beta1.PageRequest{
|
||||
Limit: 3,
|
||||
CountTotal: true,
|
||||
Offset: 5,
|
||||
Reverse: true,
|
||||
}))
|
||||
assert.NilError(t, err)
|
||||
assertIteratorItems(it, 4, 3, 2)
|
||||
res = it.PageResponse()
|
||||
assert.Assert(t, res != nil)
|
||||
assert.Assert(t, res.NextKey != nil)
|
||||
assert.Equal(t, uint64(10), res.Total)
|
||||
assert.Assert(t, res.HaveMore)
|
||||
assert.Equal(t, 3, len(res.Cursors))
|
||||
assertGotItems(items, 4, 3, 2)
|
||||
|
||||
// now an offset that's slightly too big
|
||||
items = nil
|
||||
res, err = ormtable.Paginate(table, ctx, &ormtable.PaginationRequest{
|
||||
PageRequest: &queryv1beta1.PageRequest{
|
||||
Limit: 1,
|
||||
CountTotal: true,
|
||||
Offset: 10,
|
||||
}}, onItem)
|
||||
it, err = store.List(ctx, testpb.ExampleTablePrimaryKey{}, ormlist.Paginate(&queryv1beta1.PageRequest{
|
||||
Limit: 1,
|
||||
CountTotal: true,
|
||||
Offset: 10,
|
||||
}))
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, 0, len(items))
|
||||
assert.Assert(t, !res.HaveMore)
|
||||
assert.Equal(t, uint64(10), res.Total)
|
||||
|
||||
// another offset that's too big
|
||||
items = nil
|
||||
res, err = ormtable.Paginate(table, ctx, &ormtable.PaginationRequest{
|
||||
PageRequest: &queryv1beta1.PageRequest{
|
||||
Limit: 1,
|
||||
CountTotal: true,
|
||||
Offset: 14,
|
||||
}}, onItem)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, 0, len(items))
|
||||
assert.Assert(t, !res.HaveMore)
|
||||
assert.Assert(t, !it.Next())
|
||||
res = it.PageResponse()
|
||||
assert.Assert(t, res != nil)
|
||||
assert.Assert(t, res.NextKey == nil)
|
||||
assert.Equal(t, uint64(10), res.Total)
|
||||
|
||||
// now let's update some things
|
||||
@@ -462,11 +424,11 @@ func testIndex(t *testing.T, model *IndexModel) {
|
||||
if index.IsFullyOrdered() {
|
||||
t.Logf("testing index %T %s", index, index.Fields())
|
||||
|
||||
it, err := model.index.Iterator(model.context)
|
||||
it, err := model.index.List(model.context, nil)
|
||||
assert.NilError(t, err)
|
||||
checkIteratorAgainstSlice(t, it, model.data)
|
||||
|
||||
it, err = model.index.Iterator(model.context, ormlist.Reverse())
|
||||
it, err = model.index.List(model.context, nil, ormlist.Reverse())
|
||||
assert.NilError(t, err)
|
||||
checkIteratorAgainstSlice(t, it, reverseData(model.data))
|
||||
|
||||
@@ -482,11 +444,11 @@ func testIndex(t *testing.T, model *IndexModel) {
|
||||
startVals := protoValuesToInterfaces(start)
|
||||
endVals := protoValuesToInterfaces(end)
|
||||
|
||||
it, err = model.index.Iterator(model.context, ormlist.Start(startVals...), ormlist.End(endVals...))
|
||||
it, err = model.index.ListRange(model.context, startVals, endVals)
|
||||
assert.NilError(t, err)
|
||||
checkIteratorAgainstSlice(t, it, model.data[i:j+1])
|
||||
|
||||
it, err = model.index.Iterator(model.context, ormlist.Start(startVals...), ormlist.End(endVals...), ormlist.Reverse())
|
||||
it, err = model.index.ListRange(model.context, startVals, endVals, ormlist.Reverse())
|
||||
assert.NilError(t, err)
|
||||
checkIteratorAgainstSlice(t, it, reverseData(model.data[i:j+1]))
|
||||
})
|
||||
@@ -494,7 +456,7 @@ func testIndex(t *testing.T, model *IndexModel) {
|
||||
t.Logf("testing unordered index %T %s", index, index.Fields())
|
||||
|
||||
// get all the data
|
||||
it, err := model.index.Iterator(model.context)
|
||||
it, err := model.index.List(model.context, nil)
|
||||
assert.NilError(t, err)
|
||||
var data2 []proto.Message
|
||||
for it.Next() {
|
||||
@@ -664,9 +626,9 @@ func TestJSONExportImport(t *testing.T) {
|
||||
}
|
||||
|
||||
func assertTablesEqual(t assert.TestingT, table ormtable.Table, ctx, ctx2 context.Context) {
|
||||
it, err := table.Iterator(ctx)
|
||||
it, err := table.List(ctx, nil)
|
||||
assert.NilError(t, err)
|
||||
it2, err := table.Iterator(ctx2)
|
||||
it2, err := table.List(ctx2, nil)
|
||||
assert.NilError(t, err)
|
||||
|
||||
for {
|
||||
|
||||
+82
-66
@@ -292,6 +292,88 @@ GET 0100000000047ffffffffffffffe616263 1007
|
||||
PK testpb.ExampleTable 4/-2/abc -> {"u32":4,"u64":7,"str":"abc","i64":-2}
|
||||
NEXT
|
||||
VALID false
|
||||
ITERATOR 0100 -> 0101
|
||||
VALID true
|
||||
KEY 0100000000047ffffffffffffffe616263 1007
|
||||
PK testpb.ExampleTable 4/-2/abc -> {"u32":4,"u64":7,"str":"abc","i64":-2}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000047ffffffffffffffe616264 1007
|
||||
PK testpb.ExampleTable 4/-2/abd -> {"u32":4,"u64":7,"str":"abd","i64":-2}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000047fffffffffffffff616263 1008
|
||||
PK testpb.ExampleTable 4/-1/abc -> {"u32":4,"u64":8,"str":"abc","i64":-1}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000057ffffffffffffffe616264 1008
|
||||
PK testpb.ExampleTable 5/-2/abd -> {"u32":5,"u64":8,"str":"abd","i64":-2}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000057ffffffffffffffe616265 1009
|
||||
PK testpb.ExampleTable 5/-2/abe -> {"u32":5,"u64":9,"str":"abe","i64":-2}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000077ffffffffffffffe616265 100a
|
||||
PK testpb.ExampleTable 7/-2/abe -> {"u32":7,"u64":10,"str":"abe","i64":-2}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000077fffffffffffffff616265 100b
|
||||
PK testpb.ExampleTable 7/-1/abe -> {"u32":7,"u64":11,"str":"abe","i64":-1}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000087ffffffffffffffc616263 100b
|
||||
PK testpb.ExampleTable 8/-4/abc -> {"u32":8,"u64":11,"str":"abc","i64":-4}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000088000000000000001616263 100c
|
||||
PK testpb.ExampleTable 8/1/abc -> {"u32":8,"u64":12,"str":"abc","i64":1}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000088000000000000001616264 100a
|
||||
PK testpb.ExampleTable 8/1/abd -> {"u32":8,"u64":10,"str":"abd","i64":1}
|
||||
NEXT
|
||||
VALID false
|
||||
ITERATOR 0100 -> 0101
|
||||
VALID true
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000047ffffffffffffffe616264 1007
|
||||
PK testpb.ExampleTable 4/-2/abd -> {"u32":4,"u64":7,"str":"abd","i64":-2}
|
||||
ITERATOR 0100000000047ffffffffffffffe61626400 -> 0101
|
||||
VALID true
|
||||
KEY 0100000000047fffffffffffffff616263 1008
|
||||
PK testpb.ExampleTable 4/-1/abc -> {"u32":4,"u64":8,"str":"abc","i64":-1}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000057ffffffffffffffe616264 1008
|
||||
PK testpb.ExampleTable 5/-2/abd -> {"u32":5,"u64":8,"str":"abd","i64":-2}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000057ffffffffffffffe616265 1009
|
||||
PK testpb.ExampleTable 5/-2/abe -> {"u32":5,"u64":9,"str":"abe","i64":-2}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000077ffffffffffffffe616265 100a
|
||||
PK testpb.ExampleTable 7/-2/abe -> {"u32":7,"u64":10,"str":"abe","i64":-2}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000077fffffffffffffff616265 100b
|
||||
PK testpb.ExampleTable 7/-1/abe -> {"u32":7,"u64":11,"str":"abe","i64":-1}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000087ffffffffffffffc616263 100b
|
||||
PK testpb.ExampleTable 8/-4/abc -> {"u32":8,"u64":11,"str":"abc","i64":-4}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000088000000000000001616263 100c
|
||||
PK testpb.ExampleTable 8/1/abc -> {"u32":8,"u64":12,"str":"abc","i64":1}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000088000000000000001616264 100a
|
||||
PK testpb.ExampleTable 8/1/abd -> {"u32":8,"u64":10,"str":"abd","i64":1}
|
||||
NEXT
|
||||
VALID false
|
||||
HAS 0101000000000000000c616263
|
||||
ERR:EOF
|
||||
GET 0101000000000000000c616263 000000088000000000000001
|
||||
@@ -300,20 +382,14 @@ GET 0100000000088000000000000001616263 100c
|
||||
PK testpb.ExampleTable 8/1/abc -> {"u32":8,"u64":12,"str":"abc","i64":1}
|
||||
ITERATOR 0100 -> 0101
|
||||
VALID true
|
||||
KEY 0100000000047ffffffffffffffe616263 1007
|
||||
PK testpb.ExampleTable 4/-2/abc -> {"u32":4,"u64":7,"str":"abc","i64":-2}
|
||||
KEY 0100000000047ffffffffffffffe616263 1007
|
||||
PK testpb.ExampleTable 4/-2/abc -> {"u32":4,"u64":7,"str":"abc","i64":-2}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000047ffffffffffffffe616264 1007
|
||||
PK testpb.ExampleTable 4/-2/abd -> {"u32":4,"u64":7,"str":"abd","i64":-2}
|
||||
KEY 0100000000047ffffffffffffffe616264 1007
|
||||
PK testpb.ExampleTable 4/-2/abd -> {"u32":4,"u64":7,"str":"abd","i64":-2}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000047fffffffffffffff616263 1008
|
||||
PK testpb.ExampleTable 4/-1/abc -> {"u32":4,"u64":8,"str":"abc","i64":-1}
|
||||
KEY 0100000000047fffffffffffffff616263 1008
|
||||
PK testpb.ExampleTable 4/-1/abc -> {"u32":4,"u64":8,"str":"abc","i64":-1}
|
||||
NEXT
|
||||
@@ -336,23 +412,16 @@ ITERATOR 0100 -> 0101
|
||||
VALID true
|
||||
NEXT
|
||||
VALID false
|
||||
CLOSE
|
||||
ITERATOR 0100000000057ffffffffffffffe61626400 -> 0101
|
||||
VALID true
|
||||
KEY 0100000000057ffffffffffffffe616265 1009
|
||||
PK testpb.ExampleTable 5/-2/abe -> {"u32":5,"u64":9,"str":"abe","i64":-2}
|
||||
KEY 0100000000057ffffffffffffffe616265 1009
|
||||
PK testpb.ExampleTable 5/-2/abe -> {"u32":5,"u64":9,"str":"abe","i64":-2}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000077ffffffffffffffe616265 100a
|
||||
PK testpb.ExampleTable 7/-2/abe -> {"u32":7,"u64":10,"str":"abe","i64":-2}
|
||||
KEY 0100000000077ffffffffffffffe616265 100a
|
||||
PK testpb.ExampleTable 7/-2/abe -> {"u32":7,"u64":10,"str":"abe","i64":-2}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000077fffffffffffffff616265 100b
|
||||
PK testpb.ExampleTable 7/-1/abe -> {"u32":7,"u64":11,"str":"abe","i64":-1}
|
||||
KEY 0100000000077fffffffffffffff616265 100b
|
||||
PK testpb.ExampleTable 7/-1/abe -> {"u32":7,"u64":11,"str":"abe","i64":-1}
|
||||
NEXT
|
||||
@@ -363,26 +432,18 @@ ITERATOR 0100000000057ffffffffffffffe61626400 -> 0101
|
||||
PK testpb.ExampleTable 8/-4/abc -> {"u32":8,"u64":11,"str":"abc","i64":-4}
|
||||
NEXT
|
||||
VALID true
|
||||
CLOSE
|
||||
ITERATOR 0100000000087ffffffffffffffc61626300 -> 0101
|
||||
VALID true
|
||||
KEY 0100000000088000000000000001616263 100c
|
||||
PK testpb.ExampleTable 8/1/abc -> {"u32":8,"u64":12,"str":"abc","i64":1}
|
||||
KEY 0100000000088000000000000001616263 100c
|
||||
PK testpb.ExampleTable 8/1/abc -> {"u32":8,"u64":12,"str":"abc","i64":1}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000088000000000000001616264 100a
|
||||
PK testpb.ExampleTable 8/1/abd -> {"u32":8,"u64":10,"str":"abd","i64":1}
|
||||
KEY 0100000000088000000000000001616264 100a
|
||||
PK testpb.ExampleTable 8/1/abd -> {"u32":8,"u64":10,"str":"abd","i64":1}
|
||||
NEXT
|
||||
VALID false
|
||||
CLOSE
|
||||
ITERATOR 0100 <- 0101
|
||||
VALID true
|
||||
KEY 0100000000088000000000000001616264 100a
|
||||
PK testpb.ExampleTable 8/1/abd -> {"u32":8,"u64":10,"str":"abd","i64":1}
|
||||
KEY 0100000000088000000000000001616264 100a
|
||||
PK testpb.ExampleTable 8/1/abd -> {"u32":8,"u64":10,"str":"abd","i64":1}
|
||||
NEXT
|
||||
@@ -409,11 +470,8 @@ ITERATOR 0100 <- 0101
|
||||
VALID true
|
||||
NEXT
|
||||
VALID false
|
||||
CLOSE
|
||||
ITERATOR 0100 <- 0100000000088000000000000001616263
|
||||
VALID true
|
||||
KEY 0100000000087ffffffffffffffc616263 100b
|
||||
PK testpb.ExampleTable 8/-4/abc -> {"u32":8,"u64":11,"str":"abc","i64":-4}
|
||||
KEY 0100000000087ffffffffffffffc616263 100b
|
||||
PK testpb.ExampleTable 8/-4/abc -> {"u32":8,"u64":11,"str":"abc","i64":-4}
|
||||
NEXT
|
||||
@@ -424,34 +482,24 @@ ITERATOR 0100 <- 0100000000088000000000000001616263
|
||||
PK testpb.ExampleTable 7/-1/abe -> {"u32":7,"u64":11,"str":"abe","i64":-1}
|
||||
NEXT
|
||||
VALID true
|
||||
CLOSE
|
||||
ITERATOR 0100000000047fffffffffffffff616263 -> 0100000000077ffffffffffffffe61626500
|
||||
VALID true
|
||||
KEY 0100000000047fffffffffffffff616263 1008
|
||||
PK testpb.ExampleTable 4/-1/abc -> {"u32":4,"u64":8,"str":"abc","i64":-1}
|
||||
KEY 0100000000047fffffffffffffff616263 1008
|
||||
PK testpb.ExampleTable 4/-1/abc -> {"u32":4,"u64":8,"str":"abc","i64":-1}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000057ffffffffffffffe616264 1008
|
||||
PK testpb.ExampleTable 5/-2/abd -> {"u32":5,"u64":8,"str":"abd","i64":-2}
|
||||
KEY 0100000000057ffffffffffffffe616264 1008
|
||||
PK testpb.ExampleTable 5/-2/abd -> {"u32":5,"u64":8,"str":"abd","i64":-2}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000057ffffffffffffffe616265 1009
|
||||
PK testpb.ExampleTable 5/-2/abe -> {"u32":5,"u64":9,"str":"abe","i64":-2}
|
||||
KEY 0100000000057ffffffffffffffe616265 1009
|
||||
PK testpb.ExampleTable 5/-2/abe -> {"u32":5,"u64":9,"str":"abe","i64":-2}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000077ffffffffffffffe616265 100a
|
||||
PK testpb.ExampleTable 7/-2/abe -> {"u32":7,"u64":10,"str":"abe","i64":-2}
|
||||
KEY 0100000000077ffffffffffffffe616265 100a
|
||||
PK testpb.ExampleTable 7/-2/abe -> {"u32":7,"u64":10,"str":"abe","i64":-2}
|
||||
NEXT
|
||||
VALID false
|
||||
CLOSE
|
||||
ITERATOR 0100 -> 0101
|
||||
VALID true
|
||||
NEXT
|
||||
@@ -460,8 +508,6 @@ ITERATOR 0100 -> 0101
|
||||
VALID true
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000057ffffffffffffffe616264 1008
|
||||
PK testpb.ExampleTable 5/-2/abd -> {"u32":5,"u64":8,"str":"abd","i64":-2}
|
||||
KEY 0100000000057ffffffffffffffe616264 1008
|
||||
PK testpb.ExampleTable 5/-2/abd -> {"u32":5,"u64":8,"str":"abd","i64":-2}
|
||||
NEXT
|
||||
@@ -482,7 +528,6 @@ ITERATOR 0100 -> 0101
|
||||
VALID true
|
||||
NEXT
|
||||
VALID false
|
||||
CLOSE
|
||||
ITERATOR 0100 <- 0101
|
||||
VALID true
|
||||
NEXT
|
||||
@@ -495,14 +540,10 @@ ITERATOR 0100 <- 0101
|
||||
VALID true
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000057ffffffffffffffe616265 1009
|
||||
PK testpb.ExampleTable 5/-2/abe -> {"u32":5,"u64":9,"str":"abe","i64":-2}
|
||||
KEY 0100000000057ffffffffffffffe616265 1009
|
||||
PK testpb.ExampleTable 5/-2/abe -> {"u32":5,"u64":9,"str":"abe","i64":-2}
|
||||
NEXT
|
||||
VALID true
|
||||
KEY 0100000000057ffffffffffffffe616264 1008
|
||||
PK testpb.ExampleTable 5/-2/abd -> {"u32":5,"u64":8,"str":"abd","i64":-2}
|
||||
KEY 0100000000057ffffffffffffffe616264 1008
|
||||
PK testpb.ExampleTable 5/-2/abd -> {"u32":5,"u64":8,"str":"abd","i64":-2}
|
||||
NEXT
|
||||
@@ -517,7 +558,6 @@ ITERATOR 0100 <- 0101
|
||||
VALID true
|
||||
NEXT
|
||||
VALID false
|
||||
CLOSE
|
||||
ITERATOR 0100 -> 0101
|
||||
VALID true
|
||||
NEXT
|
||||
@@ -540,30 +580,6 @@ ITERATOR 0100 -> 0101
|
||||
VALID true
|
||||
NEXT
|
||||
VALID false
|
||||
CLOSE
|
||||
ITERATOR 0100 -> 0101
|
||||
VALID true
|
||||
NEXT
|
||||
VALID true
|
||||
NEXT
|
||||
VALID true
|
||||
NEXT
|
||||
VALID true
|
||||
NEXT
|
||||
VALID true
|
||||
NEXT
|
||||
VALID true
|
||||
NEXT
|
||||
VALID true
|
||||
NEXT
|
||||
VALID true
|
||||
NEXT
|
||||
VALID true
|
||||
NEXT
|
||||
VALID true
|
||||
NEXT
|
||||
VALID false
|
||||
CLOSE
|
||||
GET 0100000000047ffffffffffffffe616263 1007
|
||||
PK testpb.ExampleTable 4/-2/abc -> {"u32":4,"u64":7,"str":"abc","i64":-2}
|
||||
ORM UPDATE testpb.ExampleTable {"u32":4,"u64":7,"str":"abc","i64":-2} -> {"u32":4,"u64":14,"str":"abc","bz":"abc","i64":-2}
|
||||
|
||||
@@ -24,13 +24,22 @@ type uniqueKeyIndex struct {
|
||||
getReadBackend func(context.Context) (ReadBackend, error)
|
||||
}
|
||||
|
||||
func (u uniqueKeyIndex) Iterator(ctx context.Context, options ...ormlist.Option) (Iterator, error) {
|
||||
func (u uniqueKeyIndex) List(ctx context.Context, prefixKey []interface{}, options ...ormlist.Option) (Iterator, error) {
|
||||
backend, err := u.getReadBackend(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return iterator(backend, backend.IndexStoreReader(), u, u.GetKeyCodec(), options)
|
||||
return prefixIterator(backend.IndexStoreReader(), backend, u, u.GetKeyCodec(), prefixKey, options)
|
||||
}
|
||||
|
||||
func (u uniqueKeyIndex) ListRange(ctx context.Context, from, to []interface{}, options ...ormlist.Option) (Iterator, error) {
|
||||
backend, err := u.getReadBackend(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rangeIterator(backend.IndexStoreReader(), backend, u, u.GetKeyCodec(), from, to, options)
|
||||
}
|
||||
|
||||
func (u uniqueKeyIndex) doNotImplement() {}
|
||||
@@ -187,8 +196,8 @@ func (u uniqueKeyIndex) readValueFromIndexKey(store ReadBackend, primaryKey []pr
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p uniqueKeyIndex) Fields() string {
|
||||
return p.fields.String()
|
||||
func (u uniqueKeyIndex) Fields() string {
|
||||
return u.fields.String()
|
||||
}
|
||||
|
||||
var _ indexer = &uniqueKeyIndex{}
|
||||
|
||||
@@ -46,7 +46,8 @@ message PageRequest {
|
||||
// }
|
||||
message PageResponse {
|
||||
// next_key is the key to be passed to PageRequest.key to
|
||||
// query the next page most efficiently
|
||||
// query the next page most efficiently. It will be empty if
|
||||
// there are no more results.
|
||||
bytes next_key = 1;
|
||||
|
||||
// total is total number of results available if PageRequest.count_total
|
||||
|
||||
@@ -129,7 +129,8 @@ func (m *PageRequest) GetReverse() bool {
|
||||
// }
|
||||
type PageResponse struct {
|
||||
// next_key is the key to be passed to PageRequest.key to
|
||||
// query the next page most efficiently
|
||||
// query the next page most efficiently. It will be empty if
|
||||
// there are no more results.
|
||||
NextKey []byte `protobuf:"bytes,1,opt,name=next_key,json=nextKey,proto3" json:"next_key,omitempty"`
|
||||
// total is total number of results available if PageRequest.count_total
|
||||
// was set, its value is undefined otherwise
|
||||
|
||||
Reference in New Issue
Block a user