mirror of
https://github.com/redis/go-redis.git
synced 2025-04-19 07:22:17 +03:00
Merge pull request #1960 from wjdqhry/feature/struct-to-hashset
feat: HSet, MSet, MSetNX allow structure parameters.
This commit is contained in:
commit
31f6ce0399
55
commands.go
55
commands.go
@ -4,6 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-redis/redis/v9/internal"
|
"github.com/go-redis/redis/v9/internal"
|
||||||
@ -74,10 +76,46 @@ func appendArg(dst []interface{}, arg interface{}) []interface{} {
|
|||||||
}
|
}
|
||||||
return dst
|
return dst
|
||||||
default:
|
default:
|
||||||
|
// scan struct field
|
||||||
|
v := reflect.ValueOf(arg)
|
||||||
|
if v.Type().Kind() == reflect.Ptr {
|
||||||
|
if v.IsNil() {
|
||||||
|
// error: arg is not a valid object
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Type().Kind() == reflect.Struct {
|
||||||
|
return appendStructField(dst, v)
|
||||||
|
}
|
||||||
|
|
||||||
return append(dst, arg)
|
return append(dst, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// appendStructField appends the field and value held by the structure v to dst, and returns the appended dst.
|
||||||
|
func appendStructField(dst []interface{}, v reflect.Value) []interface{} {
|
||||||
|
typ := v.Type()
|
||||||
|
for i := 0; i < typ.NumField(); i++ {
|
||||||
|
tag := typ.Field(i).Tag.Get("redis")
|
||||||
|
if tag == "" || tag == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tag = strings.Split(tag, ",")[0]
|
||||||
|
if tag == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
field := v.Field(i)
|
||||||
|
if field.CanInterface() {
|
||||||
|
dst = append(dst, tag, field.Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
type Cmdable interface {
|
type Cmdable interface {
|
||||||
Pipeline() Pipeliner
|
Pipeline() Pipeliner
|
||||||
Pipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error)
|
Pipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error)
|
||||||
@ -880,6 +918,7 @@ func (c cmdable) MGet(ctx context.Context, keys ...string) *SliceCmd {
|
|||||||
// - MSet("key1", "value1", "key2", "value2")
|
// - MSet("key1", "value1", "key2", "value2")
|
||||||
// - MSet([]string{"key1", "value1", "key2", "value2"})
|
// - MSet([]string{"key1", "value1", "key2", "value2"})
|
||||||
// - MSet(map[string]interface{}{"key1": "value1", "key2": "value2"})
|
// - MSet(map[string]interface{}{"key1": "value1", "key2": "value2"})
|
||||||
|
// - MSet(struct), For struct types, see HSet description.
|
||||||
func (c cmdable) MSet(ctx context.Context, values ...interface{}) *StatusCmd {
|
func (c cmdable) MSet(ctx context.Context, values ...interface{}) *StatusCmd {
|
||||||
args := make([]interface{}, 1, 1+len(values))
|
args := make([]interface{}, 1, 1+len(values))
|
||||||
args[0] = "mset"
|
args[0] = "mset"
|
||||||
@ -893,6 +932,7 @@ func (c cmdable) MSet(ctx context.Context, values ...interface{}) *StatusCmd {
|
|||||||
// - MSetNX("key1", "value1", "key2", "value2")
|
// - MSetNX("key1", "value1", "key2", "value2")
|
||||||
// - MSetNX([]string{"key1", "value1", "key2", "value2"})
|
// - MSetNX([]string{"key1", "value1", "key2", "value2"})
|
||||||
// - MSetNX(map[string]interface{}{"key1": "value1", "key2": "value2"})
|
// - MSetNX(map[string]interface{}{"key1": "value1", "key2": "value2"})
|
||||||
|
// - MSetNX(struct), For struct types, see HSet description.
|
||||||
func (c cmdable) MSetNX(ctx context.Context, values ...interface{}) *BoolCmd {
|
func (c cmdable) MSetNX(ctx context.Context, values ...interface{}) *BoolCmd {
|
||||||
args := make([]interface{}, 1, 1+len(values))
|
args := make([]interface{}, 1, 1+len(values))
|
||||||
args[0] = "msetnx"
|
args[0] = "msetnx"
|
||||||
@ -1295,10 +1335,25 @@ func (c cmdable) HMGet(ctx context.Context, key string, fields ...string) *Slice
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HSet accepts values in following formats:
|
// HSet accepts values in following formats:
|
||||||
|
//
|
||||||
// - HSet("myhash", "key1", "value1", "key2", "value2")
|
// - HSet("myhash", "key1", "value1", "key2", "value2")
|
||||||
|
//
|
||||||
// - HSet("myhash", []string{"key1", "value1", "key2", "value2"})
|
// - HSet("myhash", []string{"key1", "value1", "key2", "value2"})
|
||||||
|
//
|
||||||
// - HSet("myhash", map[string]interface{}{"key1": "value1", "key2": "value2"})
|
// - HSet("myhash", map[string]interface{}{"key1": "value1", "key2": "value2"})
|
||||||
//
|
//
|
||||||
|
// Playing struct With "redis" tag.
|
||||||
|
// type MyHash struct { Key1 string `redis:"key1"`; Key2 int `redis:"key2"` }
|
||||||
|
//
|
||||||
|
// - HSet("myhash", MyHash{"value1", "value2"})
|
||||||
|
//
|
||||||
|
// For struct, can be a structure pointer type, we only parse the field whose tag is redis.
|
||||||
|
// if you don't want the field to be read, you can use the `redis:"-"` flag to ignore it,
|
||||||
|
// or you don't need to set the redis tag.
|
||||||
|
// For the type of structure field, we only support simple data types:
|
||||||
|
// string, int/uint(8,16,32,64), float(32,64), time.Time(to RFC3339Nano), time.Duration(to Nanoseconds ),
|
||||||
|
// if you are other more complex or custom data types, please implement the encoding.BinaryMarshaler interface.
|
||||||
|
//
|
||||||
// Note that it requires Redis v4 for multiple field/value pairs support.
|
// Note that it requires Redis v4 for multiple field/value pairs support.
|
||||||
func (c cmdable) HSet(ctx context.Context, key string, values ...interface{}) *IntCmd {
|
func (c cmdable) HSet(ctx context.Context, key string, values ...interface{}) *IntCmd {
|
||||||
args := make([]interface{}, 2, 2+len(values))
|
args := make([]interface{}, 2, 2+len(values))
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
@ -1220,6 +1221,33 @@ var _ = Describe("Commands", func() {
|
|||||||
mGet := client.MGet(ctx, "key1", "key2", "_")
|
mGet := client.MGet(ctx, "key1", "key2", "_")
|
||||||
Expect(mGet.Err()).NotTo(HaveOccurred())
|
Expect(mGet.Err()).NotTo(HaveOccurred())
|
||||||
Expect(mGet.Val()).To(Equal([]interface{}{"hello1", "hello2", nil}))
|
Expect(mGet.Val()).To(Equal([]interface{}{"hello1", "hello2", nil}))
|
||||||
|
|
||||||
|
// MSet struct
|
||||||
|
type set struct {
|
||||||
|
Set1 string `redis:"set1"`
|
||||||
|
Set2 int16 `redis:"set2"`
|
||||||
|
Set3 time.Duration `redis:"set3"`
|
||||||
|
Set4 interface{} `redis:"set4"`
|
||||||
|
Set5 map[string]interface{} `redis:"-"`
|
||||||
|
}
|
||||||
|
mSet = client.MSet(ctx, &set{
|
||||||
|
Set1: "val1",
|
||||||
|
Set2: 1024,
|
||||||
|
Set3: 2 * time.Millisecond,
|
||||||
|
Set4: nil,
|
||||||
|
Set5: map[string]interface{}{"k1": 1},
|
||||||
|
})
|
||||||
|
Expect(mSet.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(mSet.Val()).To(Equal("OK"))
|
||||||
|
|
||||||
|
mGet = client.MGet(ctx, "set1", "set2", "set3", "set4")
|
||||||
|
Expect(mGet.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(mGet.Val()).To(Equal([]interface{}{
|
||||||
|
"val1",
|
||||||
|
"1024",
|
||||||
|
strconv.Itoa(int(2 * time.Millisecond.Nanoseconds())),
|
||||||
|
"",
|
||||||
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should scan Mget", func() {
|
It("should scan Mget", func() {
|
||||||
@ -1255,6 +1283,25 @@ var _ = Describe("Commands", func() {
|
|||||||
mSetNX = client.MSetNX(ctx, "key2", "hello1", "key3", "hello2")
|
mSetNX = client.MSetNX(ctx, "key2", "hello1", "key3", "hello2")
|
||||||
Expect(mSetNX.Err()).NotTo(HaveOccurred())
|
Expect(mSetNX.Err()).NotTo(HaveOccurred())
|
||||||
Expect(mSetNX.Val()).To(Equal(false))
|
Expect(mSetNX.Val()).To(Equal(false))
|
||||||
|
|
||||||
|
// set struct
|
||||||
|
// MSet struct
|
||||||
|
type set struct {
|
||||||
|
Set1 string `redis:"set1"`
|
||||||
|
Set2 int16 `redis:"set2"`
|
||||||
|
Set3 time.Duration `redis:"set3"`
|
||||||
|
Set4 interface{} `redis:"set4"`
|
||||||
|
Set5 map[string]interface{} `redis:"-"`
|
||||||
|
}
|
||||||
|
mSetNX = client.MSetNX(ctx, &set{
|
||||||
|
Set1: "val1",
|
||||||
|
Set2: 1024,
|
||||||
|
Set3: 2 * time.Millisecond,
|
||||||
|
Set4: nil,
|
||||||
|
Set5: map[string]interface{}{"k1": 1},
|
||||||
|
})
|
||||||
|
Expect(mSetNX.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(mSetNX.Val()).To(Equal(true))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should SetWithArgs with TTL", func() {
|
It("should SetWithArgs with TTL", func() {
|
||||||
@ -1895,6 +1942,35 @@ var _ = Describe("Commands", func() {
|
|||||||
hGet := client.HGet(ctx, "hash", "key")
|
hGet := client.HGet(ctx, "hash", "key")
|
||||||
Expect(hGet.Err()).NotTo(HaveOccurred())
|
Expect(hGet.Err()).NotTo(HaveOccurred())
|
||||||
Expect(hGet.Val()).To(Equal("hello"))
|
Expect(hGet.Val()).To(Equal("hello"))
|
||||||
|
|
||||||
|
// set struct
|
||||||
|
// MSet struct
|
||||||
|
type set struct {
|
||||||
|
Set1 string `redis:"set1"`
|
||||||
|
Set2 int16 `redis:"set2"`
|
||||||
|
Set3 time.Duration `redis:"set3"`
|
||||||
|
Set4 interface{} `redis:"set4"`
|
||||||
|
Set5 map[string]interface{} `redis:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
hSet = client.HSet(ctx, "hash", &set{
|
||||||
|
Set1: "val1",
|
||||||
|
Set2: 1024,
|
||||||
|
Set3: 2 * time.Millisecond,
|
||||||
|
Set4: nil,
|
||||||
|
Set5: map[string]interface{}{"k1": 1},
|
||||||
|
})
|
||||||
|
Expect(hSet.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(hSet.Val()).To(Equal(int64(4)))
|
||||||
|
|
||||||
|
hMGet := client.HMGet(ctx, "hash", "set1", "set2", "set3", "set4")
|
||||||
|
Expect(hMGet.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(hMGet.Val()).To(Equal([]interface{}{
|
||||||
|
"val1",
|
||||||
|
"1024",
|
||||||
|
strconv.Itoa(int(2 * time.Millisecond.Nanoseconds())),
|
||||||
|
"",
|
||||||
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should HSetNX", func() {
|
It("should HSetNX", func() {
|
||||||
|
@ -197,6 +197,21 @@ func ExampleClient_SetEx() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleClient_HSet() {
|
||||||
|
// Set "redis" tag for hash key
|
||||||
|
type ExampleUser struct {
|
||||||
|
Name string `redis:"name"`
|
||||||
|
Age int `redis:"age"`
|
||||||
|
}
|
||||||
|
|
||||||
|
items := ExampleUser{"jane", 22}
|
||||||
|
|
||||||
|
err := rdb.HSet(ctx, "user:1", items).Err()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleClient_Incr() {
|
func ExampleClient_Incr() {
|
||||||
result, err := rdb.Incr(ctx, "counter").Result()
|
result, err := rdb.Incr(ctx, "counter").Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user