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:
Aaron Craelius
2022-02-02 16:12:11 -05:00
committed by GitHub
co-authored by Marko Tyler
parent 894969bace
commit 5126ec3c79
20 changed files with 482 additions and 469 deletions
@@ -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)
+1 -2
View File
@@ -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")
+2 -4
View File
@@ -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()
+9 -8
View File
@@ -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
}
+4 -8
View File
@@ -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
}
+30 -49
View File
@@ -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
+28
View File
@@ -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
}
+21 -2
View File
@@ -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
+11 -2
View File
@@ -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{}
+87 -64
View File
@@ -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
View File
@@ -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
}
+19 -10
View File
@@ -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
+1 -1
View File
@@ -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()
+90 -128
View File
@@ -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
View File
@@ -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}
+13 -4
View File
@@ -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
+2 -1
View File
@@ -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