Skip to content

Commit 8860c9e

Browse files
authored
Merge branch 'redis:master' into master
2 parents e409efd + 182a04f commit 8860c9e

14 files changed

+869
-342
lines changed

.github/workflows/spellcheck.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
- name: Checkout
99
uses: actions/checkout@v4
1010
- name: Check Spelling
11-
uses: rojopolis/spellcheck-github-actions@0.47.0
11+
uses: rojopolis/spellcheck-github-actions@0.48.0
1212
with:
1313
config_path: .github/spellcheck-settings.yml
1414
task_name: Markdown

CONTRIBUTING.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,7 @@ The core team regularly looks at pull requests. We will provide
112112
feedback as soon as possible. After receiving our feedback, please respond
113113
within two weeks. After that time, we may close your PR if it isn't
114114
showing any activity.
115+
116+
## Support
117+
118+
Maintainers can provide limited support to contributors on discord: https://discord.gg/W4txy5AeKM

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@
33
[![build workflow](https://github.com/redis/go-redis/actions/workflows/build.yml/badge.svg)](https://github.com/redis/go-redis/actions)
44
[![PkgGoDev](https://pkg.go.dev/badge/github.com/redis/go-redis/v9)](https://pkg.go.dev/github.com/redis/go-redis/v9?tab=doc)
55
[![Documentation](https://img.shields.io/badge/redis-documentation-informational)](https://redis.uptrace.dev/)
6+
[![Go Report Card](https://goreportcard.com/badge/github.com/redis/go-redis/v9)](https://goreportcard.com/report/github.com/redis/go-redis/v9)
67
[![codecov](https://codecov.io/github/redis/go-redis/graph/badge.svg?token=tsrCZKuSSw)](https://codecov.io/github/redis/go-redis)
7-
[![Chat](https://discordapp.com/api/guilds/752070105847955518/widget.png)](https://discord.gg/rWtp5Aj)
8+
9+
[![Discord](https://img.shields.io/discord/697882427875393627.svg?style=social&logo=discord)](https://discord.gg/W4txy5AeKM)
10+
[![Twitch](https://img.shields.io/twitch/status/redisinc?style=social)](https://www.twitch.tv/redisinc)
11+
[![YouTube](https://img.shields.io/youtube/channel/views/UCD78lHSwYqMlyetR0_P4Vig?style=social)](https://www.youtube.com/redisinc)
12+
[![Twitter](https://img.shields.io/twitter/follow/redisinc?style=social)](https://twitter.com/redisinc)
13+
[![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)
814

915
> go-redis is the official Redis client library for the Go programming language. It offers a straightforward interface for interacting with Redis servers.
1016
@@ -44,7 +50,7 @@ in the `go.mod` to `go 1.24` in one of the next releases.
4450
## Resources
4551

4652
- [Discussions](https://github.com/redis/go-redis/discussions)
47-
- [Chat](https://discord.gg/rWtp5Aj)
53+
- [Chat](https://discord.gg/W4txy5AeKM)
4854
- [Reference](https://pkg.go.dev/github.com/redis/go-redis/v9)
4955
- [Examples](https://pkg.go.dev/github.com/redis/go-redis/v9#pkg-examples)
5056

command.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3831,7 +3831,8 @@ func (cmd *MapStringStringSliceCmd) readReply(rd *proto.Reader) error {
38313831
}
38323832

38333833
// -----------------------------------------------------------------------
3834-
// MapStringInterfaceCmd represents a command that returns a map of strings to interface{}.
3834+
3835+
// MapMapStringInterfaceCmd represents a command that returns a map of strings to interface{}.
38353836
type MapMapStringInterfaceCmd struct {
38363837
baseCmd
38373838
val map[string]interface{}

commands.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ func appendArg(dst []interface{}, arg interface{}) []interface{} {
8181
return dst
8282
case time.Time, time.Duration, encoding.BinaryMarshaler, net.IP:
8383
return append(dst, arg)
84+
case nil:
85+
return dst
8486
default:
8587
// scan struct field
8688
v := reflect.ValueOf(arg)
@@ -332,7 +334,7 @@ func (info LibraryInfo) Validate() error {
332334
return nil
333335
}
334336

335-
// Hello Set the resp protocol used.
337+
// Hello sets the resp protocol used.
336338
func (c statefulCmdable) Hello(ctx context.Context,
337339
ver int, username, password, clientName string,
338340
) *MapStringInterfaceCmd {

commands_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7255,6 +7255,17 @@ var _ = Describe("Commands", func() {
72557255
Expect(err).NotTo(HaveOccurred())
72567256
Expect(vals).To(Equal([]interface{}{int64(12), proto.RedisError("error"), "abc"}))
72577257
})
7258+
7259+
It("returns empty values when args are nil", func() {
7260+
vals, err := client.Eval(
7261+
ctx,
7262+
"return {ARGV[1]}",
7263+
[]string{},
7264+
nil,
7265+
).Result()
7266+
Expect(err).NotTo(HaveOccurred())
7267+
Expect(vals).To(BeEmpty())
7268+
})
72587269
})
72597270

72607271
Describe("EvalRO", func() {
@@ -7278,6 +7289,17 @@ var _ = Describe("Commands", func() {
72787289
Expect(err).NotTo(HaveOccurred())
72797290
Expect(vals).To(Equal([]interface{}{int64(12), proto.RedisError("error"), "abc"}))
72807291
})
7292+
7293+
It("returns empty values when args are nil", func() {
7294+
vals, err := client.EvalRO(
7295+
ctx,
7296+
"return {ARGV[1]}",
7297+
[]string{},
7298+
nil,
7299+
).Result()
7300+
Expect(err).NotTo(HaveOccurred())
7301+
Expect(vals).To(BeEmpty())
7302+
})
72817303
})
72827304

72837305
Describe("Functions", func() {

doctests/home_json_example_test.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,32 @@ func ExampleClient_search_json() {
152152
// >>> Tel Aviv
153153
// STEP_END
154154

155+
// STEP_START query2count_only
156+
citiesResult2, err := rdb.FTSearchWithArgs(
157+
ctx,
158+
"idx:users",
159+
"Paul",
160+
&redis.FTSearchOptions{
161+
Return: []redis.FTSearchReturn{
162+
{
163+
FieldName: "$.city",
164+
As: "city",
165+
},
166+
},
167+
CountOnly: true,
168+
},
169+
).Result()
170+
171+
if err != nil {
172+
panic(err)
173+
}
174+
175+
// The `Total` field has the correct number of docs found
176+
// by the query but the `Docs` slice is empty.
177+
fmt.Println(len(citiesResult2.Docs)) // >>> 0
178+
fmt.Println(citiesResult2.Total) // >>> 2
179+
// STEP_END
180+
155181
// STEP_START query3
156182
aggOptions := redis.FTAggregateOptions{
157183
GroupBy: []redis.FTAggregateGroupBy{
@@ -196,6 +222,112 @@ func ExampleClient_search_json() {
196222
// {1 [{user:3 <nil> <nil> <nil> map[$:{"age":35,"city":"Tel Aviv","email":"paul.zamir@example.com","name":"Paul Zamir"}]}]}
197223
// London
198224
// Tel Aviv
225+
// 0
226+
// 2
199227
// London - 1
200228
// Tel Aviv - 2
201229
}
230+
231+
func ExampleClient_search_hash() {
232+
ctx := context.Background()
233+
234+
rdb := redis.NewClient(&redis.Options{
235+
Addr: "localhost:6379",
236+
Password: "", // no password docs
237+
DB: 0, // use default DB
238+
Protocol: 2,
239+
})
240+
241+
// REMOVE_START
242+
rdb.Del(ctx, "huser:1", "huser:2", "huser:3")
243+
rdb.FTDropIndex(ctx, "hash-idx:users")
244+
// REMOVE_END
245+
246+
// STEP_START make_hash_index
247+
_, err := rdb.FTCreate(
248+
ctx,
249+
"hash-idx:users",
250+
// Options:
251+
&redis.FTCreateOptions{
252+
OnHash: true,
253+
Prefix: []interface{}{"huser:"},
254+
},
255+
// Index schema fields:
256+
&redis.FieldSchema{
257+
FieldName: "name",
258+
FieldType: redis.SearchFieldTypeText,
259+
},
260+
&redis.FieldSchema{
261+
FieldName: "city",
262+
FieldType: redis.SearchFieldTypeTag,
263+
},
264+
&redis.FieldSchema{
265+
FieldName: "age",
266+
FieldType: redis.SearchFieldTypeNumeric,
267+
},
268+
).Result()
269+
270+
if err != nil {
271+
panic(err)
272+
}
273+
// STEP_END
274+
275+
user1 := map[string]interface{}{
276+
"name": "Paul John",
277+
"email": "paul.john@example.com",
278+
"age": 42,
279+
"city": "London",
280+
}
281+
282+
user2 := map[string]interface{}{
283+
"name": "Eden Zamir",
284+
"email": "eden.zamir@example.com",
285+
"age": 29,
286+
"city": "Tel Aviv",
287+
}
288+
289+
user3 := map[string]interface{}{
290+
"name": "Paul Zamir",
291+
"email": "paul.zamir@example.com",
292+
"age": 35,
293+
"city": "Tel Aviv",
294+
}
295+
296+
// STEP_START add_hash_data
297+
_, err = rdb.HSet(ctx, "huser:1", user1).Result()
298+
299+
if err != nil {
300+
panic(err)
301+
}
302+
303+
_, err = rdb.HSet(ctx, "huser:2", user2).Result()
304+
305+
if err != nil {
306+
panic(err)
307+
}
308+
309+
_, err = rdb.HSet(ctx, "huser:3", user3).Result()
310+
311+
if err != nil {
312+
panic(err)
313+
}
314+
// STEP_END
315+
316+
// STEP_START query1_hash
317+
findPaulHashResult, err := rdb.FTSearch(
318+
ctx,
319+
"hash-idx:users",
320+
"Paul @age:[30 40]",
321+
).Result()
322+
323+
if err != nil {
324+
panic(err)
325+
}
326+
327+
fmt.Println(findPaulHashResult)
328+
// >>> {1 [{huser:3 <nil> <nil> <nil> map[age:35 city:Tel Aviv...
329+
// STEP_END
330+
331+
// Output:
332+
// {1 [{huser:3 <nil> <nil> <nil> map[age:35 city:Tel Aviv email:paul.zamir@example.com name:Paul Zamir]}]}
333+
}

hash_commands.go

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type HashCmdable interface {
1313
HGetDel(ctx context.Context, key string, fields ...string) *StringSliceCmd
1414
HGetEX(ctx context.Context, key string, fields ...string) *StringSliceCmd
1515
HGetEXWithArgs(ctx context.Context, key string, options *HGetEXOptions, fields ...string) *StringSliceCmd
16+
HIncrBy(ctx context.Context, key, field string, incr int64) *IntCmd
1617
HIncrByFloat(ctx context.Context, key, field string, incr float64) *FloatCmd
1718
HKeys(ctx context.Context, key string) *StringSliceCmd
1819
HLen(ctx context.Context, key string) *IntCmd
@@ -223,7 +224,10 @@ type HExpireArgs struct {
223224

224225
// HExpire - Sets the expiration time for specified fields in a hash in seconds.
225226
// The command constructs an argument list starting with "HEXPIRE", followed by the key, duration, any conditional flags, and the specified fields.
226-
// For more information - https://redis.io/commands/hexpire/
227+
// Available since Redis 7.4 CE.
228+
// For more information refer to [HEXPIRE Documentation].
229+
//
230+
// [HEXPIRE Documentation]: https://redis.io/commands/hexpire/
227231
func (c cmdable) HExpire(ctx context.Context, key string, expiration time.Duration, fields ...string) *IntSliceCmd {
228232
args := []interface{}{"HEXPIRE", key, formatSec(ctx, expiration), "FIELDS", len(fields)}
229233

@@ -238,7 +242,10 @@ func (c cmdable) HExpire(ctx context.Context, key string, expiration time.Durati
238242
// HExpireWithArgs - Sets the expiration time for specified fields in a hash in seconds.
239243
// It requires a key, an expiration duration, a struct with boolean flags for conditional expiration settings (NX, XX, GT, LT), and a list of fields.
240244
// The command constructs an argument list starting with "HEXPIRE", followed by the key, duration, any conditional flags, and the specified fields.
241-
// For more information - https://redis.io/commands/hexpire/
245+
// Available since Redis 7.4 CE.
246+
// For more information refer to [HEXPIRE Documentation].
247+
//
248+
// [HEXPIRE Documentation]: https://redis.io/commands/hexpire/
242249
func (c cmdable) HExpireWithArgs(ctx context.Context, key string, expiration time.Duration, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd {
243250
args := []interface{}{"HEXPIRE", key, formatSec(ctx, expiration)}
244251

@@ -267,7 +274,10 @@ func (c cmdable) HExpireWithArgs(ctx context.Context, key string, expiration tim
267274
// HPExpire - Sets the expiration time for specified fields in a hash in milliseconds.
268275
// Similar to HExpire, it accepts a key, an expiration duration in milliseconds, a struct with expiration condition flags, and a list of fields.
269276
// The command modifies the standard time.Duration to milliseconds for the Redis command.
270-
// For more information - https://redis.io/commands/hpexpire/
277+
// Available since Redis 7.4 CE.
278+
// For more information refer to [HPEXPIRE Documentation].
279+
//
280+
// [HPEXPIRE Documentation]: https://redis.io/commands/hpexpire/
271281
func (c cmdable) HPExpire(ctx context.Context, key string, expiration time.Duration, fields ...string) *IntSliceCmd {
272282
args := []interface{}{"HPEXPIRE", key, formatMs(ctx, expiration), "FIELDS", len(fields)}
273283

@@ -279,6 +289,13 @@ func (c cmdable) HPExpire(ctx context.Context, key string, expiration time.Durat
279289
return cmd
280290
}
281291

292+
// HPExpireWithArgs - Sets the expiration time for specified fields in a hash in milliseconds.
293+
// It requires a key, an expiration duration, a struct with boolean flags for conditional expiration settings (NX, XX, GT, LT), and a list of fields.
294+
// The command constructs an argument list starting with "HPEXPIRE", followed by the key, duration, any conditional flags, and the specified fields.
295+
// Available since Redis 7.4 CE.
296+
// For more information refer to [HPEXPIRE Documentation].
297+
//
298+
// [HPEXPIRE Documentation]: https://redis.io/commands/hpexpire/
282299
func (c cmdable) HPExpireWithArgs(ctx context.Context, key string, expiration time.Duration, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd {
283300
args := []interface{}{"HPEXPIRE", key, formatMs(ctx, expiration)}
284301

@@ -307,7 +324,10 @@ func (c cmdable) HPExpireWithArgs(ctx context.Context, key string, expiration ti
307324
// HExpireAt - Sets the expiration time for specified fields in a hash to a UNIX timestamp in seconds.
308325
// Takes a key, a UNIX timestamp, a struct of conditional flags, and a list of fields.
309326
// The command sets absolute expiration times based on the UNIX timestamp provided.
310-
// For more information - https://redis.io/commands/hexpireat/
327+
// Available since Redis 7.4 CE.
328+
// For more information refer to [HExpireAt Documentation].
329+
//
330+
// [HExpireAt Documentation]: https://redis.io/commands/hexpireat/
311331
func (c cmdable) HExpireAt(ctx context.Context, key string, tm time.Time, fields ...string) *IntSliceCmd {
312332

313333
args := []interface{}{"HEXPIREAT", key, tm.Unix(), "FIELDS", len(fields)}
@@ -347,7 +367,10 @@ func (c cmdable) HExpireAtWithArgs(ctx context.Context, key string, tm time.Time
347367

348368
// HPExpireAt - Sets the expiration time for specified fields in a hash to a UNIX timestamp in milliseconds.
349369
// Similar to HExpireAt but for timestamps in milliseconds. It accepts the same parameters and adjusts the UNIX time to milliseconds.
350-
// For more information - https://redis.io/commands/hpexpireat/
370+
// Available since Redis 7.4 CE.
371+
// For more information refer to [HExpireAt Documentation].
372+
//
373+
// [HExpireAt Documentation]: https://redis.io/commands/hexpireat/
351374
func (c cmdable) HPExpireAt(ctx context.Context, key string, tm time.Time, fields ...string) *IntSliceCmd {
352375
args := []interface{}{"HPEXPIREAT", key, tm.UnixNano() / int64(time.Millisecond), "FIELDS", len(fields)}
353376

@@ -387,7 +410,10 @@ func (c cmdable) HPExpireAtWithArgs(ctx context.Context, key string, tm time.Tim
387410
// HPersist - Removes the expiration time from specified fields in a hash.
388411
// Accepts a key and the fields themselves.
389412
// This command ensures that each field specified will have its expiration removed if present.
390-
// For more information - https://redis.io/commands/hpersist/
413+
// Available since Redis 7.4 CE.
414+
// For more information refer to [HPersist Documentation].
415+
//
416+
// [HPersist Documentation]: https://redis.io/commands/hpersist/
391417
func (c cmdable) HPersist(ctx context.Context, key string, fields ...string) *IntSliceCmd {
392418
args := []interface{}{"HPERSIST", key, "FIELDS", len(fields)}
393419

@@ -402,6 +428,10 @@ func (c cmdable) HPersist(ctx context.Context, key string, fields ...string) *In
402428
// HExpireTime - Retrieves the expiration time for specified fields in a hash as a UNIX timestamp in seconds.
403429
// Requires a key and the fields themselves to fetch their expiration timestamps.
404430
// This command returns the expiration times for each field or error/status codes for each field as specified.
431+
// Available since Redis 7.4 CE.
432+
// For more information refer to [HExpireTime Documentation].
433+
//
434+
// [HExpireTime Documentation]: https://redis.io/commands/hexpiretime/
405435
// For more information - https://redis.io/commands/hexpiretime/
406436
func (c cmdable) HExpireTime(ctx context.Context, key string, fields ...string) *IntSliceCmd {
407437
args := []interface{}{"HEXPIRETIME", key, "FIELDS", len(fields)}
@@ -417,6 +447,10 @@ func (c cmdable) HExpireTime(ctx context.Context, key string, fields ...string)
417447
// HPExpireTime - Retrieves the expiration time for specified fields in a hash as a UNIX timestamp in milliseconds.
418448
// Similar to HExpireTime, adjusted for timestamps in milliseconds. It requires the same parameters.
419449
// Provides the expiration timestamp for each field in milliseconds.
450+
// Available since Redis 7.4 CE.
451+
// For more information refer to [HExpireTime Documentation].
452+
//
453+
// [HExpireTime Documentation]: https://redis.io/commands/hexpiretime/
420454
// For more information - https://redis.io/commands/hexpiretime/
421455
func (c cmdable) HPExpireTime(ctx context.Context, key string, fields ...string) *IntSliceCmd {
422456
args := []interface{}{"HPEXPIRETIME", key, "FIELDS", len(fields)}
@@ -432,7 +466,10 @@ func (c cmdable) HPExpireTime(ctx context.Context, key string, fields ...string)
432466
// HTTL - Retrieves the remaining time to live for specified fields in a hash in seconds.
433467
// Requires a key and the fields themselves. It returns the TTL for each specified field.
434468
// This command fetches the TTL in seconds for each field or returns error/status codes as appropriate.
435-
// For more information - https://redis.io/commands/httl/
469+
// Available since Redis 7.4 CE.
470+
// For more information refer to [HTTL Documentation].
471+
//
472+
// [HTTL Documentation]: https://redis.io/commands/httl/
436473
func (c cmdable) HTTL(ctx context.Context, key string, fields ...string) *IntSliceCmd {
437474
args := []interface{}{"HTTL", key, "FIELDS", len(fields)}
438475

@@ -447,6 +484,10 @@ func (c cmdable) HTTL(ctx context.Context, key string, fields ...string) *IntSli
447484
// HPTTL - Retrieves the remaining time to live for specified fields in a hash in milliseconds.
448485
// Similar to HTTL, but returns the TTL in milliseconds. It requires a key and the specified fields.
449486
// This command provides the TTL in milliseconds for each field or returns error/status codes as needed.
487+
// Available since Redis 7.4 CE.
488+
// For more information refer to [HPTTL Documentation].
489+
//
490+
// [HPTTL Documentation]: https://redis.io/commands/hpttl/
450491
// For more information - https://redis.io/commands/hpttl/
451492
func (c cmdable) HPTTL(ctx context.Context, key string, fields ...string) *IntSliceCmd {
452493
args := []interface{}{"HPTTL", key, "FIELDS", len(fields)}
@@ -479,7 +520,7 @@ func (c cmdable) HGetEX(ctx context.Context, key string, fields ...string) *Stri
479520
return cmd
480521
}
481522

482-
// ExpirationType represents an expiration option for the HGETEX command.
523+
// HGetEXExpirationType represents an expiration option for the HGETEX command.
483524
type HGetEXExpirationType string
484525

485526
const (

0 commit comments

Comments
 (0)