mirror of
https://github.com/tiennm99/tiennm99bot.git
synced 2026-10-11 03:13:46 +00:00
fix(storage): return Mongo List keys in key order like the memory store
This commit is contained in:
1 parent
00eccd5f5b
commit
897e37ba80
3 files changed
+31
-4
No files matched your search
@@ -40,8 +40,9 @@ var ErrInvalidModuleName = errors.New("storage: invalid module name")
|
|||||||
// - PutVersioned writes only if the stored version equals expectedVersion,
|
// - PutVersioned writes only if the stored version equals expectedVersion,
|
||||||
// then bumps it. expectedVersion == 0 means "create (or adopt a not-yet-
|
// then bumps it. expectedVersion == 0 means "create (or adopt a not-yet-
|
||||||
// versioned key)". A mismatch returns ErrConflict.
|
// versioned key)". A mismatch returns ErrConflict.
|
||||||
// - List returns the keys under a prefix; Scan returns those keys with their
|
// - List returns the keys under a prefix, ordered by key ascending.
|
||||||
// values, ordered by key ascending.
|
// - Scan returns the keys under a prefix with their values, ordered by key
|
||||||
|
// ascending.
|
||||||
type DocStore[T any] interface {
|
type DocStore[T any] interface {
|
||||||
Get(ctx context.Context, id string) (val T, version int64, err error)
|
Get(ctx context.Context, id string) (val T, version int64, err error)
|
||||||
Put(ctx context.Context, id string, val T) error
|
Put(ctx context.Context, id string, val T) error
|
||||||
|
|||||||
@@ -153,7 +153,8 @@ func (s *mongoDocStore[T]) Delete(ctx context.Context, id string) error {
|
|||||||
|
|
||||||
// List returns all document IDs whose key starts with prefix, via a half-open
|
// List returns all document IDs whose key starts with prefix, via a half-open
|
||||||
// range scan on _id (reusing prefixSuccessor) so it uses the _id index and
|
// range scan on _id (reusing prefixSuccessor) so it uses the _id index and
|
||||||
// avoids regex injection. Empty prefix returns the whole collection.
|
// avoids regex injection. Keys come back sorted by _id, matching the memory
|
||||||
|
// store. Empty prefix returns the whole collection.
|
||||||
func (s *mongoDocStore[T]) List(ctx context.Context, prefix string) ([]string, error) {
|
func (s *mongoDocStore[T]) List(ctx context.Context, prefix string) ([]string, error) {
|
||||||
if err := validatePrefix(prefix); err != nil {
|
if err := validatePrefix(prefix); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -162,7 +163,9 @@ func (s *mongoDocStore[T]) List(ctx context.Context, prefix string) ([]string, e
|
|||||||
if prefix != "" {
|
if prefix != "" {
|
||||||
filter[mongoIDField] = bson.M{"$gte": prefix, "$lt": prefixSuccessor(prefix)}
|
filter[mongoIDField] = bson.M{"$gte": prefix, "$lt": prefixSuccessor(prefix)}
|
||||||
}
|
}
|
||||||
cur, err := s.coll.Find(ctx, filter, options.Find().SetProjection(bson.M{mongoIDField: 1}))
|
cur, err := s.coll.Find(ctx, filter, options.Find().
|
||||||
|
SetProjection(bson.M{mongoIDField: 1}).
|
||||||
|
SetSort(bson.D{{Key: mongoIDField, Value: 1}}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("mongo list %s prefix=%q: %w", s.module, prefix, err)
|
return nil, fmt.Errorf("mongo list %s prefix=%q: %w", s.module, prefix, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -235,3 +236,25 @@ func TestMongoDocStore_ScanReturnsPayloadsInKeyOrder(t *testing.T) {
|
|||||||
t.Fatalf("Scan all = %d docs, err %v", len(all), err)
|
t.Fatalf("Scan all = %d docs, err %v", len(all), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List returns keys in key order, like the memory store, so callers behave the
|
||||||
|
// same on both backends. Insertion order is deliberately reversed here.
|
||||||
|
func TestMongoDocStore_ListReturnsKeysInKeyOrder(t *testing.T) {
|
||||||
|
store, _, cleanup := mongoStore[portfolioLike](t, "coin")
|
||||||
|
defer cleanup()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
for _, key := range []string{"user:3", "user:1", "other", "user:2"} {
|
||||||
|
if err := store.Put(ctx, key, portfolioLike{}); err != nil {
|
||||||
|
t.Fatalf("Put %s: %v", key, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keys, err := store.List(ctx, "user:")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("List: %v", err)
|
||||||
|
}
|
||||||
|
if want := []string{"user:1", "user:2", "user:3"}; !slices.Equal(keys, want) {
|
||||||
|
t.Errorf("List = %v, want %v", keys, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user