44 "context"
55 "errors"
66 "io"
7+ "reflect"
8+ "strings"
79 "time"
810
911 "github.com/go-redis/redis/v9/internal"
@@ -74,10 +76,46 @@ func appendArg(dst []interface{}, arg interface{}) []interface{} {
7476 }
7577 return dst
7678 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+
7793 return append (dst , arg )
7894 }
7995}
8096
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+ }
109+
110+ field := v .Field (i )
111+ if field .CanInterface () {
112+ dst = append (dst , tag , field .Interface ())
113+ }
114+ }
115+
116+ return dst
117+ }
118+
81119type Cmdable interface {
82120 Pipeline () Pipeliner
83121 Pipelined (ctx context.Context , fn func (Pipeliner ) error ) ([]Cmder , error )
@@ -880,6 +918,7 @@ func (c cmdable) MGet(ctx context.Context, keys ...string) *SliceCmd {
880918// - MSet("key1", "value1", "key2", "value2")
881919// - MSet([]string{"key1", "value1", "key2", "value2"})
882920// - MSet(map[string]interface{}{"key1": "value1", "key2": "value2"})
921+ // - MSet(struct), For struct types, see HSet description.
883922func (c cmdable ) MSet (ctx context.Context , values ... interface {}) * StatusCmd {
884923 args := make ([]interface {}, 1 , 1 + len (values ))
885924 args [0 ] = "mset"
@@ -893,6 +932,7 @@ func (c cmdable) MSet(ctx context.Context, values ...interface{}) *StatusCmd {
893932// - MSetNX("key1", "value1", "key2", "value2")
894933// - MSetNX([]string{"key1", "value1", "key2", "value2"})
895934// - MSetNX(map[string]interface{}{"key1": "value1", "key2": "value2"})
935+ // - MSetNX(struct), For struct types, see HSet description.
896936func (c cmdable ) MSetNX (ctx context.Context , values ... interface {}) * BoolCmd {
897937 args := make ([]interface {}, 1 , 1 + len (values ))
898938 args [0 ] = "msetnx"
@@ -1295,10 +1335,25 @@ func (c cmdable) HMGet(ctx context.Context, key string, fields ...string) *Slice
12951335}
12961336
12971337// HSet accepts values in following formats:
1338+ //
12981339// - HSet("myhash", "key1", "value1", "key2", "value2")
1340+ //
12991341// - HSet("myhash", []string{"key1", "value1", "key2", "value2"})
1342+ //
13001343// - HSet("myhash", map[string]interface{}{"key1": "value1", "key2": "value2"})
13011344//
1345+ // Playing struct With "redis" tag.
1346+ // type MyHash struct { Key1 string `redis:"key1"`; Key2 int `redis:"key2"` }
1347+ //
1348+ // - HSet("myhash", MyHash{"value1", "value2"})
1349+ //
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+ //
13021357// Note that it requires Redis v4 for multiple field/value pairs support.
13031358func (c cmdable ) HSet (ctx context.Context , key string , values ... interface {}) * IntCmd {
13041359 args := make ([]interface {}, 2 , 2 + len (values ))
0 commit comments