Skip to content

Commit 3dfa4b6

Browse files
authored
feat(json): Add JSON.SET fpha support (#3797)
* add json.set fpha support * fix json set test
1 parent 85bca80 commit 3dfa4b6

2 files changed

Lines changed: 89 additions & 7 deletions

File tree

json.go

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ type JSONCmdable interface {
3535
JSONObjLen(ctx context.Context, key, path string) *IntPointerSliceCmd
3636
JSONSet(ctx context.Context, key, path string, value interface{}) *StatusCmd
3737
JSONSetMode(ctx context.Context, key, path string, value interface{}, mode string) *StatusCmd
38+
JSONSetWithArgs(ctx context.Context, key, path string, value interface{}, options *JSONSetArgsOptions) *StatusCmd
3839
JSONStrAppend(ctx context.Context, key, path, value string) *IntPointerSliceCmd
3940
JSONStrLen(ctx context.Context, key, path string) *IntPointerSliceCmd
4041
JSONToggle(ctx context.Context, key, path string) *IntPointerSliceCmd
@@ -57,6 +58,25 @@ type JSONArrTrimArgs struct {
5758
Stop *int
5859
}
5960

61+
// FPHAType is the floating-point type used for storing FP homogeneous arrays
62+
// in JSON.SET (Redis 8.8+).
63+
type FPHAType string
64+
65+
const (
66+
FPHATypeBF16 FPHAType = "BF16"
67+
FPHATypeFP16 FPHAType = "FP16"
68+
FPHATypeFP32 FPHAType = "FP32"
69+
FPHATypeFP64 FPHAType = "FP64"
70+
)
71+
72+
// JSONSetArgsOptions are the optional arguments for JSONSetWithArgs.
73+
// Mode is "NX" or "XX" (case-insensitive). FPHA, when set, forces Redis to
74+
// store all FP homogeneous arrays using the specified floating-point type.
75+
type JSONSetArgsOptions struct {
76+
Mode string
77+
FPHA FPHAType
78+
}
79+
6080
type JSONCmd struct {
6181
baseCmd
6282
val string
@@ -584,6 +604,15 @@ func (c cmdable) JSONSet(ctx context.Context, key, path string, value interface{
584604
// the argument is a string or []byte when we assume that it can be passed directly as JSON.
585605
// For more information, see https://redis.io/commands/json.set
586606
func (c cmdable) JSONSetMode(ctx context.Context, key, path string, value interface{}, mode string) *StatusCmd {
607+
return c.JSONSetWithArgs(ctx, key, path, value, &JSONSetArgsOptions{Mode: mode})
608+
}
609+
610+
// JSONSetWithArgs sets the JSON value at the given path in the given key with optional arguments
611+
// for setting mode (NX/XX) and the FPHA (Floating-Point Homogeneous Array) type used for storing
612+
// FP arrays. The value must be something that can be marshaled to JSON (using encoding/JSON) unless
613+
// the argument is a string or []byte when we assume that it can be passed directly as JSON.
614+
// For more information, see https://redis.io/commands/json.set
615+
func (c cmdable) JSONSetWithArgs(ctx context.Context, key, path string, value interface{}, options *JSONSetArgsOptions) *StatusCmd {
587616
var bytes []byte
588617
var err error
589618
switch v := value.(type) {
@@ -595,13 +624,17 @@ func (c cmdable) JSONSetMode(ctx context.Context, key, path string, value interf
595624
bytes, err = json.Marshal(v)
596625
}
597626
args := []interface{}{"JSON.SET", key, path, util.BytesToString(bytes)}
598-
if mode != "" {
599-
switch strings.ToUpper(mode) {
600-
case "XX", "NX":
601-
args = append(args, strings.ToUpper(mode))
602-
603-
default:
604-
panic("redis: JSON.SET mode must be NX or XX")
627+
if options != nil {
628+
if options.Mode != "" {
629+
switch strings.ToUpper(options.Mode) {
630+
case "XX", "NX":
631+
args = append(args, strings.ToUpper(options.Mode))
632+
default:
633+
panic("redis: JSON.SET mode must be NX or XX")
634+
}
635+
}
636+
if options.FPHA != "" {
637+
args = append(args, "FPHA", string(options.FPHA))
605638
}
606639
}
607640
cmd := NewStatusCmd(ctx, args...)

json_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,55 @@ var _ = Describe("JSON Commands", Label("json"), func() {
262262
Expect(cmd.Val()).To(Equal("OK"))
263263
})
264264

265+
It("should JSONSetWithArgs with FPHA", Label("json.set", "json"), func() {
266+
SkipBeforeRedisVersion(8.8, "FPHA argument requires Redis 8.8+")
267+
268+
fpArray := `[1.1, 2.2, 3.3, 4.4]`
269+
for _, fpha := range []redis.FPHAType{
270+
redis.FPHATypeBF16,
271+
redis.FPHATypeFP16,
272+
redis.FPHATypeFP32,
273+
redis.FPHATypeFP64,
274+
} {
275+
key := "fpha_" + string(fpha)
276+
cmd := client.JSONSetWithArgs(ctx, key, "$", fpArray, &redis.JSONSetArgsOptions{FPHA: fpha})
277+
Expect(cmd.Err()).NotTo(HaveOccurred())
278+
Expect(cmd.Val()).To(Equal("OK"))
279+
280+
res, err := client.JSONGet(ctx, key, "$").Result()
281+
Expect(err).NotTo(HaveOccurred())
282+
Expect(res).NotTo(BeEmpty())
283+
}
284+
})
285+
286+
It("should JSONSetWithArgs with FPHA and NX/XX", Label("json.set", "json"), func() {
287+
SkipBeforeRedisVersion(8.8, "FPHA argument requires Redis 8.8+")
288+
289+
key := "fpha_nx"
290+
fpArray := `[1.5, 2.5, 3.5]`
291+
292+
// NX + FPHA succeeds when key does not exist
293+
res, err := client.JSONSetWithArgs(ctx, key, "$", fpArray, &redis.JSONSetArgsOptions{
294+
Mode: "NX", FPHA: redis.FPHATypeFP32,
295+
}).Result()
296+
Expect(err).NotTo(HaveOccurred())
297+
Expect(res).To(Equal("OK"))
298+
299+
// NX + FPHA returns redis.Nil when key already exists
300+
cmd := client.JSONSetWithArgs(ctx, key, "$", `[4.5, 5.5]`, &redis.JSONSetArgsOptions{
301+
Mode: "NX", FPHA: redis.FPHATypeFP32,
302+
})
303+
Expect(cmd.Err()).To(Equal(redis.Nil))
304+
Expect(cmd.Val()).To(Equal(""))
305+
306+
// XX + FPHA succeeds when key exists
307+
res, err = client.JSONSetWithArgs(ctx, key, "$", `[4.5, 5.5]`, &redis.JSONSetArgsOptions{
308+
Mode: "XX", FPHA: redis.FPHATypeFP32,
309+
}).Result()
310+
Expect(err).NotTo(HaveOccurred())
311+
Expect(res).To(Equal("OK"))
312+
})
313+
265314
It("should JSONGet", Label("json.get", "json", "NonRedisEnterprise"), func() {
266315
res, err := client.JSONSet(ctx, "get3", "$", `{"a": 1, "b": 2}`).Result()
267316
Expect(err).NotTo(HaveOccurred())

0 commit comments

Comments
 (0)