Skip to content

Commit 814ac42

Browse files
committed
Add support for iter.Seq for ScanIterator
1 parent a4aea25 commit 814ac42

File tree

17 files changed

+124
-27
lines changed

17 files changed

+124
-27
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ In `go-redis` we are aiming to support the last three releases of Redis. Current
1515
- [Redis 7.4](https://raw.githubusercontent.com/redis/redis/7.4/00-RELEASENOTES) - using Redis Stack 7.4 for modules support
1616
- [Redis 8.0](https://raw.githubusercontent.com/redis/redis/8.0/00-RELEASENOTES) - using Redis CE 8.0 where modules are included
1717

18-
Although the `go.mod` states it requires at minimum `go 1.18`, our CI is configured to run the tests against all three
18+
Although the `go.mod` states it requires at minimum `go 1.23`, our CI is configured to run the tests against all three
1919
versions of Redis and latest two versions of Go ([1.23](https://go.dev/doc/devel/release#go1.23.0),
2020
[1.24](https://go.dev/doc/devel/release#go1.24.0)). We observe that some modules related test may not pass with
2121
Redis Stack 7.2 and some commands are changed with Redis CE 8.0.

example/del-keys-without-ttl/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/redis/go-redis/example/del-keys-without-ttl
22

3-
go 1.18
3+
go 1.23
44

55
replace github.com/redis/go-redis/v9 => ../..
66

example/del-keys-without-ttl/main.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ func main() {
2828
checker.Start(ctx)
2929

3030
iter := rdb.Scan(ctx, 0, "", 0).Iterator()
31-
for iter.Next(ctx) {
32-
checker.Add(iter.Val())
31+
for val := range iter.Vals(ctx) {
32+
checker.Add(val)
3333
}
3434
if err := iter.Err(); err != nil {
3535
panic(err)

example/hll/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/redis/go-redis/example/hll
22

3-
go 1.18
3+
go 1.23
44

55
replace github.com/redis/go-redis/v9 => ../..
66

example/hset-struct/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/redis/go-redis/example/scan-struct
22

3-
go 1.18
3+
go 1.23
44

55
replace github.com/redis/go-redis/v9 => ../..
66

example/lua-scripting/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/redis/go-redis/example/lua-scripting
22

3-
go 1.18
3+
go 1.23
44

55
replace github.com/redis/go-redis/v9 => ../..
66

example/redis-bloom/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/redis/go-redis/example/redis-bloom
22

3-
go 1.18
3+
go 1.23
44

55
replace github.com/redis/go-redis/v9 => ../..
66

example/scan-struct/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/redis/go-redis/example/scan-struct
22

3-
go 1.18
3+
go 1.23
44

55
replace github.com/redis/go-redis/v9 => ../..
66

example_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -632,8 +632,8 @@ func Example_customCommand2() {
632632

633633
func ExampleScanIterator() {
634634
iter := rdb.Scan(ctx, 0, "", 0).Iterator()
635-
for iter.Next(ctx) {
636-
fmt.Println(iter.Val())
635+
for val := range iter.Vals(ctx) {
636+
fmt.Println(val)
637637
}
638638
if err := iter.Err(); err != nil {
639639
panic(err)
@@ -642,8 +642,8 @@ func ExampleScanIterator() {
642642

643643
func ExampleScanCmd_Iterator() {
644644
iter := rdb.Scan(ctx, 0, "", 0).Iterator()
645-
for iter.Next(ctx) {
646-
fmt.Println(iter.Val())
645+
for val := range iter.Vals(ctx) {
646+
fmt.Println(val)
647647
}
648648
if err := iter.Err(); err != nil {
649649
panic(err)

extra/rediscensus/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/redis/go-redis/extra/rediscensus/v9
22

3-
go 1.19
3+
go 1.23
44

55
replace github.com/redis/go-redis/v9 => ../..
66

extra/rediscmd/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/redis/go-redis/extra/rediscmd/v9
22

3-
go 1.19
3+
go 1.23
44

55
replace github.com/redis/go-redis/v9 => ../..
66

extra/redisotel/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/redis/go-redis/extra/redisotel/v9
22

3-
go 1.19
3+
go 1.23
44

55
replace github.com/redis/go-redis/v9 => ../..
66

extra/redisprometheus/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/redis/go-redis/extra/redisprometheus/v9
22

3-
go 1.19
3+
go 1.23
44

55
replace github.com/redis/go-redis/v9 => ../..
66

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/redis/go-redis/v9
22

3-
go 1.18
3+
go 1.23
44

55
require (
66
github.com/bsm/ginkgo/v2 v2.12.0

internal/customvet/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/redis/go-redis/internal/customvet
22

3-
go 1.17
3+
go 1.23
44

55
require golang.org/x/tools v0.5.0
66

iterator.go

+16
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package redis
22

33
import (
44
"context"
5+
"iter"
56
)
67

78
// ScanIterator is used to incrementally iterate over a collection of elements.
@@ -16,6 +17,8 @@ func (it *ScanIterator) Err() error {
1617
}
1718

1819
// Next advances the cursor and returns true if more values can be read.
20+
//
21+
// Deprecated: support for native iterators has been added in go 1.23. Use Vals instead.
1922
func (it *ScanIterator) Next(ctx context.Context) bool {
2023
// Instantly return on errors.
2124
if it.cmd.Err() != nil {
@@ -57,10 +60,23 @@ func (it *ScanIterator) Next(ctx context.Context) bool {
5760
}
5861

5962
// Val returns the key/field at the current cursor position.
63+
//
64+
// Deprecated: support for native iterators has been added in go 1.23. Use Vals instead.
6065
func (it *ScanIterator) Val() string {
6166
var v string
6267
if it.cmd.Err() == nil && it.pos > 0 && it.pos <= len(it.cmd.page) {
6368
v = it.cmd.page[it.pos-1]
6469
}
6570
return v
6671
}
72+
73+
// Vals returns iterator over key/field at the current cursor position.
74+
func (it *ScanIterator) Vals(ctx context.Context) iter.Seq[string] {
75+
return func(yield func(string) bool) {
76+
for it.Next(ctx) {
77+
if !yield(it.Val()) {
78+
return
79+
}
80+
}
81+
}
82+
}

iterator_test.go

+89-8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package redis_test
22

33
import (
44
"fmt"
5+
"slices"
56

67
. "github.com/bsm/ginkgo/v2"
78
. "github.com/bsm/gomega"
@@ -52,13 +53,20 @@ var _ = Describe("ScanIterator", func() {
5253
Expect(client.Close()).NotTo(HaveOccurred())
5354
})
5455

55-
It("should scan across empty DBs", func() {
56+
It("should scan across empty DBs using Next", func() {
5657
iter := client.Scan(ctx, 0, "", 10).Iterator()
5758
Expect(iter.Next(ctx)).To(BeFalse())
5859
Expect(iter.Err()).NotTo(HaveOccurred())
5960
})
6061

61-
It("should scan across one page", func() {
62+
It("should scan across empty DBs using Vals", func() {
63+
iter := client.Scan(ctx, 0, "", 10).Iterator()
64+
vals := slices.Collect(iter.Vals(ctx))
65+
Expect(vals).To(BeEmpty())
66+
Expect(iter.Err()).NotTo(HaveOccurred())
67+
})
68+
69+
It("should scan across one page using Next", func() {
6270
Expect(seed(7)).NotTo(HaveOccurred())
6371

6472
var vals []string
@@ -70,7 +78,16 @@ var _ = Describe("ScanIterator", func() {
7078
Expect(vals).To(ConsistOf([]string{"K01", "K02", "K03", "K04", "K05", "K06", "K07"}))
7179
})
7280

73-
It("should scan across multiple pages", func() {
81+
It("should scan across one page using Vals", func() {
82+
Expect(seed(7)).NotTo(HaveOccurred())
83+
84+
iter := client.Scan(ctx, 0, "", 0).Iterator()
85+
vals := slices.Collect(iter.Vals(ctx))
86+
Expect(iter.Err()).NotTo(HaveOccurred())
87+
Expect(vals).To(ConsistOf([]string{"K01", "K02", "K03", "K04", "K05", "K06", "K07"}))
88+
})
89+
90+
It("should scan across multiple pages using Next", func() {
7491
Expect(seed(71)).NotTo(HaveOccurred())
7592

7693
var vals []string
@@ -84,7 +101,18 @@ var _ = Describe("ScanIterator", func() {
84101
Expect(vals).To(ContainElement("K71"))
85102
})
86103

87-
It("should hscan across multiple pages", func() {
104+
It("should scan across multiple pages using Vals", func() {
105+
Expect(seed(71)).NotTo(HaveOccurred())
106+
107+
iter := client.Scan(ctx, 0, "", 10).Iterator()
108+
vals := slices.Collect(iter.Vals(ctx))
109+
Expect(iter.Err()).NotTo(HaveOccurred())
110+
Expect(vals).To(HaveLen(71))
111+
Expect(vals).To(ContainElement("K01"))
112+
Expect(vals).To(ContainElement("K71"))
113+
})
114+
115+
It("should hscan across multiple pages using Next", func() {
88116
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
89117
Expect(hashSeed(71)).NotTo(HaveOccurred())
90118

@@ -100,7 +128,20 @@ var _ = Describe("ScanIterator", func() {
100128
Expect(vals).To(ContainElement("x"))
101129
})
102130

103-
It("should hscan without values across multiple pages", Label("NonRedisEnterprise"), func() {
131+
It("should hscan across multiple pages using Vals", func() {
132+
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
133+
Expect(hashSeed(71)).NotTo(HaveOccurred())
134+
135+
iter := client.HScan(ctx, hashKey, 0, "", 10).Iterator()
136+
vals := slices.Collect(iter.Vals(ctx))
137+
Expect(iter.Err()).NotTo(HaveOccurred())
138+
Expect(vals).To(HaveLen(71 * 2))
139+
Expect(vals).To(ContainElement("K01"))
140+
Expect(vals).To(ContainElement("K71"))
141+
Expect(vals).To(ContainElement("x"))
142+
})
143+
144+
It("should hscan without values across multiple pages using Next", Label("NonRedisEnterprise"), func() {
104145
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
105146
Expect(hashSeed(71)).NotTo(HaveOccurred())
106147

@@ -116,7 +157,20 @@ var _ = Describe("ScanIterator", func() {
116157
Expect(vals).NotTo(ContainElement("x"))
117158
})
118159

119-
It("should scan to page borders", func() {
160+
It("should hscan without values across multiple pages using Vals", Label("NonRedisEnterprise"), func() {
161+
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
162+
Expect(hashSeed(71)).NotTo(HaveOccurred())
163+
164+
iter := client.HScanNoValues(ctx, hashKey, 0, "", 10).Iterator()
165+
vals := slices.Collect(iter.Vals(ctx))
166+
Expect(iter.Err()).NotTo(HaveOccurred())
167+
Expect(vals).To(HaveLen(71))
168+
Expect(vals).To(ContainElement("K01"))
169+
Expect(vals).To(ContainElement("K71"))
170+
Expect(vals).NotTo(ContainElement("x"))
171+
})
172+
173+
It("should scan to page borders using Next", func() {
120174
Expect(seed(20)).NotTo(HaveOccurred())
121175

122176
var vals []string
@@ -128,7 +182,16 @@ var _ = Describe("ScanIterator", func() {
128182
Expect(vals).To(HaveLen(20))
129183
})
130184

131-
It("should scan with match", func() {
185+
It("should scan to page borders using Vals", func() {
186+
Expect(seed(20)).NotTo(HaveOccurred())
187+
188+
iter := client.Scan(ctx, 0, "", 10).Iterator()
189+
vals := slices.Collect(iter.Vals(ctx))
190+
Expect(iter.Err()).NotTo(HaveOccurred())
191+
Expect(vals).To(HaveLen(20))
192+
})
193+
194+
It("should scan with match using Next", func() {
132195
Expect(seed(33)).NotTo(HaveOccurred())
133196

134197
var vals []string
@@ -140,7 +203,16 @@ var _ = Describe("ScanIterator", func() {
140203
Expect(vals).To(HaveLen(13))
141204
})
142205

143-
It("should scan with match across empty pages", func() {
206+
It("should scan with match using Vals", func() {
207+
Expect(seed(33)).NotTo(HaveOccurred())
208+
209+
iter := client.Scan(ctx, 0, "K*2*", 10).Iterator()
210+
vals := slices.Collect(iter.Vals(ctx))
211+
Expect(iter.Err()).NotTo(HaveOccurred())
212+
Expect(vals).To(HaveLen(13))
213+
})
214+
215+
It("should scan with match across empty pages using Next", func() {
144216
Expect(extraSeed(2, 10)).NotTo(HaveOccurred())
145217

146218
var vals []string
@@ -151,4 +223,13 @@ var _ = Describe("ScanIterator", func() {
151223
Expect(iter.Err()).NotTo(HaveOccurred())
152224
Expect(vals).To(HaveLen(2))
153225
})
226+
227+
It("should scan with match across empty pages using Vals", func() {
228+
Expect(extraSeed(2, 10)).NotTo(HaveOccurred())
229+
230+
iter := client.Scan(ctx, 0, "K*", 1).Iterator()
231+
vals := slices.Collect(iter.Vals(ctx))
232+
Expect(iter.Err()).NotTo(HaveOccurred())
233+
Expect(vals).To(HaveLen(2))
234+
})
154235
})

0 commit comments

Comments
 (0)