Merge branch 'master' into redundant-dial-mutex

This commit is contained in:
ofekshenawa 2024-11-13 13:40:15 +02:00 committed by GitHub
commit 7532edf6bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
52 changed files with 9249 additions and 2481 deletions

View File

@ -1,4 +1,5 @@
ACLs
APIs
autoload
autoloader
autoloading
@ -46,11 +47,14 @@ runtime
SHA
sharding
SETNAME
SpellCheck
SSL
struct
stunnel
SynDump
TCP
TLS
UnstableResp
uri
URI
url
@ -59,3 +63,5 @@ RedisStack
RedisGears
RedisTimeseries
RediSearch
RawResult
RawVal

View File

@ -37,3 +37,9 @@ jobs:
- name: Test
run: make test
- name: Upload to Codecov
uses: codecov/codecov-action@v4
with:
files: coverage.txt
token: ${{ secrets.CODECOV_TOKEN }}

View File

@ -14,6 +14,7 @@ test: testdeps
go test ./... -short -race && \
go test ./... -run=NONE -bench=. -benchmem && \
env GOOS=linux GOARCH=386 go test && \
go test -coverprofile=coverage.txt -covermode=atomic ./... && \
go vet); \
done
cd internal/customvet && go build .

View File

@ -3,6 +3,7 @@
[![build workflow](https://github.com/redis/go-redis/actions/workflows/build.yml/badge.svg)](https://github.com/redis/go-redis/actions)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/redis/go-redis/v9)](https://pkg.go.dev/github.com/redis/go-redis/v9?tab=doc)
[![Documentation](https://img.shields.io/badge/redis-documentation-informational)](https://redis.uptrace.dev/)
[![codecov](https://codecov.io/github/redis/go-redis/graph/badge.svg?token=tsrCZKuSSw)](https://codecov.io/github/redis/go-redis)
[![Chat](https://discordapp.com/api/guilds/752070105847955518/widget.png)](https://discord.gg/rWtp5Aj)
> go-redis is brought to you by :star: [**uptrace/uptrace**](https://github.com/uptrace/uptrace).
@ -182,6 +183,24 @@ rdb := redis.NewClient(&redis.Options{
})
```
#### Unstable RESP3 Structures for RediSearch Commands
When integrating Redis with application functionalities using RESP3, it's important to note that some response structures aren't final yet. This is especially true for more complex structures like search and query results. We recommend using RESP2 when using the search and query capabilities, but we plan to stabilize the RESP3-based API-s in the coming versions. You can find more guidance in the upcoming release notes.
To enable unstable RESP3, set the option in your client configuration:
```go
redis.NewClient(&redis.Options{
UnstableResp3: true,
})
```
**Note:** When UnstableResp3 mode is enabled, it's necessary to use RawResult() and RawVal() to retrieve a raw data.
Since, raw response is the only option for unstable search commands Val() and Result() calls wouldn't have any affect on them:
```go
res1, err := client.FTSearchWithArgs(ctx, "txt", "foo bar", &redis.FTSearchOptions{}).RawResult()
val1 := client.FTSearchWithArgs(ctx, "txt", "foo bar", &redis.FTSearchOptions{}).RawVal()
```
## Contributing
Please see [out contributing guidelines](CONTRIBUTING.md) to help us improve this library!

View File

@ -40,7 +40,7 @@ type Cmder interface {
readTimeout() *time.Duration
readReply(rd *proto.Reader) error
readRawReply(rd *proto.Reader) error
SetErr(error)
Err() error
}
@ -122,11 +122,11 @@ func cmdString(cmd Cmder, val interface{}) string {
//------------------------------------------------------------------------------
type baseCmd struct {
ctx context.Context
args []interface{}
err error
keyPos int8
ctx context.Context
args []interface{}
err error
keyPos int8
rawVal interface{}
_readTimeout *time.Duration
}
@ -197,6 +197,11 @@ func (cmd *baseCmd) setReadTimeout(d time.Duration) {
cmd._readTimeout = &d
}
func (cmd *baseCmd) readRawReply(rd *proto.Reader) (err error) {
cmd.rawVal, err = rd.ReadReply()
return err
}
//------------------------------------------------------------------------------
type Cmd struct {
@ -1398,27 +1403,63 @@ func (cmd *MapStringSliceInterfaceCmd) Val() map[string][]interface{} {
}
func (cmd *MapStringSliceInterfaceCmd) readReply(rd *proto.Reader) (err error) {
n, err := rd.ReadMapLen()
readType, err := rd.PeekReplyType()
if err != nil {
return err
}
cmd.val = make(map[string][]interface{}, n)
for i := 0; i < n; i++ {
k, err := rd.ReadString()
cmd.val = make(map[string][]interface{})
if readType == proto.RespMap {
n, err := rd.ReadMapLen()
if err != nil {
return err
}
nn, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmd.val[k] = make([]interface{}, nn)
for j := 0; j < nn; j++ {
value, err := rd.ReadReply()
for i := 0; i < n; i++ {
k, err := rd.ReadString()
if err != nil {
return err
}
cmd.val[k][j] = value
nn, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmd.val[k] = make([]interface{}, nn)
for j := 0; j < nn; j++ {
value, err := rd.ReadReply()
if err != nil {
return err
}
cmd.val[k][j] = value
}
}
} else if readType == proto.RespArray {
// RESP2 response
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
for i := 0; i < n; i++ {
// Each entry in this array is itself an array with key details
itemLen, err := rd.ReadArrayLen()
if err != nil {
return err
}
key, err := rd.ReadString()
if err != nil {
return err
}
cmd.val[key] = make([]interface{}, 0, itemLen-1)
for j := 1; j < itemLen; j++ {
// Read the inner array for timestamp-value pairs
data, err := rd.ReadReply()
if err != nil {
return err
}
cmd.val[key] = append(cmd.val[key], data)
}
}
}

View File

@ -0,0 +1,83 @@
// EXAMPLE: bf_tutorial
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_bloom() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:models")
// REMOVE_END
// STEP_START bloom
res1, err := rdb.BFReserve(ctx, "bikes:models", 0.01, 1000).Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> OK
res2, err := rdb.BFAdd(ctx, "bikes:models", "Smoky Mountain Striker").Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> true
res3, err := rdb.BFExists(ctx, "bikes:models", "Smoky Mountain Striker").Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> true
res4, err := rdb.BFMAdd(ctx, "bikes:models",
"Rocky Mountain Racer",
"Cloudy City Cruiser",
"Windy City Wippet",
).Result()
if err != nil {
panic(err)
}
fmt.Println(res4) // >>> [true true true]
res5, err := rdb.BFMExists(ctx, "bikes:models",
"Rocky Mountain Racer",
"Cloudy City Cruiser",
"Windy City Wippet",
).Result()
if err != nil {
panic(err)
}
fmt.Println(res5) // >>> [true true true]
// STEP_END
// Output:
// OK
// true
// true
// [true true true]
// [true true true]
}

View File

@ -0,0 +1,79 @@
// EXAMPLE: bitfield_tutorial
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_bf() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bike:1:stats")
// REMOVE_END
// STEP_START bf
res1, err := rdb.BitField(ctx, "bike:1:stats",
"set", "u32", "#0", "1000",
).Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> [0]
res2, err := rdb.BitField(ctx,
"bike:1:stats",
"incrby", "u32", "#0", "-50",
"incrby", "u32", "#1", "1",
).Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> [950 1]
res3, err := rdb.BitField(ctx,
"bike:1:stats",
"incrby", "u32", "#0", "500",
"incrby", "u32", "#1", "1",
).Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> [1450 2]
res4, err := rdb.BitField(ctx, "bike:1:stats",
"get", "u32", "#0",
"get", "u32", "#1",
).Result()
if err != nil {
panic(err)
}
fmt.Println(res4) // >>> [1450 2]
// STEP_END
// Output:
// [0]
// [950 1]
// [1450 2]
// [1450 2]
}

View File

@ -0,0 +1,92 @@
// EXAMPLE: bitmap_tutorial
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_ping() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "pings:2024-01-01-00:00")
// REMOVE_END
// STEP_START ping
res1, err := rdb.SetBit(ctx, "pings:2024-01-01-00:00", 123, 1).Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> 0
res2, err := rdb.GetBit(ctx, "pings:2024-01-01-00:00", 123).Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> 1
res3, err := rdb.GetBit(ctx, "pings:2024-01-01-00:00", 456).Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> 0
// STEP_END
// Output:
// 0
// 1
// 0
}
func ExampleClient_bitcount() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
_, err := rdb.SetBit(ctx, "pings:2024-01-01-00:00", 123, 1).Result()
if err != nil {
panic(err)
}
// REMOVE_END
// STEP_START bitcount
res4, err := rdb.BitCount(ctx, "pings:2024-01-01-00:00",
&redis.BitCount{
Start: 0,
End: 456,
}).Result()
if err != nil {
panic(err)
}
fmt.Println(res4) // >>> 1
// STEP_END
// Output:
// 1
}

View File

@ -0,0 +1,194 @@
// EXAMPLE: cmds_generic
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"math"
"time"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_del_cmd() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "key1", "key2", "key3")
// REMOVE_END
// STEP_START del
delResult1, err := rdb.Set(ctx, "key1", "Hello", 0).Result()
if err != nil {
panic(err)
}
fmt.Println(delResult1) // >>> OK
delResult2, err := rdb.Set(ctx, "key2", "World", 0).Result()
if err != nil {
panic(err)
}
fmt.Println(delResult2) // >>> OK
delResult3, err := rdb.Del(ctx, "key1", "key2", "key3").Result()
if err != nil {
panic(err)
}
fmt.Println(delResult3) // >>> 2
// STEP_END
// Output:
// OK
// OK
// 2
}
func ExampleClient_expire_cmd() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "mykey")
// REMOVE_END
// STEP_START expire
expireResult1, err := rdb.Set(ctx, "mykey", "Hello", 0).Result()
if err != nil {
panic(err)
}
fmt.Println(expireResult1) // >>> OK
expireResult2, err := rdb.Expire(ctx, "mykey", 10*time.Second).Result()
if err != nil {
panic(err)
}
fmt.Println(expireResult2) // >>> true
expireResult3, err := rdb.TTL(ctx, "mykey").Result()
if err != nil {
panic(err)
}
fmt.Println(math.Round(expireResult3.Seconds())) // >>> 10
expireResult4, err := rdb.Set(ctx, "mykey", "Hello World", 0).Result()
if err != nil {
panic(err)
}
fmt.Println(expireResult4) // >>> OK
expireResult5, err := rdb.TTL(ctx, "mykey").Result()
if err != nil {
panic(err)
}
fmt.Println(expireResult5) // >>> -1ns
expireResult6, err := rdb.ExpireXX(ctx, "mykey", 10*time.Second).Result()
if err != nil {
panic(err)
}
fmt.Println(expireResult6) // >>> false
expireResult7, err := rdb.TTL(ctx, "mykey").Result()
if err != nil {
panic(err)
}
fmt.Println(expireResult7) // >>> -1ns
expireResult8, err := rdb.ExpireNX(ctx, "mykey", 10*time.Second).Result()
if err != nil {
panic(err)
}
fmt.Println(expireResult8) // >>> true
expireResult9, err := rdb.TTL(ctx, "mykey").Result()
if err != nil {
panic(err)
}
fmt.Println(math.Round(expireResult9.Seconds())) // >>> 10
// STEP_END
// Output:
// OK
// true
// 10
// OK
// -1ns
// false
// -1ns
// true
// 10
}
func ExampleClient_ttl_cmd() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "mykey")
// REMOVE_END
// STEP_START ttl
ttlResult1, err := rdb.Set(ctx, "mykey", "Hello", 10*time.Second).Result()
if err != nil {
panic(err)
}
fmt.Println(ttlResult1) // >>> OK
ttlResult2, err := rdb.TTL(ctx, "mykey").Result()
if err != nil {
panic(err)
}
fmt.Println(math.Round(ttlResult2.Seconds())) // >>> 10
// STEP_END
// Output:
// OK
// 10
}

133
doctests/cmds_hash_test.go Normal file
View File

@ -0,0 +1,133 @@
// EXAMPLE: cmds_hash
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_hset() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "myhash")
// REMOVE_END
// STEP_START hset
res1, err := rdb.HSet(ctx, "myhash", "field1", "Hello").Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> 1
res2, err := rdb.HGet(ctx, "myhash", "field1").Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> Hello
res3, err := rdb.HSet(ctx, "myhash",
"field2", "Hi",
"field3", "World",
).Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> 2
res4, err := rdb.HGet(ctx, "myhash", "field2").Result()
if err != nil {
panic(err)
}
fmt.Println(res4) // >>> Hi
res5, err := rdb.HGet(ctx, "myhash", "field3").Result()
if err != nil {
panic(err)
}
fmt.Println(res5) // >>> World
res6, err := rdb.HGetAll(ctx, "myhash").Result()
if err != nil {
panic(err)
}
fmt.Println(res6)
// >>> map[field1:Hello field2:Hi field3:World]
// STEP_END
// Output:
// 1
// Hello
// 2
// Hi
// World
// map[field1:Hello field2:Hi field3:World]
}
func ExampleClient_hget() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "myhash")
// REMOVE_END
// STEP_START hget
res7, err := rdb.HSet(ctx, "myhash", "field1", "foo").Result()
if err != nil {
panic(err)
}
fmt.Println(res7) // >>> 1
res8, err := rdb.HGet(ctx, "myhash", "field1").Result()
if err != nil {
panic(err)
}
fmt.Println(res8) // >>> foo
res9, err := rdb.HGet(ctx, "myhash", "field2").Result()
if err != nil {
fmt.Println(err)
}
fmt.Println(res9) // >>> <empty string>
// STEP_END
// Output:
// 1
// foo
// redis: nil
}

View File

@ -0,0 +1,220 @@
// EXAMPLE: cmds_sorted_set
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_zadd_cmd() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "myzset")
// REMOVE_END
// STEP_START zadd
zAddResult1, err := rdb.ZAdd(ctx, "myzset",
redis.Z{Member: "one", Score: 1},
).Result()
if err != nil {
panic(err)
}
fmt.Println(zAddResult1) // >>> 1
zAddResult2, err := rdb.ZAdd(ctx, "myzset",
redis.Z{Member: "uno", Score: 1},
).Result()
if err != nil {
panic(err)
}
fmt.Println(zAddResult2)
zAddResult3, err := rdb.ZAdd(ctx, "myzset",
redis.Z{Member: "two", Score: 2},
redis.Z{Member: "three", Score: 3},
).Result()
if err != nil {
panic(err)
}
fmt.Println(zAddResult3) // >>> 2
zAddResult4, err := rdb.ZRangeWithScores(ctx, "myzset", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(zAddResult4) // >>> [{1 one} {1 uno} {2 two} {3 three}]
// STEP_END
// Output:
// 1
// 1
// 2
// [{1 one} {1 uno} {2 two} {3 three}]
}
func ExampleClient_zrange1() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "myzset")
// REMOVE_END
// STEP_START zrange1
zrangeResult1, err := rdb.ZAdd(ctx, "myzset",
redis.Z{Member: "one", Score: 1},
redis.Z{Member: "two", Score: 2},
redis.Z{Member: "three", Score: 3},
).Result()
if err != nil {
panic(err)
}
fmt.Println(zrangeResult1) // >>> 3
zrangeResult2, err := rdb.ZRange(ctx, "myzset", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(zrangeResult2) // >>> [one two three]
zrangeResult3, err := rdb.ZRange(ctx, "myzset", 2, 3).Result()
if err != nil {
panic(err)
}
fmt.Println(zrangeResult3) // >>> [three]
zrangeResult4, err := rdb.ZRange(ctx, "myzset", -2, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(zrangeResult4) // >>> [two three]
// STEP_END
// Output:
// 3
// [one two three]
// [three]
// [two three]
}
func ExampleClient_zrange2() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "myzset")
// REMOVE_END
// STEP_START zrange2
zRangeResult5, err := rdb.ZAdd(ctx, "myzset",
redis.Z{Member: "one", Score: 1},
redis.Z{Member: "two", Score: 2},
redis.Z{Member: "three", Score: 3},
).Result()
if err != nil {
panic(err)
}
fmt.Println(zRangeResult5) // >>> 3
zRangeResult6, err := rdb.ZRangeWithScores(ctx, "myzset", 0, 1).Result()
if err != nil {
panic(err)
}
fmt.Println(zRangeResult6) // >>> [{1 one} {2 two}]
// STEP_END
// Output:
// 3
// [{1 one} {2 two}]
}
func ExampleClient_zrange3() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "myzset")
// REMOVE_END
// STEP_START zrange3
zRangeResult7, err := rdb.ZAdd(ctx, "myzset",
redis.Z{Member: "one", Score: 1},
redis.Z{Member: "two", Score: 2},
redis.Z{Member: "three", Score: 3},
).Result()
if err != nil {
panic(err)
}
fmt.Println(zRangeResult7) // >>> 3
zRangeResult8, err := rdb.ZRangeArgs(ctx,
redis.ZRangeArgs{
Key: "myzset",
ByScore: true,
Start: "(1",
Stop: "+inf",
Offset: 1,
Count: 1,
},
).Result()
if err != nil {
panic(err)
}
fmt.Println(zRangeResult8) // >>> [three]
// STEP_END
// Output:
// 3
// [three]
}

View File

@ -0,0 +1,57 @@
// EXAMPLE: cmds_string
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_cmd_incr() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "mykey")
// REMOVE_END
// STEP_START incr
incrResult1, err := rdb.Set(ctx, "mykey", "10", 0).Result()
if err != nil {
panic(err)
}
fmt.Println(incrResult1) // >>> OK
incrResult2, err := rdb.Incr(ctx, "mykey").Result()
if err != nil {
panic(err)
}
fmt.Println(incrResult2) // >>> 11
incrResult3, err := rdb.Get(ctx, "mykey").Result()
if err != nil {
panic(err)
}
fmt.Println(incrResult3) // >>> 11
// STEP_END
// Output:
// OK
// 11
// 11
}

View File

@ -0,0 +1,84 @@
// EXAMPLE: cms_tutorial
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_cms() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:profit")
// REMOVE_END
// STEP_START cms
res1, err := rdb.CMSInitByProb(ctx, "bikes:profit", 0.001, 0.002).Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> OK
res2, err := rdb.CMSIncrBy(ctx, "bikes:profit",
"Smoky Mountain Striker", 100,
).Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> [100]
res3, err := rdb.CMSIncrBy(ctx, "bikes:profit",
"Rocky Mountain Racer", 200,
"Cloudy City Cruiser", 150,
).Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> [200 150]
res4, err := rdb.CMSQuery(ctx, "bikes:profit",
"Smoky Mountain Striker",
).Result()
if err != nil {
panic(err)
}
fmt.Println(res4) // >>> [100]
res5, err := rdb.CMSInfo(ctx, "bikes:profit").Result()
if err != nil {
panic(err)
}
fmt.Printf("Width: %v, Depth: %v, Count: %v",
res5.Width, res5.Depth, res5.Count)
// >>> Width: 2000, Depth: 9, Count: 450
// STEP_END
// Output:
// OK
// [100]
// [200 150]
// [100]
// Width: 2000, Depth: 9, Count: 450
}

View File

@ -0,0 +1,75 @@
// EXAMPLE: cuckoo_tutorial
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_cuckoo() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:models")
// REMOVE_END
// STEP_START cuckoo
res1, err := rdb.CFReserve(ctx, "bikes:models", 1000000).Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> OK
res2, err := rdb.CFAdd(ctx, "bikes:models", "Smoky Mountain Striker").Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> true
res3, err := rdb.CFExists(ctx, "bikes:models", "Smoky Mountain Striker").Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> true
res4, err := rdb.CFExists(ctx, "bikes:models", "Terrible Bike Name").Result()
if err != nil {
panic(err)
}
fmt.Println(res4) // >>> false
res5, err := rdb.CFDel(ctx, "bikes:models", "Smoky Mountain Striker").Result()
if err != nil {
panic(err)
}
fmt.Println(res5) // >>> true
// STEP_END
// Output:
// OK
// true
// true
// false
// true
}

View File

@ -0,0 +1,139 @@
// EXAMPLE: geo_tutorial
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_geoadd() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:rentable")
// REMOVE_END
// STEP_START geoadd
res1, err := rdb.GeoAdd(ctx, "bikes:rentable",
&redis.GeoLocation{
Longitude: -122.27652,
Latitude: 37.805186,
Name: "station:1",
}).Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> 1
res2, err := rdb.GeoAdd(ctx, "bikes:rentable",
&redis.GeoLocation{
Longitude: -122.2674626,
Latitude: 37.8062344,
Name: "station:2",
}).Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> 1
res3, err := rdb.GeoAdd(ctx, "bikes:rentable",
&redis.GeoLocation{
Longitude: -122.2469854,
Latitude: 37.8104049,
Name: "station:3",
}).Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> 1
// STEP_END
// Output:
// 1
// 1
// 1
}
func ExampleClient_geosearch() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:rentable")
_, err := rdb.GeoAdd(ctx, "bikes:rentable",
&redis.GeoLocation{
Longitude: -122.27652,
Latitude: 37.805186,
Name: "station:1",
}).Result()
if err != nil {
panic(err)
}
_, err = rdb.GeoAdd(ctx, "bikes:rentable",
&redis.GeoLocation{
Longitude: -122.2674626,
Latitude: 37.8062344,
Name: "station:2",
}).Result()
if err != nil {
panic(err)
}
_, err = rdb.GeoAdd(ctx, "bikes:rentable",
&redis.GeoLocation{
Longitude: -122.2469854,
Latitude: 37.8104049,
Name: "station:3",
}).Result()
if err != nil {
panic(err)
}
// REMOVE_END
// STEP_START geosearch
res4, err := rdb.GeoSearch(ctx, "bikes:rentable",
&redis.GeoSearchQuery{
Longitude: -122.27652,
Latitude: 37.805186,
Radius: 5,
RadiusUnit: "km",
},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res4) // >>> [station:1 station:2 station:3]
// STEP_END
// Output:
// [station:1 station:2 station:3]
}

View File

@ -0,0 +1,281 @@
// EXAMPLE: hash_tutorial
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_set_get_all() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bike:1")
// REMOVE_END
// STEP_START set_get_all
hashFields := []string{
"model", "Deimos",
"brand", "Ergonom",
"type", "Enduro bikes",
"price", "4972",
}
res1, err := rdb.HSet(ctx, "bike:1", hashFields).Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> 4
res2, err := rdb.HGet(ctx, "bike:1", "model").Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> Deimos
res3, err := rdb.HGet(ctx, "bike:1", "price").Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> 4972
cmdReturn := rdb.HGetAll(ctx, "bike:1")
res4, err := cmdReturn.Result()
if err != nil {
panic(err)
}
fmt.Println(res4)
// >>> map[brand:Ergonom model:Deimos price:4972 type:Enduro bikes]
type BikeInfo struct {
Model string `redis:"model"`
Brand string `redis:"brand"`
Type string `redis:"type"`
Price int `redis:"price"`
}
var res4a BikeInfo
if err := cmdReturn.Scan(&res4a); err != nil {
panic(err)
}
fmt.Printf("Model: %v, Brand: %v, Type: %v, Price: $%v\n",
res4a.Model, res4a.Brand, res4a.Type, res4a.Price)
// >>> Model: Deimos, Brand: Ergonom, Type: Enduro bikes, Price: $4972
// STEP_END
// Output:
// 4
// Deimos
// 4972
// map[brand:Ergonom model:Deimos price:4972 type:Enduro bikes]
// Model: Deimos, Brand: Ergonom, Type: Enduro bikes, Price: $4972
}
func ExampleClient_hmget() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bike:1")
// REMOVE_END
hashFields := []string{
"model", "Deimos",
"brand", "Ergonom",
"type", "Enduro bikes",
"price", "4972",
}
_, err := rdb.HSet(ctx, "bike:1", hashFields).Result()
if err != nil {
panic(err)
}
// STEP_START hmget
cmdReturn := rdb.HMGet(ctx, "bike:1", "model", "price")
res5, err := cmdReturn.Result()
if err != nil {
panic(err)
}
fmt.Println(res5) // >>> [Deimos 4972]
type BikeInfo struct {
Model string `redis:"model"`
Brand string `redis:"-"`
Type string `redis:"-"`
Price int `redis:"price"`
}
var res5a BikeInfo
if err := cmdReturn.Scan(&res5a); err != nil {
panic(err)
}
fmt.Printf("Model: %v, Price: $%v\n", res5a.Model, res5a.Price)
// >>> Model: Deimos, Price: $4972
// STEP_END
// Output:
// [Deimos 4972]
// Model: Deimos, Price: $4972
}
func ExampleClient_hincrby() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bike:1")
// REMOVE_END
hashFields := []string{
"model", "Deimos",
"brand", "Ergonom",
"type", "Enduro bikes",
"price", "4972",
}
_, err := rdb.HSet(ctx, "bike:1", hashFields).Result()
if err != nil {
panic(err)
}
// STEP_START hincrby
res6, err := rdb.HIncrBy(ctx, "bike:1", "price", 100).Result()
if err != nil {
panic(err)
}
fmt.Println(res6) // >>> 5072
res7, err := rdb.HIncrBy(ctx, "bike:1", "price", -100).Result()
if err != nil {
panic(err)
}
fmt.Println(res7) // >>> 4972
// STEP_END
// Output:
// 5072
// 4972
}
func ExampleClient_incrby_get_mget() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bike:1:stats")
// REMOVE_END
// STEP_START incrby_get_mget
res8, err := rdb.HIncrBy(ctx, "bike:1:stats", "rides", 1).Result()
if err != nil {
panic(err)
}
fmt.Println(res8) // >>> 1
res9, err := rdb.HIncrBy(ctx, "bike:1:stats", "rides", 1).Result()
if err != nil {
panic(err)
}
fmt.Println(res9) // >>> 2
res10, err := rdb.HIncrBy(ctx, "bike:1:stats", "rides", 1).Result()
if err != nil {
panic(err)
}
fmt.Println(res10) // >>> 3
res11, err := rdb.HIncrBy(ctx, "bike:1:stats", "crashes", 1).Result()
if err != nil {
panic(err)
}
fmt.Println(res11) // >>> 1
res12, err := rdb.HIncrBy(ctx, "bike:1:stats", "owners", 1).Result()
if err != nil {
panic(err)
}
fmt.Println(res12) // >>> 1
res13, err := rdb.HGet(ctx, "bike:1:stats", "rides").Result()
if err != nil {
panic(err)
}
fmt.Println(res13) // >>> 3
res14, err := rdb.HMGet(ctx, "bike:1:stats", "crashes", "owners").Result()
if err != nil {
panic(err)
}
fmt.Println(res14) // >>> [1 1]
// STEP_END
// Output:
// 1
// 2
// 3
// 1
// 1
// 3
// [1 1]
}

View File

@ -0,0 +1,75 @@
// EXAMPLE: hll_tutorial
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_pfadd() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes", "commuter_bikes", "all_bikes")
// REMOVE_END
// STEP_START pfadd
res1, err := rdb.PFAdd(ctx, "bikes", "Hyperion", "Deimos", "Phoebe", "Quaoar").Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // 1
res2, err := rdb.PFCount(ctx, "bikes").Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // 4
res3, err := rdb.PFAdd(ctx, "commuter_bikes", "Salacia", "Mimas", "Quaoar").Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // 1
res4, err := rdb.PFMerge(ctx, "all_bikes", "bikes", "commuter_bikes").Result()
if err != nil {
panic(err)
}
fmt.Println(res4) // OK
res5, err := rdb.PFCount(ctx, "all_bikes").Result()
if err != nil {
panic(err)
}
fmt.Println(res5) // 6
// STEP_END
// Output:
// 1
// 4
// 1
// OK
// 6
}

View File

@ -0,0 +1,199 @@
// EXAMPLE: go_home_json
// HIDE_START
package example_commands_test
// HIDE_END
// STEP_START import
import (
"context"
"fmt"
"sort"
"github.com/redis/go-redis/v9"
)
// STEP_END
func ExampleClient_search_json() {
// STEP_START connect
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
Protocol: 2,
})
// STEP_END
// REMOVE_START
rdb.Del(ctx, "user:1", "user:2", "user:3")
rdb.FTDropIndex(ctx, "idx:users")
// REMOVE_END
// STEP_START create_data
user1 := map[string]interface{}{
"name": "Paul John",
"email": "paul.john@example.com",
"age": 42,
"city": "London",
}
user2 := map[string]interface{}{
"name": "Eden Zamir",
"email": "eden.zamir@example.com",
"age": 29,
"city": "Tel Aviv",
}
user3 := map[string]interface{}{
"name": "Paul Zamir",
"email": "paul.zamir@example.com",
"age": 35,
"city": "Tel Aviv",
}
// STEP_END
// STEP_START make_index
_, err := rdb.FTCreate(
ctx,
"idx:users",
// Options:
&redis.FTCreateOptions{
OnJSON: true,
Prefix: []interface{}{"user:"},
},
// Index schema fields:
&redis.FieldSchema{
FieldName: "$.name",
As: "name",
FieldType: redis.SearchFieldTypeText,
},
&redis.FieldSchema{
FieldName: "$.city",
As: "city",
FieldType: redis.SearchFieldTypeTag,
},
&redis.FieldSchema{
FieldName: "$.age",
As: "age",
FieldType: redis.SearchFieldTypeNumeric,
},
).Result()
if err != nil {
panic(err)
}
// STEP_END
// STEP_START add_data
_, err = rdb.JSONSet(ctx, "user:1", "$", user1).Result()
if err != nil {
panic(err)
}
_, err = rdb.JSONSet(ctx, "user:2", "$", user2).Result()
if err != nil {
panic(err)
}
_, err = rdb.JSONSet(ctx, "user:3", "$", user3).Result()
if err != nil {
panic(err)
}
// STEP_END
// STEP_START query1
findPaulResult, err := rdb.FTSearch(
ctx,
"idx:users",
"Paul @age:[30 40]",
).Result()
if err != nil {
panic(err)
}
fmt.Println(findPaulResult)
// >>> {1 [{user:3 <nil> <nil> <nil> map[$:{"age":35,"city":"Tel Aviv"...
// STEP_END
// STEP_START query2
citiesResult, err := rdb.FTSearchWithArgs(
ctx,
"idx:users",
"Paul",
&redis.FTSearchOptions{
Return: []redis.FTSearchReturn{
{
FieldName: "$.city",
As: "city",
},
},
},
).Result()
if err != nil {
panic(err)
}
sort.Slice(citiesResult.Docs, func(i, j int) bool {
return citiesResult.Docs[i].Fields["city"] < citiesResult.Docs[j].Fields["city"]
})
for _, result := range citiesResult.Docs {
fmt.Println(result.Fields["city"])
}
// >>> London
// >>> Tel Aviv
// STEP_END
// STEP_START query3
aggOptions := redis.FTAggregateOptions{
GroupBy: []redis.FTAggregateGroupBy{
{
Fields: []interface{}{"@city"},
Reduce: []redis.FTAggregateReducer{
{
Reducer: redis.SearchCount,
As: "count",
},
},
},
},
}
aggResult, err := rdb.FTAggregateWithArgs(
ctx,
"idx:users",
"*",
&aggOptions,
).Result()
if err != nil {
panic(err)
}
sort.Slice(aggResult.Rows, func(i, j int) bool {
return aggResult.Rows[i].Fields["city"].(string) <
aggResult.Rows[j].Fields["city"].(string)
})
for _, row := range aggResult.Rows {
fmt.Printf("%v - %v\n",
row.Fields["city"], row.Fields["count"],
)
}
// >>> City: London - 1
// >>> City: Tel Aviv - 2
// STEP_END
// Output:
// {1 [{user:3 <nil> <nil> <nil> map[$:{"age":35,"city":"Tel Aviv","email":"paul.zamir@example.com","name":"Paul Zamir"}]}]}
// London
// Tel Aviv
// London - 1
// Tel Aviv - 2
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,766 @@
// EXAMPLE: list_tutorial
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_queue() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
// STEP_START queue
res1, err := rdb.LPush(ctx, "bikes:repairs", "bike:1").Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> 1
res2, err := rdb.LPush(ctx, "bikes:repairs", "bike:2").Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> 2
res3, err := rdb.RPop(ctx, "bikes:repairs").Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> bike:1
res4, err := rdb.RPop(ctx, "bikes:repairs").Result()
if err != nil {
panic(err)
}
fmt.Println(res4) // >>> bike:2
// STEP_END
// Output:
// 1
// 2
// bike:1
// bike:2
}
func ExampleClient_stack() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
// STEP_START stack
res5, err := rdb.LPush(ctx, "bikes:repairs", "bike:1").Result()
if err != nil {
panic(err)
}
fmt.Println(res5) // >>> 1
res6, err := rdb.LPush(ctx, "bikes:repairs", "bike:2").Result()
if err != nil {
panic(err)
}
fmt.Println(res6) // >>> 2
res7, err := rdb.LPop(ctx, "bikes:repairs").Result()
if err != nil {
panic(err)
}
fmt.Println(res7) // >>> bike:2
res8, err := rdb.LPop(ctx, "bikes:repairs").Result()
if err != nil {
panic(err)
}
fmt.Println(res8) // >>> bike:1
// STEP_END
// Output:
// 1
// 2
// bike:2
// bike:1
}
func ExampleClient_llen() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
// STEP_START llen
res9, err := rdb.LLen(ctx, "bikes:repairs").Result()
if err != nil {
panic(err)
}
fmt.Println(res9) // >>> 0
// STEP_END
// Output:
// 0
}
func ExampleClient_lmove_lrange() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:repairs")
rdb.Del(ctx, "bikes:finished")
// REMOVE_END
// STEP_START lmove_lrange
res10, err := rdb.LPush(ctx, "bikes:repairs", "bike:1").Result()
if err != nil {
panic(err)
}
fmt.Println(res10) // >>> 1
res11, err := rdb.LPush(ctx, "bikes:repairs", "bike:2").Result()
if err != nil {
panic(err)
}
fmt.Println(res11) // >>> 2
res12, err := rdb.LMove(ctx, "bikes:repairs", "bikes:finished", "LEFT", "LEFT").Result()
if err != nil {
panic(err)
}
fmt.Println(res12) // >>> bike:2
res13, err := rdb.LRange(ctx, "bikes:repairs", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(res13) // >>> [bike:1]
res14, err := rdb.LRange(ctx, "bikes:finished", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(res14) // >>> [bike:2]
// STEP_END
// Output:
// 1
// 2
// bike:2
// [bike:1]
// [bike:2]
}
func ExampleClient_lpush_rpush() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
// STEP_START lpush_rpush
res15, err := rdb.RPush(ctx, "bikes:repairs", "bike:1").Result()
if err != nil {
panic(err)
}
fmt.Println(res15) // >>> 1
res16, err := rdb.RPush(ctx, "bikes:repairs", "bike:2").Result()
if err != nil {
panic(err)
}
fmt.Println(res16) // >>> 2
res17, err := rdb.LPush(ctx, "bikes:repairs", "bike:important_bike").Result()
if err != nil {
panic(err)
}
fmt.Println(res17) // >>> 3
res18, err := rdb.LRange(ctx, "bikes:repairs", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(res18) // >>> [bike:important_bike bike:1 bike:2]
// STEP_END
// Output:
// 1
// 2
// 3
// [bike:important_bike bike:1 bike:2]
}
func ExampleClient_variadic() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
// STEP_START variadic
res19, err := rdb.RPush(ctx, "bikes:repairs", "bike:1", "bike:2", "bike:3").Result()
if err != nil {
panic(err)
}
fmt.Println(res19) // >>> 3
res20, err := rdb.LPush(ctx, "bikes:repairs", "bike:important_bike", "bike:very_important_bike").Result()
if err != nil {
panic(err)
}
fmt.Println(res20) // >>> 5
res21, err := rdb.LRange(ctx, "bikes:repairs", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(res21) // >>> [bike:very_important_bike bike:important_bike bike:1 bike:2 bike:3]
// STEP_END
// Output:
// 3
// 5
// [bike:very_important_bike bike:important_bike bike:1 bike:2 bike:3]
}
func ExampleClient_lpop_rpop() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
// STEP_START lpop_rpop
res22, err := rdb.RPush(ctx, "bikes:repairs", "bike:1", "bike:2", "bike:3").Result()
if err != nil {
panic(err)
}
fmt.Println(res22) // >>> 3
res23, err := rdb.RPop(ctx, "bikes:repairs").Result()
if err != nil {
panic(err)
}
fmt.Println(res23) // >>> bike:3
res24, err := rdb.LPop(ctx, "bikes:repairs").Result()
if err != nil {
panic(err)
}
fmt.Println(res24) // >>> bike:1
res25, err := rdb.RPop(ctx, "bikes:repairs").Result()
if err != nil {
panic(err)
}
fmt.Println(res25) // >>> bike:2
res26, err := rdb.RPop(ctx, "bikes:repairs").Result()
if err != nil {
fmt.Println(err) // >>> redis: nil
}
fmt.Println(res26) // >>> <empty string>
// STEP_END
// Output:
// 3
// bike:3
// bike:1
// bike:2
// redis: nil
//
}
func ExampleClient_ltrim() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
// STEP_START ltrim
res27, err := rdb.LPush(ctx, "bikes:repairs", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5").Result()
if err != nil {
panic(err)
}
fmt.Println(res27) // >>> 5
res28, err := rdb.LTrim(ctx, "bikes:repairs", 0, 2).Result()
if err != nil {
panic(err)
}
fmt.Println(res28) // >>> OK
res29, err := rdb.LRange(ctx, "bikes:repairs", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(res29) // >>> [bike:5 bike:4 bike:3]
// STEP_END
// Output:
// 5
// OK
// [bike:5 bike:4 bike:3]
}
func ExampleClient_ltrim_end_of_list() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
// STEP_START ltrim_end_of_list
res30, err := rdb.RPush(ctx, "bikes:repairs", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5").Result()
if err != nil {
panic(err)
}
fmt.Println(res30) // >>> 5
res31, err := rdb.LTrim(ctx, "bikes:repairs", -3, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(res31) // >>> OK
res32, err := rdb.LRange(ctx, "bikes:repairs", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(res32) // >>> [bike:3 bike:4 bike:5]
// STEP_END
// Output:
// 5
// OK
// [bike:3 bike:4 bike:5]
}
func ExampleClient_brpop() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
// STEP_START brpop
res33, err := rdb.RPush(ctx, "bikes:repairs", "bike:1", "bike:2").Result()
if err != nil {
panic(err)
}
fmt.Println(res33) // >>> 2
res34, err := rdb.BRPop(ctx, 1, "bikes:repairs").Result()
if err != nil {
panic(err)
}
fmt.Println(res34) // >>> [bikes:repairs bike:2]
res35, err := rdb.BRPop(ctx, 1, "bikes:repairs").Result()
if err != nil {
panic(err)
}
fmt.Println(res35) // >>> [bikes:repairs bike:1]
res36, err := rdb.BRPop(ctx, 1, "bikes:repairs").Result()
if err != nil {
fmt.Println(err) // >>> redis: nil
}
fmt.Println(res36) // >>> []
// STEP_END
// Output:
// 2
// [bikes:repairs bike:2]
// [bikes:repairs bike:1]
// redis: nil
// []
}
func ExampleClient_rule1() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "new_bikes")
// REMOVE_END
// STEP_START rule_1
res37, err := rdb.Del(ctx, "new_bikes").Result()
if err != nil {
panic(err)
}
fmt.Println(res37) // >>> 0
res38, err := rdb.LPush(ctx, "new_bikes", "bike:1", "bike:2", "bike:3").Result()
if err != nil {
panic(err)
}
fmt.Println(res38) // >>> 3
// STEP_END
// Output:
// 0
// 3
}
func ExampleClient_rule11() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "new_bikes")
// REMOVE_END
// STEP_START rule_1.1
res39, err := rdb.Set(ctx, "new_bikes", "bike:1", 0).Result()
if err != nil {
panic(err)
}
fmt.Println(res39) // >>> OK
res40, err := rdb.Type(ctx, "new_bikes").Result()
if err != nil {
panic(err)
}
fmt.Println(res40) // >>> string
res41, err := rdb.LPush(ctx, "new_bikes", "bike:2", "bike:3").Result()
if err != nil {
fmt.Println(err)
// >>> WRONGTYPE Operation against a key holding the wrong kind of value
}
fmt.Println(res41)
// STEP_END
// Output:
// OK
// string
// WRONGTYPE Operation against a key holding the wrong kind of value
// 0
}
func ExampleClient_rule2() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
// STEP_START rule_2
res42, err := rdb.LPush(ctx, "bikes:repairs", "bike:1", "bike:2", "bike:3").Result()
if err != nil {
panic(err)
}
fmt.Println(res42) // >>> 3
res43, err := rdb.Exists(ctx, "bikes:repairs").Result()
if err != nil {
panic(err)
}
fmt.Println(res43) // >>> 1
res44, err := rdb.LPop(ctx, "bikes:repairs").Result()
if err != nil {
panic(err)
}
fmt.Println(res44) // >>> bike:3
res45, err := rdb.LPop(ctx, "bikes:repairs").Result()
if err != nil {
panic(err)
}
fmt.Println(res45) // >>> bike:2
res46, err := rdb.LPop(ctx, "bikes:repairs").Result()
if err != nil {
panic(err)
}
fmt.Println(res46) // >>> bike:1
res47, err := rdb.Exists(ctx, "bikes:repairs").Result()
if err != nil {
panic(err)
}
fmt.Println(res47) // >>> 0
// STEP_END
// Output:
// 3
// 1
// bike:3
// bike:2
// bike:1
// 0
}
func ExampleClient_rule3() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
// STEP_START rule_3
res48, err := rdb.Del(ctx, "bikes:repairs").Result()
if err != nil {
panic(err)
}
fmt.Println(res48) // >>> 0
res49, err := rdb.LLen(ctx, "bikes:repairs").Result()
if err != nil {
panic(err)
}
fmt.Println(res49) // >>> 0
res50, err := rdb.LPop(ctx, "bikes:repairs").Result()
if err != nil {
fmt.Println(err) // >>> redis: nil
}
fmt.Println(res50) // >>> <empty string>
// STEP_END
// Output:
// 0
// 0
// redis: nil
//
}
func ExampleClient_ltrim1() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
// STEP_START ltrim.1
res51, err := rdb.LPush(ctx, "bikes:repairs", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5").Result()
if err != nil {
panic(err)
}
fmt.Println(res51) // >>> 5
res52, err := rdb.LTrim(ctx, "bikes:repairs", 0, 2).Result()
if err != nil {
panic(err)
}
fmt.Println(res52) // >>> OK
res53, err := rdb.LRange(ctx, "bikes:repairs", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(res53) // >>> [bike:5 bike:4 bike:3]
// STEP_END
// Output:
// 5
// OK
// [bike:5 bike:4 bike:3]
}

View File

@ -0,0 +1,442 @@
// EXAMPLE: sets_tutorial
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_sadd() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:racing:france")
rdb.Del(ctx, "bikes:racing:usa")
// REMOVE_END
// STEP_START sadd
res1, err := rdb.SAdd(ctx, "bikes:racing:france", "bike:1").Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> 1
res2, err := rdb.SAdd(ctx, "bikes:racing:france", "bike:1").Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> 0
res3, err := rdb.SAdd(ctx, "bikes:racing:france", "bike:2", "bike:3").Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> 2
res4, err := rdb.SAdd(ctx, "bikes:racing:usa", "bike:1", "bike:4").Result()
if err != nil {
panic(err)
}
fmt.Println(res4) // >>> 2
// STEP_END
// Output:
// 1
// 0
// 2
// 2
}
func ExampleClient_sismember() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:racing:france")
rdb.Del(ctx, "bikes:racing:usa")
// REMOVE_END
_, err := rdb.SAdd(ctx, "bikes:racing:france", "bike:1", "bike:2", "bike:3").Result()
if err != nil {
panic(err)
}
_, err = rdb.SAdd(ctx, "bikes:racing:usa", "bike:1", "bike:4").Result()
if err != nil {
panic(err)
}
// STEP_START sismember
res5, err := rdb.SIsMember(ctx, "bikes:racing:usa", "bike:1").Result()
if err != nil {
panic(err)
}
fmt.Println(res5) // >>> true
res6, err := rdb.SIsMember(ctx, "bikes:racing:usa", "bike:2").Result()
if err != nil {
panic(err)
}
fmt.Println(res6) // >>> false
// STEP_END
// Output:
// true
// false
}
func ExampleClient_sinter() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:racing:france")
rdb.Del(ctx, "bikes:racing:usa")
// REMOVE_END
_, err := rdb.SAdd(ctx, "bikes:racing:france", "bike:1", "bike:2", "bike:3").Result()
if err != nil {
panic(err)
}
_, err = rdb.SAdd(ctx, "bikes:racing:usa", "bike:1", "bike:4").Result()
if err != nil {
panic(err)
}
// STEP_START sinter
res7, err := rdb.SInter(ctx, "bikes:racing:france", "bikes:racing:usa").Result()
if err != nil {
panic(err)
}
fmt.Println(res7) // >>> [bike:1]
// STEP_END
// Output:
// [bike:1]
}
func ExampleClient_scard() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:racing:france")
// REMOVE_END
_, err := rdb.SAdd(ctx, "bikes:racing:france", "bike:1", "bike:2", "bike:3").Result()
if err != nil {
panic(err)
}
// STEP_START scard
res8, err := rdb.SCard(ctx, "bikes:racing:france").Result()
if err != nil {
panic(err)
}
fmt.Println(res8) // >>> 3
// STEP_END
// Output:
// 3
}
func ExampleClient_saddsmembers() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:racing:france")
// REMOVE_END
// STEP_START sadd_smembers
res9, err := rdb.SAdd(ctx, "bikes:racing:france", "bike:1", "bike:2", "bike:3").Result()
if err != nil {
panic(err)
}
fmt.Println(res9) // >>> 3
res10, err := rdb.SMembers(ctx, "bikes:racing:france").Result()
if err != nil {
panic(err)
}
fmt.Println(res10) // >>> [bike:1 bike:2 bike:3]
// STEP_END
// Output:
// 3
// [bike:1 bike:2 bike:3]
}
func ExampleClient_smismember() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:racing:france")
// REMOVE_END
_, err := rdb.SAdd(ctx, "bikes:racing:france", "bike:1", "bike:2", "bike:3").Result()
if err != nil {
panic(err)
}
// STEP_START smismember
res11, err := rdb.SIsMember(ctx, "bikes:racing:france", "bike:1").Result()
if err != nil {
panic(err)
}
fmt.Println(res11) // >>> true
res12, err := rdb.SMIsMember(ctx, "bikes:racing:france", "bike:2", "bike:3", "bike:4").Result()
if err != nil {
panic(err)
}
fmt.Println(res12) // >>> [true true false]
// STEP_END
// Output:
// true
// [true true false]
}
func ExampleClient_sdiff() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:racing:france")
rdb.Del(ctx, "bikes:racing:usa")
// REMOVE_END
// STEP_START sdiff
_, err := rdb.SAdd(ctx, "bikes:racing:france", "bike:1", "bike:2", "bike:3").Result()
if err != nil {
panic(err)
}
_, err = rdb.SAdd(ctx, "bikes:racing:usa", "bike:1", "bike:4").Result()
res13, err := rdb.SDiff(ctx, "bikes:racing:france", "bikes:racing:usa").Result()
if err != nil {
panic(err)
}
fmt.Println(res13) // >>> [bike:2 bike:3]
// STEP_END
// Output:
// [bike:2 bike:3]
}
func ExampleClient_multisets() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:racing:france")
rdb.Del(ctx, "bikes:racing:usa")
rdb.Del(ctx, "bikes:racing:italy")
// REMOVE_END
// STEP_START multisets
_, err := rdb.SAdd(ctx, "bikes:racing:france", "bike:1", "bike:2", "bike:3").Result()
if err != nil {
panic(err)
}
_, err = rdb.SAdd(ctx, "bikes:racing:usa", "bike:1", "bike:4").Result()
if err != nil {
panic(err)
}
_, err = rdb.SAdd(ctx, "bikes:racing:italy", "bike:1", "bike:2", "bike:3", "bike:4").Result()
if err != nil {
panic(err)
}
res14, err := rdb.SInter(ctx, "bikes:racing:france", "bikes:racing:usa", "bikes:racing:italy").Result()
if err != nil {
panic(err)
}
fmt.Println(res14) // >>> [bike:1]
res15, err := rdb.SUnion(ctx, "bikes:racing:france", "bikes:racing:usa", "bikes:racing:italy").Result()
if err != nil {
panic(err)
}
fmt.Println(res15) // >>> [bike:1 bike:2 bike:3 bike:4]
res16, err := rdb.SDiff(ctx, "bikes:racing:france", "bikes:racing:usa", "bikes:racing:italy").Result()
if err != nil {
panic(err)
}
fmt.Println(res16) // >>> []
res17, err := rdb.SDiff(ctx, "bikes:racing:usa", "bikes:racing:france").Result()
if err != nil {
panic(err)
}
fmt.Println(res17) // >>> [bike:4]
res18, err := rdb.SDiff(ctx, "bikes:racing:france", "bikes:racing:usa").Result()
if err != nil {
panic(err)
}
fmt.Println(res18) // >>> [bike:2 bike:3]
// STEP_END
// Output:
// [bike:1]
// [bike:1 bike:2 bike:3 bike:4]
// []
// [bike:4]
// [bike:2 bike:3]
}
func ExampleClient_srem() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:racing:france")
// REMOVE_END
// STEP_START srem
_, err := rdb.SAdd(ctx, "bikes:racing:france", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5").Result()
if err != nil {
panic(err)
}
res19, err := rdb.SRem(ctx, "bikes:racing:france", "bike:1").Result()
if err != nil {
panic(err)
}
fmt.Println(res19) // >>> 1
res20, err := rdb.SPop(ctx, "bikes:racing:france").Result()
if err != nil {
panic(err)
}
fmt.Println(res20) // >>> <random element>
res21, err := rdb.SMembers(ctx, "bikes:racing:france").Result()
if err != nil {
panic(err)
}
fmt.Println(res21) // >>> <remaining elements>
res22, err := rdb.SRandMember(ctx, "bikes:racing:france").Result()
if err != nil {
panic(err)
}
fmt.Println(res22) // >>> <random element>
// STEP_END
// Testable examples not available because the test output
// is not deterministic.
}

View File

@ -0,0 +1,437 @@
// EXAMPLE: ss_tutorial
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_zadd() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "racer_scores")
// REMOVE_END
// STEP_START zadd
res1, err := rdb.ZAdd(ctx, "racer_scores",
redis.Z{Member: "Norem", Score: 10},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> 1
res2, err := rdb.ZAdd(ctx, "racer_scores",
redis.Z{Member: "Castilla", Score: 12},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> 1
res3, err := rdb.ZAdd(ctx, "racer_scores",
redis.Z{Member: "Norem", Score: 10},
redis.Z{Member: "Sam-Bodden", Score: 8},
redis.Z{Member: "Royce", Score: 10},
redis.Z{Member: "Ford", Score: 6},
redis.Z{Member: "Prickett", Score: 14},
redis.Z{Member: "Castilla", Score: 12},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> 4
// STEP_END
// Output:
// 1
// 1
// 4
}
func ExampleClient_zrange() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "racer_scores")
// REMOVE_END
_, err := rdb.ZAdd(ctx, "racer_scores",
redis.Z{Member: "Norem", Score: 10},
redis.Z{Member: "Sam-Bodden", Score: 8},
redis.Z{Member: "Royce", Score: 10},
redis.Z{Member: "Ford", Score: 6},
redis.Z{Member: "Prickett", Score: 14},
redis.Z{Member: "Castilla", Score: 12},
).Result()
if err != nil {
panic(err)
}
// STEP_START zrange
res4, err := rdb.ZRange(ctx, "racer_scores", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(res4)
// >>> [Ford Sam-Bodden Norem Royce Castilla Prickett]
res5, err := rdb.ZRevRange(ctx, "racer_scores", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(res5)
// >>> [Prickett Castilla Royce Norem Sam-Bodden Ford]
// STEP_END
// Output:
// [Ford Sam-Bodden Norem Royce Castilla Prickett]
// [Prickett Castilla Royce Norem Sam-Bodden Ford]
}
func ExampleClient_zrangewithscores() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "racer_scores")
// REMOVE_END
_, err := rdb.ZAdd(ctx, "racer_scores",
redis.Z{Member: "Norem", Score: 10},
redis.Z{Member: "Sam-Bodden", Score: 8},
redis.Z{Member: "Royce", Score: 10},
redis.Z{Member: "Ford", Score: 6},
redis.Z{Member: "Prickett", Score: 14},
redis.Z{Member: "Castilla", Score: 12},
).Result()
if err != nil {
panic(err)
}
// STEP_START zrange_withscores
res6, err := rdb.ZRangeWithScores(ctx, "racer_scores", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(res6)
// >>> [{6 Ford} {8 Sam-Bodden} {10 Norem} {10 Royce} {12 Castilla} {14 Prickett}]
// STEP_END
// Output:
// [{6 Ford} {8 Sam-Bodden} {10 Norem} {10 Royce} {12 Castilla} {14 Prickett}]
}
func ExampleClient_zrangebyscore() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "racer_scores")
// REMOVE_END
_, err := rdb.ZAdd(ctx, "racer_scores",
redis.Z{Member: "Norem", Score: 10},
redis.Z{Member: "Sam-Bodden", Score: 8},
redis.Z{Member: "Royce", Score: 10},
redis.Z{Member: "Ford", Score: 6},
redis.Z{Member: "Prickett", Score: 14},
redis.Z{Member: "Castilla", Score: 12},
).Result()
if err != nil {
panic(err)
}
// STEP_START zrangebyscore
res7, err := rdb.ZRangeByScore(ctx, "racer_scores",
&redis.ZRangeBy{Min: "-inf", Max: "10"},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res7)
// >>> [Ford Sam-Bodden Norem Royce]
// STEP_END
// Output:
// [Ford Sam-Bodden Norem Royce]
}
func ExampleClient_zremrangebyscore() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "racer_scores")
// REMOVE_END
_, err := rdb.ZAdd(ctx, "racer_scores",
redis.Z{Member: "Norem", Score: 10},
redis.Z{Member: "Sam-Bodden", Score: 8},
redis.Z{Member: "Royce", Score: 10},
redis.Z{Member: "Ford", Score: 6},
redis.Z{Member: "Prickett", Score: 14},
redis.Z{Member: "Castilla", Score: 12},
).Result()
if err != nil {
panic(err)
}
// STEP_START zremrangebyscore
res8, err := rdb.ZRem(ctx, "racer_scores", "Castilla").Result()
if err != nil {
panic(err)
}
fmt.Println(res8) // >>> 1
res9, err := rdb.ZRemRangeByScore(ctx, "racer_scores", "-inf", "9").Result()
if err != nil {
panic(err)
}
fmt.Println(res9) // >>> 2
res10, err := rdb.ZRange(ctx, "racer_scores", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(res10)
// >>> [Norem Royce Prickett]
// STEP_END
// Output:
// 1
// 2
// [Norem Royce Prickett]
}
func ExampleClient_zrank() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "racer_scores")
// REMOVE_END
_, err := rdb.ZAdd(ctx, "racer_scores",
redis.Z{Member: "Norem", Score: 10},
redis.Z{Member: "Royce", Score: 10},
redis.Z{Member: "Prickett", Score: 14},
).Result()
if err != nil {
panic(err)
}
// STEP_START zrank
res11, err := rdb.ZRank(ctx, "racer_scores", "Norem").Result()
if err != nil {
panic(err)
}
fmt.Println(res11) // >>> 0
res12, err := rdb.ZRevRank(ctx, "racer_scores", "Norem").Result()
if err != nil {
panic(err)
}
fmt.Println(res12) // >>> 2
// STEP_END
// Output:
// 0
// 2
}
func ExampleClient_zaddlex() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "racer_scores")
// REMOVE_END
_, err := rdb.ZAdd(ctx, "racer_scores",
redis.Z{Member: "Norem", Score: 0},
redis.Z{Member: "Royce", Score: 0},
redis.Z{Member: "Prickett", Score: 0},
).Result()
// STEP_START zadd_lex
res13, err := rdb.ZAdd(ctx, "racer_scores",
redis.Z{Member: "Norem", Score: 0},
redis.Z{Member: "Sam-Bodden", Score: 0},
redis.Z{Member: "Royce", Score: 0},
redis.Z{Member: "Ford", Score: 0},
redis.Z{Member: "Prickett", Score: 0},
redis.Z{Member: "Castilla", Score: 0},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res13) // >>> 3
res14, err := rdb.ZRange(ctx, "racer_scores", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(res14)
// >>> [Castilla Ford Norem Prickett Royce Sam-Bodden]
res15, err := rdb.ZRangeByLex(ctx, "racer_scores", &redis.ZRangeBy{
Min: "[A", Max: "[L",
}).Result()
if err != nil {
panic(err)
}
fmt.Println(res15) // >>> [Castilla Ford]
// STEP_END
// Output:
// 3
// [Castilla Ford Norem Prickett Royce Sam-Bodden]
// [Castilla Ford]
}
func ExampleClient_leaderboard() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "racer_scores")
// REMOVE_END
// STEP_START leaderboard
res16, err := rdb.ZAdd(ctx, "racer_scores",
redis.Z{Member: "Wood", Score: 100},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res16) // >>> 1
res17, err := rdb.ZAdd(ctx, "racer_scores",
redis.Z{Member: "Henshaw", Score: 100},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res17) // >>> 1
res18, err := rdb.ZAdd(ctx, "racer_scores",
redis.Z{Member: "Henshaw", Score: 150},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res18) // >>> 0
res19, err := rdb.ZIncrBy(ctx, "racer_scores", 50, "Wood").Result()
if err != nil {
panic(err)
}
fmt.Println(res19) // >>> 150
res20, err := rdb.ZIncrBy(ctx, "racer_scores", 50, "Henshaw").Result()
if err != nil {
panic(err)
}
fmt.Println(res20) // >>> 200
// STEP_END
// Output:
// 1
// 1
// 0
// 150
// 200
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,173 @@
// EXAMPLE: set_tutorial
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_set_get() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bike:1")
// REMOVE_END
// STEP_START set_get
res1, err := rdb.Set(ctx, "bike:1", "Deimos", 0).Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> OK
res2, err := rdb.Get(ctx, "bike:1").Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> Deimos
// STEP_END
// Output:
// OK
// Deimos
}
func ExampleClient_setnx_xx() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Set(ctx, "bike:1", "Deimos", 0)
// REMOVE_END
// STEP_START setnx_xx
res3, err := rdb.SetNX(ctx, "bike:1", "bike", 0).Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> false
res4, err := rdb.Get(ctx, "bike:1").Result()
if err != nil {
panic(err)
}
fmt.Println(res4) // >>> Deimos
res5, err := rdb.SetXX(ctx, "bike:1", "bike", 0).Result()
if err != nil {
panic(err)
}
fmt.Println(res5) // >>> OK
// STEP_END
// Output:
// false
// Deimos
// true
}
func ExampleClient_mset() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bike:1", "bike:2", "bike:3")
// REMOVE_END
// STEP_START mset
res6, err := rdb.MSet(ctx, "bike:1", "Deimos", "bike:2", "Ares", "bike:3", "Vanth").Result()
if err != nil {
panic(err)
}
fmt.Println(res6) // >>> OK
res7, err := rdb.MGet(ctx, "bike:1", "bike:2", "bike:3").Result()
if err != nil {
panic(err)
}
fmt.Println(res7) // >>> [Deimos Ares Vanth]
// STEP_END
// Output:
// OK
// [Deimos Ares Vanth]
}
func ExampleClient_incr() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "total_crashes")
// REMOVE_END
// STEP_START incr
res8, err := rdb.Set(ctx, "total_crashes", "0", 0).Result()
if err != nil {
panic(err)
}
fmt.Println(res8) // >>> OK
res9, err := rdb.Incr(ctx, "total_crashes").Result()
if err != nil {
panic(err)
}
fmt.Println(res9) // >>> 1
res10, err := rdb.IncrBy(ctx, "total_crashes", 10).Result()
if err != nil {
panic(err)
}
fmt.Println(res10) // >>> 11
// STEP_END
// Output:
// OK
// 1
// 11
}

View File

@ -0,0 +1,251 @@
// EXAMPLE: tdigest_tutorial
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_tdigstart() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "racer_ages", "bikes:sales")
// REMOVE_END
// STEP_START tdig_start
res1, err := rdb.TDigestCreate(ctx, "bikes:sales").Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> OK
res2, err := rdb.TDigestAdd(ctx, "bikes:sales", 21).Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> OK
res3, err := rdb.TDigestAdd(ctx, "bikes:sales",
150, 95, 75, 34,
).Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> OK
// STEP_END
// Output:
// OK
// OK
// OK
}
func ExampleClient_tdigcdf() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "racer_ages", "bikes:sales")
// REMOVE_END
// STEP_START tdig_cdf
res4, err := rdb.TDigestCreate(ctx, "racer_ages").Result()
if err != nil {
panic(err)
}
fmt.Println(res4) // >>> OK
res5, err := rdb.TDigestAdd(ctx, "racer_ages",
45.88, 44.2, 58.03, 19.76, 39.84, 69.28,
50.97, 25.41, 19.27, 85.71, 42.63,
).Result()
if err != nil {
panic(err)
}
fmt.Println(res5) // >>> OK
res6, err := rdb.TDigestRank(ctx, "racer_ages", 50).Result()
if err != nil {
panic(err)
}
fmt.Println(res6) // >>> [7]
res7, err := rdb.TDigestRank(ctx, "racer_ages", 50, 40).Result()
if err != nil {
panic(err)
}
fmt.Println(res7) // >>> [7 4]
// STEP_END
// Output:
// OK
// OK
// [7]
// [7 4]
}
func ExampleClient_tdigquant() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "racer_ages")
// REMOVE_END
_, err := rdb.TDigestCreate(ctx, "racer_ages").Result()
if err != nil {
panic(err)
}
_, err = rdb.TDigestAdd(ctx, "racer_ages",
45.88, 44.2, 58.03, 19.76, 39.84, 69.28,
50.97, 25.41, 19.27, 85.71, 42.63,
).Result()
if err != nil {
panic(err)
}
// STEP_START tdig_quant
res8, err := rdb.TDigestQuantile(ctx, "racer_ages", 0.5).Result()
if err != nil {
panic(err)
}
fmt.Println(res8) // >>> [44.2]
res9, err := rdb.TDigestByRank(ctx, "racer_ages", 4).Result()
if err != nil {
panic(err)
}
fmt.Println(res9) // >>> [42.63]
// STEP_END
// Output:
// [44.2]
// [42.63]
}
func ExampleClient_tdigmin() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "racer_ages")
// REMOVE_END
_, err := rdb.TDigestCreate(ctx, "racer_ages").Result()
if err != nil {
panic(err)
}
_, err = rdb.TDigestAdd(ctx, "racer_ages",
45.88, 44.2, 58.03, 19.76, 39.84, 69.28,
50.97, 25.41, 19.27, 85.71, 42.63,
).Result()
if err != nil {
panic(err)
}
// STEP_START tdig_min
res10, err := rdb.TDigestMin(ctx, "racer_ages").Result()
if err != nil {
panic(err)
}
fmt.Println(res10) // >>> 19.27
res11, err := rdb.TDigestMax(ctx, "racer_ages").Result()
if err != nil {
panic(err)
}
fmt.Println(res11) // >>> 85.71
// STEP_END
// Output:
// 19.27
// 85.71
}
func ExampleClient_tdigreset() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "racer_ages")
// REMOVE_END
_, err := rdb.TDigestCreate(ctx, "racer_ages").Result()
if err != nil {
panic(err)
}
// STEP_START tdig_reset
res12, err := rdb.TDigestReset(ctx, "racer_ages").Result()
if err != nil {
panic(err)
}
fmt.Println(res12) // >>> OK
// STEP_END
// Output:
// OK
}

View File

@ -0,0 +1,75 @@
// EXAMPLE: topk_tutorial
// HIDE_START
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// HIDE_END
func ExampleClient_topk() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
// REMOVE_START
rdb.Del(ctx, "bikes:keywords")
// REMOVE_END
// STEP_START topk
res1, err := rdb.TopKReserve(ctx, "bikes:keywords", 5).Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> OK
res2, err := rdb.TopKAdd(ctx, "bikes:keywords",
"store",
"seat",
"handlebars",
"handles",
"pedals",
"tires",
"store",
"seat",
).Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> [ handlebars ]
res3, err := rdb.TopKList(ctx, "bikes:keywords").Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // [store seat pedals tires handles]
res4, err := rdb.TopKQuery(ctx, "bikes:keywords", "store", "handlebars").Result()
if err != nil {
panic(err)
}
fmt.Println(res4) // [true false]
// STEP_END
// Output:
// OK
// [ handlebars ]
// [store seat pedals tires handles]
// [true false]
}

View File

@ -5,7 +5,7 @@ go 1.18
replace github.com/redis/go-redis/v9 => ../..
require (
github.com/redis/go-redis/v9 v9.6.1
github.com/redis/go-redis/v9 v9.6.2
go.uber.org/zap v1.24.0
)

View File

@ -4,7 +4,7 @@ go 1.18
replace github.com/redis/go-redis/v9 => ../..
require github.com/redis/go-redis/v9 v9.6.1
require github.com/redis/go-redis/v9 v9.6.2
require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect

View File

@ -4,7 +4,7 @@ go 1.18
replace github.com/redis/go-redis/v9 => ../..
require github.com/redis/go-redis/v9 v9.6.1
require github.com/redis/go-redis/v9 v9.6.2
require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect

View File

@ -9,8 +9,8 @@ replace github.com/redis/go-redis/extra/redisotel/v9 => ../../extra/redisotel
replace github.com/redis/go-redis/extra/rediscmd/v9 => ../../extra/rediscmd
require (
github.com/redis/go-redis/extra/redisotel/v9 v9.6.1
github.com/redis/go-redis/v9 v9.6.1
github.com/redis/go-redis/extra/redisotel/v9 v9.6.2
github.com/redis/go-redis/v9 v9.6.2
github.com/uptrace/uptrace-go v1.21.0
go.opentelemetry.io/otel v1.22.0
)
@ -23,7 +23,7 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
github.com/redis/go-redis/extra/rediscmd/v9 v9.6.1 // indirect
github.com/redis/go-redis/extra/rediscmd/v9 v9.6.2 // indirect
go.opentelemetry.io/contrib/instrumentation/runtime v0.46.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect

View File

@ -4,7 +4,7 @@ go 1.18
replace github.com/redis/go-redis/v9 => ../..
require github.com/redis/go-redis/v9 v9.6.1
require github.com/redis/go-redis/v9 v9.6.2
require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect

View File

@ -6,7 +6,7 @@ replace github.com/redis/go-redis/v9 => ../..
require (
github.com/davecgh/go-spew v1.1.1
github.com/redis/go-redis/v9 v9.6.1
github.com/redis/go-redis/v9 v9.6.2
)
require (

View File

@ -7,8 +7,8 @@ replace github.com/redis/go-redis/v9 => ../..
replace github.com/redis/go-redis/extra/rediscmd/v9 => ../rediscmd
require (
github.com/redis/go-redis/extra/rediscmd/v9 v9.6.1
github.com/redis/go-redis/v9 v9.6.1
github.com/redis/go-redis/extra/rediscmd/v9 v9.6.2
github.com/redis/go-redis/v9 v9.6.2
go.opencensus.io v0.24.0
)
@ -17,3 +17,7 @@ require (
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
)
retract (
v9.5.3 // This version was accidentally released.
)

View File

@ -7,10 +7,14 @@ replace github.com/redis/go-redis/v9 => ../..
require (
github.com/bsm/ginkgo/v2 v2.12.0
github.com/bsm/gomega v1.27.10
github.com/redis/go-redis/v9 v9.6.1
github.com/redis/go-redis/v9 v9.6.2
)
require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
)
retract (
v9.5.3 // This version was accidentally released.
)

View File

@ -7,8 +7,8 @@ replace github.com/redis/go-redis/v9 => ../..
replace github.com/redis/go-redis/extra/rediscmd/v9 => ../rediscmd
require (
github.com/redis/go-redis/extra/rediscmd/v9 v9.6.1
github.com/redis/go-redis/v9 v9.6.1
github.com/redis/go-redis/extra/rediscmd/v9 v9.6.2
github.com/redis/go-redis/v9 v9.6.2
go.opentelemetry.io/otel v1.22.0
go.opentelemetry.io/otel/metric v1.22.0
go.opentelemetry.io/otel/sdk v1.22.0
@ -22,3 +22,7 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect
golang.org/x/sys v0.16.0 // indirect
)
retract (
v9.5.3 // This version was accidentally released.
)

View File

@ -6,7 +6,7 @@ replace github.com/redis/go-redis/v9 => ../..
require (
github.com/prometheus/client_golang v1.14.0
github.com/redis/go-redis/v9 v9.6.1
github.com/redis/go-redis/v9 v9.6.2
)
require (
@ -21,3 +21,7 @@ require (
golang.org/x/sys v0.4.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
)
retract (
v9.5.3 // This version was accidentally released.
)

2
go.mod
View File

@ -10,6 +10,6 @@ require (
)
retract (
v9.5.3 // This version was accidentally released. Please use version 9.6.0 instead.
v9.5.4 // This version was accidentally released. Please use version 9.6.0 instead.
v9.5.3 // This version was accidentally released. Please use version 9.6.0 instead.
)

View File

@ -3,7 +3,6 @@
package pool
import (
"crypto/tls"
"errors"
"io"
"net"
@ -17,10 +16,6 @@ func connCheck(conn net.Conn) error {
// Reset previous timeout.
_ = conn.SetDeadline(time.Time{})
// Check if tls.Conn.
if c, ok := conn.(*tls.Conn); ok {
conn = c.NetConn()
}
sysConn, ok := conn.(syscall.Conn)
if !ok {
return nil

View File

@ -3,7 +3,6 @@
package pool
import (
"crypto/tls"
"net"
"net/http/httptest"
"time"
@ -15,17 +14,12 @@ import (
var _ = Describe("tests conn_check with real conns", func() {
var ts *httptest.Server
var conn net.Conn
var tlsConn *tls.Conn
var err error
BeforeEach(func() {
ts = httptest.NewServer(nil)
conn, err = net.DialTimeout(ts.Listener.Addr().Network(), ts.Listener.Addr().String(), time.Second)
Expect(err).NotTo(HaveOccurred())
tlsTestServer := httptest.NewUnstartedServer(nil)
tlsTestServer.StartTLS()
tlsConn, err = tls.DialWithDialer(&net.Dialer{Timeout: time.Second}, tlsTestServer.Listener.Addr().Network(), tlsTestServer.Listener.Addr().String(), &tls.Config{InsecureSkipVerify: true})
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
@ -39,23 +33,11 @@ var _ = Describe("tests conn_check with real conns", func() {
Expect(connCheck(conn)).To(HaveOccurred())
})
It("good tls conn check", func() {
Expect(connCheck(tlsConn)).NotTo(HaveOccurred())
Expect(tlsConn.Close()).NotTo(HaveOccurred())
Expect(connCheck(tlsConn)).To(HaveOccurred())
})
It("bad conn check", func() {
Expect(conn.Close()).NotTo(HaveOccurred())
Expect(connCheck(conn)).To(HaveOccurred())
})
It("bad tls conn check", func() {
Expect(tlsConn.Close()).NotTo(HaveOccurred())
Expect(connCheck(tlsConn)).To(HaveOccurred())
})
It("check conn deadline", func() {
Expect(conn.SetDeadline(time.Now())).NotTo(HaveOccurred())
time.Sleep(time.Millisecond * 10)

View File

@ -60,7 +60,7 @@ type JSONArrTrimArgs struct {
type JSONCmd struct {
baseCmd
val string
expanded []interface{}
expanded interface{}
}
var _ Cmder = (*JSONCmd)(nil)
@ -100,11 +100,11 @@ func (cmd *JSONCmd) Result() (string, error) {
return cmd.Val(), cmd.Err()
}
func (cmd JSONCmd) Expanded() (interface{}, error) {
func (cmd *JSONCmd) Expanded() (interface{}, error) {
if len(cmd.val) != 0 && cmd.expanded == nil {
err := json.Unmarshal([]byte(cmd.val), &cmd.expanded)
if err != nil {
return "", err
return nil, err
}
}
@ -494,7 +494,7 @@ func (c cmdable) JSONMSet(ctx context.Context, params ...interface{}) *StatusCmd
}
// JSONNumIncrBy increments the number value stored at the specified path by the provided number.
// For more information, see https://redis.io/commands/json.numincreby
// For more information, see https://redis.io/docs/latest/commands/json.numincrby/
func (c cmdable) JSONNumIncrBy(ctx context.Context, key, path string, value float64) *JSONCmd {
args := []interface{}{"JSON.NUMINCRBY", key, path, value}
cmd := newJSONCmd(ctx, args...)

File diff suppressed because it is too large Load Diff

View File

@ -153,6 +153,9 @@ type Options struct {
// Add suffix to client name. Default is empty.
IdentitySuffix string
// Enable Unstable mode for Redis Search module with RESP3.
UnstableResp3 bool
}
func (opt *Options) init() {

View File

@ -319,37 +319,69 @@ func (cmd *BFInfoCmd) Result() (BFInfo, error) {
}
func (cmd *BFInfoCmd) readReply(rd *proto.Reader) (err error) {
n, err := rd.ReadMapLen()
result := BFInfo{}
// Create a mapping from key names to pointers of struct fields
respMapping := map[string]*int64{
"Capacity": &result.Capacity,
"CAPACITY": &result.Capacity,
"Size": &result.Size,
"SIZE": &result.Size,
"Number of filters": &result.Filters,
"FILTERS": &result.Filters,
"Number of items inserted": &result.ItemsInserted,
"ITEMS": &result.ItemsInserted,
"Expansion rate": &result.ExpansionRate,
"EXPANSION": &result.ExpansionRate,
}
// Helper function to read and assign a value based on the key
readAndAssignValue := func(key string) error {
fieldPtr, exists := respMapping[key]
if !exists {
return fmt.Errorf("redis: BLOOM.INFO unexpected key %s", key)
}
// Read the integer and assign to the field via pointer dereferencing
val, err := rd.ReadInt()
if err != nil {
return err
}
*fieldPtr = val
return nil
}
readType, err := rd.PeekReplyType()
if err != nil {
return err
}
var key string
var result BFInfo
for f := 0; f < n; f++ {
key, err = rd.ReadString()
if len(cmd.args) > 2 && readType == proto.RespArray {
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
switch key {
case "Capacity":
result.Capacity, err = rd.ReadInt()
case "Size":
result.Size, err = rd.ReadInt()
case "Number of filters":
result.Filters, err = rd.ReadInt()
case "Number of items inserted":
result.ItemsInserted, err = rd.ReadInt()
case "Expansion rate":
result.ExpansionRate, err = rd.ReadInt()
default:
return fmt.Errorf("redis: BLOOM.INFO unexpected key %s", key)
if key, ok := cmd.args[2].(string); ok && n == 1 {
if err := readAndAssignValue(key); err != nil {
return err
}
} else {
return fmt.Errorf("redis: BLOOM.INFO invalid argument key type")
}
} else {
n, err := rd.ReadMapLen()
if err != nil {
return err
}
for i := 0; i < n; i++ {
key, err := rd.ReadString()
if err != nil {
return err
}
if err := readAndAssignValue(key); err != nil {
return err
}
}
}
cmd.val = result

File diff suppressed because it is too large Load Diff

View File

@ -410,6 +410,19 @@ func (c *baseClient) process(ctx context.Context, cmd Cmder) error {
return lastErr
}
func (c *baseClient) assertUnstableCommand(cmd Cmder) bool {
switch cmd.(type) {
case *AggregateCmd, *FTInfoCmd, *FTSpellCheckCmd, *FTSearchCmd, *FTSynDumpCmd:
if c.opt.UnstableResp3 {
return true
} else {
panic("RESP3 responses for this command are disabled because they may still change. Please set the flag UnstableResp3 . See the [README](https://github.com/redis/go-redis/blob/master/README.md) and the release notes for guidance.")
}
default:
return false
}
}
func (c *baseClient) _process(ctx context.Context, cmd Cmder, attempt int) (bool, error) {
if attempt > 0 {
if err := internal.Sleep(ctx, c.retryBackoff(attempt)); err != nil {
@ -425,8 +438,12 @@ func (c *baseClient) _process(ctx context.Context, cmd Cmder, attempt int) (bool
atomic.StoreUint32(&retryTimeout, 1)
return err
}
if err := cn.WithReader(c.context(ctx), c.cmdTimeout(cmd), cmd.readReply); err != nil {
readReplyFunc := cmd.readReply
// Apply unstable RESP3 search module.
if c.opt.Protocol != 2 && c.assertUnstableCommand(cmd) {
readReplyFunc = cmd.readRawReply
}
if err := cn.WithReader(c.context(ctx), c.cmdTimeout(cmd), readReplyFunc); err != nil {
if cmd.readTimeout() == nil {
atomic.StoreUint32(&retryTimeout, 1)
} else {

View File

@ -100,6 +100,7 @@ type RingOptions struct {
DisableIndentity bool
IdentitySuffix string
UnstableResp3 bool
}
func (opt *RingOptions) init() {
@ -168,6 +169,7 @@ func (opt *RingOptions) clientOptions() *Options {
DisableIndentity: opt.DisableIndentity,
IdentitySuffix: opt.IdentitySuffix,
UnstableResp3: opt.UnstableResp3,
}
}

View File

@ -16,7 +16,7 @@ type SearchCmdable interface {
FTAliasAdd(ctx context.Context, index string, alias string) *StatusCmd
FTAliasDel(ctx context.Context, alias string) *StatusCmd
FTAliasUpdate(ctx context.Context, index string, alias string) *StatusCmd
FTAlter(ctx context.Context, index string, skipInitalScan bool, definition []interface{}) *StatusCmd
FTAlter(ctx context.Context, index string, skipInitialScan bool, definition []interface{}) *StatusCmd
FTConfigGet(ctx context.Context, option string) *MapMapStringInterfaceCmd
FTConfigSet(ctx context.Context, option string, value interface{}) *StatusCmd
FTCreate(ctx context.Context, index string, options *FTCreateOptions, schema ...*FieldSchema) *StatusCmd
@ -57,7 +57,7 @@ type FTCreateOptions struct {
NoFields bool
NoFreqs bool
StopWords []interface{}
SkipInitalScan bool
SkipInitialScan bool
}
type FieldSchema struct {
@ -70,7 +70,7 @@ type FieldSchema struct {
NoIndex bool
PhoneticMatcher string
Weight float64
Seperator string
Separator string
CaseSensitive bool
WithSuffixtrie bool
VectorArgs *FTVectorArgs
@ -285,7 +285,7 @@ type FTSearchSortBy struct {
type FTSearchOptions struct {
NoContent bool
Verbatim bool
NoStopWrods bool
NoStopWords bool
WithScores bool
WithPayloads bool
WithSortKeys bool
@ -638,6 +638,14 @@ func (cmd *AggregateCmd) Result() (*FTAggregateResult, error) {
return cmd.val, cmd.err
}
func (cmd *AggregateCmd) RawVal() interface{} {
return cmd.rawVal
}
func (cmd *AggregateCmd) RawResult() (interface{}, error) {
return cmd.rawVal, cmd.err
}
func (cmd *AggregateCmd) String() string {
return cmdString(cmd, cmd.val)
}
@ -800,13 +808,13 @@ func (c cmdable) FTAliasUpdate(ctx context.Context, index string, alias string)
}
// FTAlter - Alters the definition of an existing index.
// The 'index' parameter specifies the index to alter, and the 'skipInitalScan' parameter specifies whether to skip the initial scan.
// The 'index' parameter specifies the index to alter, and the 'skipInitialScan' parameter specifies whether to skip the initial scan.
// The 'definition' parameter specifies the new definition for the index.
// For more information, please refer to the Redis documentation:
// [FT.ALTER]: (https://redis.io/commands/ft.alter/)
func (c cmdable) FTAlter(ctx context.Context, index string, skipInitalScan bool, definition []interface{}) *StatusCmd {
func (c cmdable) FTAlter(ctx context.Context, index string, skipInitialScan bool, definition []interface{}) *StatusCmd {
args := []interface{}{"FT.ALTER", index}
if skipInitalScan {
if skipInitialScan {
args = append(args, "SKIPINITIALSCAN")
}
args = append(args, "SCHEMA", "ADD")
@ -899,7 +907,7 @@ func (c cmdable) FTCreate(ctx context.Context, index string, options *FTCreateOp
args = append(args, "STOPWORDS", len(options.StopWords))
args = append(args, options.StopWords...)
}
if options.SkipInitalScan {
if options.SkipInitialScan {
args = append(args, "SKIPINITIALSCAN")
}
}
@ -995,8 +1003,8 @@ func (c cmdable) FTCreate(ctx context.Context, index string, options *FTCreateOp
if schema.Weight > 0 {
args = append(args, "WEIGHT", schema.Weight)
}
if schema.Seperator != "" {
args = append(args, "SEPERATOR", schema.Seperator)
if schema.Separator != "" {
args = append(args, "SEPARATOR", schema.Separator)
}
if schema.CaseSensitive {
args = append(args, "CASESENSITIVE")
@ -1337,6 +1345,13 @@ func (cmd *FTInfoCmd) Val() FTInfoResult {
return cmd.val
}
func (cmd *FTInfoCmd) RawVal() interface{} {
return cmd.rawVal
}
func (cmd *FTInfoCmd) RawResult() (interface{}, error) {
return cmd.rawVal, cmd.err
}
func (cmd *FTInfoCmd) readReply(rd *proto.Reader) (err error) {
n, err := rd.ReadMapLen()
if err != nil {
@ -1447,6 +1462,14 @@ func (cmd *FTSpellCheckCmd) Val() []SpellCheckResult {
return cmd.val
}
func (cmd *FTSpellCheckCmd) RawVal() interface{} {
return cmd.rawVal
}
func (cmd *FTSpellCheckCmd) RawResult() (interface{}, error) {
return cmd.rawVal, cmd.err
}
func (cmd *FTSpellCheckCmd) readReply(rd *proto.Reader) (err error) {
data, err := rd.ReadSlice()
if err != nil {
@ -1628,6 +1651,14 @@ func (cmd *FTSearchCmd) Val() FTSearchResult {
return cmd.val
}
func (cmd *FTSearchCmd) RawVal() interface{} {
return cmd.rawVal
}
func (cmd *FTSearchCmd) RawResult() (interface{}, error) {
return cmd.rawVal, cmd.err
}
func (cmd *FTSearchCmd) readReply(rd *proto.Reader) (err error) {
data, err := rd.ReadSlice()
if err != nil {
@ -1663,7 +1694,7 @@ func FTSearchQuery(query string, options *FTSearchOptions) SearchQuery {
if options.Verbatim {
queryArgs = append(queryArgs, "VERBATIM")
}
if options.NoStopWrods {
if options.NoStopWords {
queryArgs = append(queryArgs, "NOSTOPWORDS")
}
if options.WithScores {
@ -1777,7 +1808,7 @@ func (c cmdable) FTSearchWithArgs(ctx context.Context, index string, query strin
if options.Verbatim {
args = append(args, "VERBATIM")
}
if options.NoStopWrods {
if options.NoStopWords {
args = append(args, "NOSTOPWORDS")
}
if options.WithScores {
@ -1904,6 +1935,14 @@ func (cmd *FTSynDumpCmd) Result() ([]FTSynDumpResult, error) {
return cmd.val, cmd.err
}
func (cmd *FTSynDumpCmd) RawVal() interface{} {
return cmd.rawVal
}
func (cmd *FTSynDumpCmd) RawResult() (interface{}, error) {
return cmd.rawVal, cmd.err
}
func (cmd *FTSynDumpCmd) readReply(rd *proto.Reader) error {
termSynonymPairs, err := rd.ReadSlice()
if err != nil {

View File

@ -18,11 +18,13 @@ func WaitForIndexing(c *redis.Client, index string) {
return
}
time.Sleep(100 * time.Millisecond)
} else {
return
}
}
}
var _ = Describe("RediSearch commands", Label("search"), func() {
var _ = Describe("RediSearch commands Resp 2", Label("search"), func() {
ctx := context.TODO()
var client *redis.Client
@ -635,11 +637,11 @@ var _ = Describe("RediSearch commands", Label("search"), func() {
})
It("should FTSearch SkipInitalScan", Label("search", "ftsearch"), func() {
It("should FTSearch SkipInitialScan", Label("search", "ftsearch"), func() {
client.HSet(ctx, "doc1", "foo", "bar")
text1 := &redis.FieldSchema{FieldName: "foo", FieldType: redis.SearchFieldTypeText}
val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{SkipInitalScan: true}, text1).Result()
val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{SkipInitialScan: true}, text1).Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(BeEquivalentTo("OK"))
WaitForIndexing(client, "idx1")
@ -1415,3 +1417,189 @@ func _assert_geosearch_result(result *redis.FTSearchResult, expectedDocIDs []str
// Expect(results0["id"]).To(BeEquivalentTo("a"))
// Expect(results0["extra_attributes"].(map[interface{}]interface{})["__v_score"]).To(BeEquivalentTo("0"))
// })
var _ = Describe("RediSearch commands Resp 3", Label("search"), func() {
ctx := context.TODO()
var client *redis.Client
var client2 *redis.Client
BeforeEach(func() {
client = redis.NewClient(&redis.Options{Addr: ":6379", Protocol: 3, UnstableResp3: true})
client2 = redis.NewClient(&redis.Options{Addr: ":6379", Protocol: 3})
Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred())
})
AfterEach(func() {
Expect(client.Close()).NotTo(HaveOccurred())
})
It("should handle FTAggregate with Unstable RESP3 Search Module and without stability", Label("search", "ftcreate", "ftaggregate"), func() {
text1 := &redis.FieldSchema{FieldName: "PrimaryKey", FieldType: redis.SearchFieldTypeText, Sortable: true}
num1 := &redis.FieldSchema{FieldName: "CreatedDateTimeUTC", FieldType: redis.SearchFieldTypeNumeric, Sortable: true}
val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{}, text1, num1).Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(BeEquivalentTo("OK"))
WaitForIndexing(client, "idx1")
client.HSet(ctx, "doc1", "PrimaryKey", "9::362330", "CreatedDateTimeUTC", "637387878524969984")
client.HSet(ctx, "doc2", "PrimaryKey", "9::362329", "CreatedDateTimeUTC", "637387875859270016")
options := &redis.FTAggregateOptions{Apply: []redis.FTAggregateApply{{Field: "@CreatedDateTimeUTC * 10", As: "CreatedDateTimeUTC"}}}
res, err := client.FTAggregateWithArgs(ctx, "idx1", "*", options).RawResult()
results := res.(map[interface{}]interface{})["results"].([]interface{})
Expect(results[0].(map[interface{}]interface{})["extra_attributes"].(map[interface{}]interface{})["CreatedDateTimeUTC"]).
To(Or(BeEquivalentTo("6373878785249699840"), BeEquivalentTo("6373878758592700416")))
Expect(results[1].(map[interface{}]interface{})["extra_attributes"].(map[interface{}]interface{})["CreatedDateTimeUTC"]).
To(Or(BeEquivalentTo("6373878785249699840"), BeEquivalentTo("6373878758592700416")))
rawVal := client.FTAggregateWithArgs(ctx, "idx1", "*", options).RawVal()
rawValResults := rawVal.(map[interface{}]interface{})["results"].([]interface{})
Expect(err).NotTo(HaveOccurred())
Expect(rawValResults[0]).To(Or(BeEquivalentTo(results[0]), BeEquivalentTo(results[1])))
Expect(rawValResults[1]).To(Or(BeEquivalentTo(results[0]), BeEquivalentTo(results[1])))
// Test with UnstableResp3 false
Expect(func() {
options = &redis.FTAggregateOptions{Apply: []redis.FTAggregateApply{{Field: "@CreatedDateTimeUTC * 10", As: "CreatedDateTimeUTC"}}}
rawRes, _ := client2.FTAggregateWithArgs(ctx, "idx1", "*", options).RawResult()
rawVal = client2.FTAggregateWithArgs(ctx, "idx1", "*", options).RawVal()
Expect(rawRes).To(BeNil())
Expect(rawVal).To(BeNil())
}).Should(Panic())
})
It("should handle FTInfo with Unstable RESP3 Search Module and without stability", Label("search", "ftcreate", "ftinfo"), func() {
val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{}, &redis.FieldSchema{FieldName: "txt", FieldType: redis.SearchFieldTypeText, Sortable: true, NoStem: true}).Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(BeEquivalentTo("OK"))
WaitForIndexing(client, "idx1")
resInfo, err := client.FTInfo(ctx, "idx1").RawResult()
Expect(err).NotTo(HaveOccurred())
attributes := resInfo.(map[interface{}]interface{})["attributes"].([]interface{})
flags := attributes[0].(map[interface{}]interface{})["flags"].([]interface{})
Expect(flags).To(ConsistOf("SORTABLE", "NOSTEM"))
valInfo := client.FTInfo(ctx, "idx1").RawVal()
attributes = valInfo.(map[interface{}]interface{})["attributes"].([]interface{})
flags = attributes[0].(map[interface{}]interface{})["flags"].([]interface{})
Expect(flags).To(ConsistOf("SORTABLE", "NOSTEM"))
// Test with UnstableResp3 false
Expect(func() {
rawResInfo, _ := client2.FTInfo(ctx, "idx1").RawResult()
rawValInfo := client2.FTInfo(ctx, "idx1").RawVal()
Expect(rawResInfo).To(BeNil())
Expect(rawValInfo).To(BeNil())
}).Should(Panic())
})
It("should handle FTSpellCheck with Unstable RESP3 Search Module and without stability", Label("search", "ftcreate", "ftspellcheck"), func() {
text1 := &redis.FieldSchema{FieldName: "f1", FieldType: redis.SearchFieldTypeText}
text2 := &redis.FieldSchema{FieldName: "f2", FieldType: redis.SearchFieldTypeText}
val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{}, text1, text2).Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(BeEquivalentTo("OK"))
WaitForIndexing(client, "idx1")
client.HSet(ctx, "doc1", "f1", "some valid content", "f2", "this is sample text")
client.HSet(ctx, "doc2", "f1", "very important", "f2", "lorem ipsum")
resSpellCheck, err := client.FTSpellCheck(ctx, "idx1", "impornant").RawResult()
valSpellCheck := client.FTSpellCheck(ctx, "idx1", "impornant").RawVal()
Expect(err).NotTo(HaveOccurred())
Expect(valSpellCheck).To(BeEquivalentTo(resSpellCheck))
results := resSpellCheck.(map[interface{}]interface{})["results"].(map[interface{}]interface{})
Expect(results["impornant"].([]interface{})[0].(map[interface{}]interface{})["important"]).To(BeEquivalentTo(0.5))
// Test with UnstableResp3 false
Expect(func() {
rawResSpellCheck, _ := client2.FTSpellCheck(ctx, "idx1", "impornant").RawResult()
rawValSpellCheck := client2.FTSpellCheck(ctx, "idx1", "impornant").RawVal()
Expect(rawResSpellCheck).To(BeNil())
Expect(rawValSpellCheck).To(BeNil())
}).Should(Panic())
})
It("should handle FTSearch with Unstable RESP3 Search Module and without stability", Label("search", "ftcreate", "ftsearch"), func() {
val, err := client.FTCreate(ctx, "txt", &redis.FTCreateOptions{StopWords: []interface{}{"foo", "bar", "baz"}}, &redis.FieldSchema{FieldName: "txt", FieldType: redis.SearchFieldTypeText}).Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(BeEquivalentTo("OK"))
WaitForIndexing(client, "txt")
client.HSet(ctx, "doc1", "txt", "foo baz")
client.HSet(ctx, "doc2", "txt", "hello world")
res1, err := client.FTSearchWithArgs(ctx, "txt", "foo bar", &redis.FTSearchOptions{NoContent: true}).RawResult()
val1 := client.FTSearchWithArgs(ctx, "txt", "foo bar", &redis.FTSearchOptions{NoContent: true}).RawVal()
Expect(err).NotTo(HaveOccurred())
Expect(val1).To(BeEquivalentTo(res1))
totalResults := res1.(map[interface{}]interface{})["total_results"]
Expect(totalResults).To(BeEquivalentTo(int64(0)))
res2, err := client.FTSearchWithArgs(ctx, "txt", "foo bar hello world", &redis.FTSearchOptions{NoContent: true}).RawResult()
Expect(err).NotTo(HaveOccurred())
totalResults2 := res2.(map[interface{}]interface{})["total_results"]
Expect(totalResults2).To(BeEquivalentTo(int64(1)))
// Test with UnstableResp3 false
Expect(func() {
rawRes2, _ := client2.FTSearchWithArgs(ctx, "txt", "foo bar hello world", &redis.FTSearchOptions{NoContent: true}).RawResult()
rawVal2 := client2.FTSearchWithArgs(ctx, "txt", "foo bar hello world", &redis.FTSearchOptions{NoContent: true}).RawVal()
Expect(rawRes2).To(BeNil())
Expect(rawVal2).To(BeNil())
}).Should(Panic())
})
It("should handle FTSynDump with Unstable RESP3 Search Module and without stability", Label("search", "ftsyndump"), func() {
text1 := &redis.FieldSchema{FieldName: "title", FieldType: redis.SearchFieldTypeText}
text2 := &redis.FieldSchema{FieldName: "body", FieldType: redis.SearchFieldTypeText}
val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{OnHash: true}, text1, text2).Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(BeEquivalentTo("OK"))
WaitForIndexing(client, "idx1")
resSynUpdate, err := client.FTSynUpdate(ctx, "idx1", "id1", []interface{}{"boy", "child", "offspring"}).Result()
Expect(err).NotTo(HaveOccurred())
Expect(resSynUpdate).To(BeEquivalentTo("OK"))
resSynUpdate, err = client.FTSynUpdate(ctx, "idx1", "id1", []interface{}{"baby", "child"}).Result()
Expect(err).NotTo(HaveOccurred())
Expect(resSynUpdate).To(BeEquivalentTo("OK"))
resSynUpdate, err = client.FTSynUpdate(ctx, "idx1", "id1", []interface{}{"tree", "wood"}).Result()
Expect(err).NotTo(HaveOccurred())
Expect(resSynUpdate).To(BeEquivalentTo("OK"))
resSynDump, err := client.FTSynDump(ctx, "idx1").RawResult()
valSynDump := client.FTSynDump(ctx, "idx1").RawVal()
Expect(err).NotTo(HaveOccurred())
Expect(valSynDump).To(BeEquivalentTo(resSynDump))
Expect(resSynDump.(map[interface{}]interface{})["baby"]).To(BeEquivalentTo([]interface{}{"id1"}))
// Test with UnstableResp3 false
Expect(func() {
rawResSynDump, _ := client2.FTSynDump(ctx, "idx1").RawResult()
rawValSynDump := client2.FTSynDump(ctx, "idx1").RawVal()
Expect(rawResSynDump).To(BeNil())
Expect(rawValSynDump).To(BeNil())
}).Should(Panic())
})
It("should test not affected Resp 3 Search method - FTExplain", Label("search", "ftexplain"), func() {
text1 := &redis.FieldSchema{FieldName: "f1", FieldType: redis.SearchFieldTypeText}
text2 := &redis.FieldSchema{FieldName: "f2", FieldType: redis.SearchFieldTypeText}
text3 := &redis.FieldSchema{FieldName: "f3", FieldType: redis.SearchFieldTypeText}
val, err := client.FTCreate(ctx, "txt", &redis.FTCreateOptions{}, text1, text2, text3).Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(BeEquivalentTo("OK"))
WaitForIndexing(client, "txt")
res1, err := client.FTExplain(ctx, "txt", "@f3:f3_val @f2:f2_val @f1:f1_val").Result()
Expect(err).NotTo(HaveOccurred())
Expect(res1).ToNot(BeEmpty())
// Test with UnstableResp3 false
Expect(func() {
res2, err := client2.FTExplain(ctx, "txt", "@f3:f3_val @f2:f2_val @f1:f1_val").Result()
Expect(err).NotTo(HaveOccurred())
Expect(res2).ToNot(BeEmpty())
}).ShouldNot(Panic())
})
})

View File

@ -82,6 +82,7 @@ type FailoverOptions struct {
DisableIndentity bool
IdentitySuffix string
UnstableResp3 bool
}
func (opt *FailoverOptions) clientOptions() *Options {
@ -119,6 +120,7 @@ func (opt *FailoverOptions) clientOptions() *Options {
DisableIndentity: opt.DisableIndentity,
IdentitySuffix: opt.IdentitySuffix,
UnstableResp3: opt.UnstableResp3,
}
}
@ -156,6 +158,7 @@ func (opt *FailoverOptions) sentinelOptions(addr string) *Options {
DisableIndentity: opt.DisableIndentity,
IdentitySuffix: opt.IdentitySuffix,
UnstableResp3: opt.UnstableResp3,
}
}

File diff suppressed because it is too large Load Diff

View File

@ -68,6 +68,7 @@ type UniversalOptions struct {
DisableIndentity bool
IdentitySuffix string
UnstableResp3 bool
}
// Cluster returns cluster options created from the universal options.
@ -160,6 +161,7 @@ func (o *UniversalOptions) Failover() *FailoverOptions {
DisableIndentity: o.DisableIndentity,
IdentitySuffix: o.IdentitySuffix,
UnstableResp3: o.UnstableResp3,
}
}
@ -203,6 +205,7 @@ func (o *UniversalOptions) Simple() *Options {
DisableIndentity: o.DisableIndentity,
IdentitySuffix: o.IdentitySuffix,
UnstableResp3: o.UnstableResp3,
}
}

View File

@ -2,5 +2,5 @@ package redis
// Version is the current release version.
func Version() string {
return "9.6.1"
return "9.6.2"
}