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,
|
||||
// then bumps it. expectedVersion == 0 means "create (or adopt a not-yet-
|
||||
// versioned key)". A mismatch returns ErrConflict.
|
||||
// - List returns the keys under a prefix; Scan returns those keys with their
|
||||
// values, ordered by key ascending.
|
||||
// - List returns the keys under a prefix, ordered by key ascending.
|
||||
// - Scan returns the keys under a prefix with their values, ordered by key
|
||||
// ascending.
|
||||
type DocStore[T any] interface {
|
||||
Get(ctx context.Context, id string) (val T, version int64, err 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
|
||||
// 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) {
|
||||
if err := validatePrefix(prefix); err != nil {
|
||||
return nil, err
|
||||
@@ -162,7 +163,9 @@ func (s *mongoDocStore[T]) List(ctx context.Context, prefix string) ([]string, e
|
||||
if 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 {
|
||||
return nil, fmt.Errorf("mongo list %s prefix=%q: %w", s.module, prefix, err)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -235,3 +236,25 @@ func TestMongoDocStore_ScanReturnsPayloadsInKeyOrder(t *testing.T) {
|
||||
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