diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bcaee7c7..7228a4a0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -112,3 +112,7 @@ The core team regularly looks at pull requests. We will provide feedback as soon as possible. After receiving our feedback, please respond within two weeks. After that time, we may close your PR if it isn't showing any activity. + +## Support + +Maintainers can provide limited support to contributors on discord: https://discord.gg/W4txy5AeKM diff --git a/README.md b/README.md index 6c42c0fc..4487c6e9 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,14 @@ [![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/) +[![Go Report Card](https://goreportcard.com/badge/github.com/redis/go-redis/v9)](https://goreportcard.com/report/github.com/redis/go-redis/v9) [![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) + +[![Discord](https://img.shields.io/discord/697882427875393627.svg?style=social&logo=discord)](https://discord.gg/W4txy5AeKM) +[![Twitch](https://img.shields.io/twitch/status/redisinc?style=social)](https://www.twitch.tv/redisinc) +[![YouTube](https://img.shields.io/youtube/channel/views/UCD78lHSwYqMlyetR0_P4Vig?style=social)](https://www.youtube.com/redisinc) +[![Twitter](https://img.shields.io/twitter/follow/redisinc?style=social)](https://twitter.com/redisinc) +[![Stack Exchange questions](https://img.shields.io/stackexchange/stackoverflow/t/go-redis?style=social&logo=stackoverflow&label=Stackoverflow)](https://stackoverflow.com/questions/tagged/go-redis) > go-redis is the official Redis client library for the Go programming language. It offers a straightforward interface for interacting with Redis servers. @@ -44,7 +50,7 @@ in the `go.mod` to `go 1.24` in one of the next releases. ## Resources - [Discussions](https://github.com/redis/go-redis/discussions) -- [Chat](https://discord.gg/rWtp5Aj) +- [Chat](https://discord.gg/W4txy5AeKM) - [Reference](https://pkg.go.dev/github.com/redis/go-redis/v9) - [Examples](https://pkg.go.dev/github.com/redis/go-redis/v9#pkg-examples) diff --git a/command.go b/command.go index 69650145..364706e3 100644 --- a/command.go +++ b/command.go @@ -3831,7 +3831,8 @@ func (cmd *MapStringStringSliceCmd) readReply(rd *proto.Reader) error { } // ----------------------------------------------------------------------- -// MapStringInterfaceCmd represents a command that returns a map of strings to interface{}. + +// MapMapStringInterfaceCmd represents a command that returns a map of strings to interface{}. type MapMapStringInterfaceCmd struct { baseCmd val map[string]interface{} diff --git a/commands.go b/commands.go index 6321c15e..bca7d7ee 100644 --- a/commands.go +++ b/commands.go @@ -81,6 +81,8 @@ func appendArg(dst []interface{}, arg interface{}) []interface{} { return dst case time.Time, time.Duration, encoding.BinaryMarshaler, net.IP: return append(dst, arg) + case nil: + return dst default: // scan struct field v := reflect.ValueOf(arg) @@ -330,7 +332,7 @@ func (info LibraryInfo) Validate() error { return nil } -// Hello Set the resp protocol used. +// Hello sets the resp protocol used. func (c statefulCmdable) Hello(ctx context.Context, ver int, username, password, clientName string, ) *MapStringInterfaceCmd { diff --git a/commands_test.go b/commands_test.go index 55b95749..6a76756a 100644 --- a/commands_test.go +++ b/commands_test.go @@ -7209,6 +7209,17 @@ var _ = Describe("Commands", func() { Expect(err).NotTo(HaveOccurred()) Expect(vals).To(Equal([]interface{}{int64(12), proto.RedisError("error"), "abc"})) }) + + It("returns empty values when args are nil", func() { + vals, err := client.Eval( + ctx, + "return {ARGV[1]}", + []string{}, + nil, + ).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(BeEmpty()) + }) }) Describe("EvalRO", func() { @@ -7232,6 +7243,17 @@ var _ = Describe("Commands", func() { Expect(err).NotTo(HaveOccurred()) Expect(vals).To(Equal([]interface{}{int64(12), proto.RedisError("error"), "abc"})) }) + + It("returns empty values when args are nil", func() { + vals, err := client.EvalRO( + ctx, + "return {ARGV[1]}", + []string{}, + nil, + ).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(BeEmpty()) + }) }) Describe("Functions", func() { diff --git a/doctests/home_json_example_test.go b/doctests/home_json_example_test.go index 4ee93d79..f32bf8d1 100644 --- a/doctests/home_json_example_test.go +++ b/doctests/home_json_example_test.go @@ -227,3 +227,107 @@ func ExampleClient_search_json() { // London - 1 // Tel Aviv - 2 } + +func ExampleClient_search_hash() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + Protocol: 2, + }) + + // REMOVE_START + rdb.Del(ctx, "huser:1", "huser:2", "huser:3") + rdb.FTDropIndex(ctx, "hash-idx:users") + // REMOVE_END + + // STEP_START make_hash_index + _, err := rdb.FTCreate( + ctx, + "hash-idx:users", + // Options: + &redis.FTCreateOptions{ + OnHash: true, + Prefix: []interface{}{"huser:"}, + }, + // Index schema fields: + &redis.FieldSchema{ + FieldName: "name", + FieldType: redis.SearchFieldTypeText, + }, + &redis.FieldSchema{ + FieldName: "city", + FieldType: redis.SearchFieldTypeTag, + }, + &redis.FieldSchema{ + FieldName: "age", + FieldType: redis.SearchFieldTypeNumeric, + }, + ).Result() + + if err != nil { + panic(err) + } + // STEP_END + + 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_START add_hash_data + _, err = rdb.HSet(ctx, "huser:1", user1).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.HSet(ctx, "huser:2", user2).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.HSet(ctx, "huser:3", user3).Result() + + if err != nil { + panic(err) + } + // STEP_END + + // STEP_START query1_hash + findPaulHashResult, err := rdb.FTSearch( + ctx, + "hash-idx:users", + "Paul @age:[30 40]", + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(findPaulHashResult) + // >>> {1 [{huser:3 map[age:35 city:Tel Aviv... + // STEP_END + + // Output: + // {1 [{huser:3 map[age:35 city:Tel Aviv email:paul.zamir@example.com name:Paul Zamir]}]} +} diff --git a/hash_commands.go b/hash_commands.go index 74d569dd..98a361b3 100644 --- a/hash_commands.go +++ b/hash_commands.go @@ -520,7 +520,7 @@ func (c cmdable) HGetEX(ctx context.Context, key string, fields ...string) *Stri return cmd } -// ExpirationType represents an expiration option for the HGETEX command. +// HGetEXExpirationType represents an expiration option for the HGETEX command. type HGetEXExpirationType string const (