Add ScanType command to Scan with 'type' option

As of version 6.0 you can use this 'type' option to ask SCAN to only
return objects that match a given type, allowing you to
iterate through the database looking for keys of a specific type.
This commit is contained in:
Leandro Forain 2021-01-08 16:36:20 -03:00
parent b3e0aa270a
commit c2351b491a
3 changed files with 57 additions and 0 deletions

View File

@ -141,6 +141,7 @@ type Cmdable interface {
BitField(ctx context.Context, key string, args ...interface{}) *IntSliceCmd BitField(ctx context.Context, key string, args ...interface{}) *IntSliceCmd
Scan(ctx context.Context, cursor uint64, match string, count int64) *ScanCmd Scan(ctx context.Context, cursor uint64, match string, count int64) *ScanCmd
ScanType(ctx context.Context, cursor uint64, match string, count int64, keyType string) *ScanCmd
SScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd SScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd
HScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd HScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd
ZScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd ZScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd
@ -965,6 +966,22 @@ func (c cmdable) Scan(ctx context.Context, cursor uint64, match string, count in
return cmd return cmd
} }
func (c cmdable) ScanType(ctx context.Context, cursor uint64, match string, count int64, keyType string) *ScanCmd {
args := []interface{}{"scan", cursor}
if match != "" {
args = append(args, "match", match)
}
if count > 0 {
args = append(args, "count", count)
}
if keyType != "" {
args = append(args, "type", keyType)
}
cmd := NewScanCmd(ctx, c, args...)
_ = c(ctx, cmd)
return cmd
}
func (c cmdable) SScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd { func (c cmdable) SScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd {
args := []interface{}{"sscan", key, cursor} args := []interface{}{"sscan", key, cursor}
if match != "" { if match != "" {

View File

@ -770,6 +770,18 @@ var _ = Describe("Commands", func() {
Expect(cursor).NotTo(BeZero()) Expect(cursor).NotTo(BeZero())
}) })
It("should ScanType", func() {
for i := 0; i < 1000; i++ {
set := client.Set(ctx, fmt.Sprintf("key%d", i), "hello", 0)
Expect(set.Err()).NotTo(HaveOccurred())
}
keys, cursor, err := client.ScanType(ctx, 0, "", 0, "string").Result()
Expect(err).NotTo(HaveOccurred())
Expect(keys).NotTo(BeEmpty())
Expect(cursor).NotTo(BeZero())
})
It("should SScan", func() { It("should SScan", func() {
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
sadd := client.SAdd(ctx, "myset", fmt.Sprintf("member%d", i)) sadd := client.SAdd(ctx, "myset", fmt.Sprintf("member%d", i))

View File

@ -248,6 +248,34 @@ func ExampleClient_Scan() {
// Output: found 33 keys // Output: found 33 keys
} }
func ExampleClient_ScanType() {
rdb.FlushDB(ctx)
for i := 0; i < 33; i++ {
err := rdb.Set(ctx, fmt.Sprintf("key%d", i), "value", 0).Err()
if err != nil {
panic(err)
}
}
var cursor uint64
var n int
for {
var keys []string
var err error
keys, cursor, err = rdb.ScanType(ctx, cursor, "key*", 10, "string").Result()
if err != nil {
panic(err)
}
n += len(keys)
if cursor == 0 {
break
}
}
fmt.Printf("found %d keys\n", n)
// Output: found 33 keys
}
func ExampleClient_Pipelined() { func ExampleClient_Pipelined() {
var incr *redis.IntCmd var incr *redis.IntCmd
_, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error { _, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {