55 "errors"
66 "io"
77 "reflect"
8+ "strings"
89 "time"
910
1011 "github.com/go-redis/redis/v9/internal"
@@ -75,31 +76,44 @@ func appendArg(dst []interface{}, arg interface{}) []interface{} {
7576 }
7677 return dst
7778 default :
79+ // scan struct field
80+ v := reflect .ValueOf (arg )
81+ if v .Type ().Kind () == reflect .Ptr {
82+ if v .IsNil () {
83+ // error: arg is not a valid object
84+ return dst
85+ }
86+ v = v .Elem ()
87+ }
88+
89+ if v .Type ().Kind () == reflect .Struct {
90+ return appendStructField (dst , v )
91+ }
92+
7893 return append (dst , arg )
7994 }
8095}
8196
82- func structToMap (items interface {}) map [string ]interface {} {
83- res := map [string ]interface {}{}
84- if items == nil {
85- return res
86- }
87- v := reflect .TypeOf (items )
88- reflectValue := reflect .Indirect (reflect .ValueOf (items ))
89-
90- if v .Kind () == reflect .Ptr {
91- v = v .Elem ()
92- }
93- for i := 0 ; i < v .NumField (); i ++ {
94- tag := v .Field (i ).Tag .Get ("redis" )
97+ // appendStructField appends the field and value held by the structure v to dst, and returns the appended dst.
98+ func appendStructField (dst []interface {}, v reflect.Value ) []interface {} {
99+ typ := v .Type ()
100+ for i := 0 ; i < typ .NumField (); i ++ {
101+ tag := typ .Field (i ).Tag .Get ("redis" )
102+ if tag == "" || tag == "-" {
103+ continue
104+ }
105+ tag = strings .Split (tag , "," )[0 ]
106+ if tag == "" {
107+ continue
108+ }
95109
96- if tag != "" && v .Field (i ). Type . Kind () != reflect . Struct {
97- field := reflectValue . Field ( i ). Interface ()
98- res [ tag ] = field
110+ field := v .Field (i )
111+ if field . CanInterface () {
112+ dst = append ( dst , tag , field . Interface ())
99113 }
100114 }
101115
102- return res
116+ return dst
103117}
104118
105119type Cmdable interface {
@@ -904,6 +918,7 @@ func (c cmdable) MGet(ctx context.Context, keys ...string) *SliceCmd {
904918// - MSet("key1", "value1", "key2", "value2")
905919// - MSet([]string{"key1", "value1", "key2", "value2"})
906920// - MSet(map[string]interface{}{"key1": "value1", "key2": "value2"})
921+ // - MSet(struct), For struct types, see HSet description.
907922func (c cmdable ) MSet (ctx context.Context , values ... interface {}) * StatusCmd {
908923 args := make ([]interface {}, 1 , 1 + len (values ))
909924 args [0 ] = "mset"
@@ -917,6 +932,7 @@ func (c cmdable) MSet(ctx context.Context, values ...interface{}) *StatusCmd {
917932// - MSetNX("key1", "value1", "key2", "value2")
918933// - MSetNX([]string{"key1", "value1", "key2", "value2"})
919934// - MSetNX(map[string]interface{}{"key1": "value1", "key2": "value2"})
935+ // - MSetNX(struct), For struct types, see HSet description.
920936func (c cmdable ) MSetNX (ctx context.Context , values ... interface {}) * BoolCmd {
921937 args := make ([]interface {}, 1 , 1 + len (values ))
922938 args [0 ] = "msetnx"
@@ -1319,21 +1335,27 @@ func (c cmdable) HMGet(ctx context.Context, key string, fields ...string) *Slice
13191335}
13201336
13211337// HSet accepts values in following formats:
1338+ //
13221339// - HSet("myhash", "key1", "value1", "key2", "value2")
1340+ //
13231341// - HSet("myhash", []string{"key1", "value1", "key2", "value2"})
1342+ //
13241343// - HSet("myhash", map[string]interface{}{"key1": "value1", "key2": "value2"})
13251344//
1326- // Playing struct With "redis" tag
1327- // - type MyHash struct { Key1 string `redis:"key1"`; Key2 int `redis:"key2"` }
1345+ // Playing struct With "redis" tag.
1346+ // type MyHash struct { Key1 string `redis:"key1"`; Key2 int `redis:"key2"` }
1347+ //
13281348// - HSet("myhash", MyHash{"value1", "value2"})
13291349//
1350+ // For struct, can be a structure pointer type, we only parse the field whose tag is redis.
1351+ // if you don't want the field to be read, you can use the `redis:"-"` flag to ignore it,
1352+ // or you don't need to set the redis tag.
1353+ // For the type of structure field, we only support simple data types:
1354+ // string, int/uint(8,16,32,64), float(32,64), time.Time(to RFC3339Nano), time.Duration(to Nanoseconds ),
1355+ // if you are other more complex or custom data types, please implement the encoding.BinaryMarshaler interface.
1356+ //
13301357// Note that it requires Redis v4 for multiple field/value pairs support.
13311358func (c cmdable ) HSet (ctx context.Context , key string , values ... interface {}) * IntCmd {
1332- if len (values ) == 1 {
1333- if reflect .ValueOf (values [0 ]).Kind () == reflect .Struct {
1334- values = []interface {}{structToMap (values [0 ])}
1335- }
1336- }
13371359 args := make ([]interface {}, 2 , 2 + len (values ))
13381360 args [0 ] = "hset"
13391361 args [1 ] = key
0 commit comments