1 package redis
2
3 import (
4 "context"
5 "encoding/json"
6 "strings"
7
8 "github.com/redis/go-redis/v9/internal/proto"
9 "github.com/redis/go-redis/v9/internal/util"
10 )
11
12
13
14 type JSONCmdable interface {
15 JSONArrAppend(ctx context.Context, key, path string, values ...interface{}) *IntSliceCmd
16 JSONArrIndex(ctx context.Context, key, path string, value ...interface{}) *IntSliceCmd
17 JSONArrIndexWithArgs(ctx context.Context, key, path string, options *JSONArrIndexArgs, value ...interface{}) *IntSliceCmd
18 JSONArrInsert(ctx context.Context, key, path string, index int64, values ...interface{}) *IntSliceCmd
19 JSONArrLen(ctx context.Context, key, path string) *IntSliceCmd
20 JSONArrPop(ctx context.Context, key, path string, index int) *StringSliceCmd
21 JSONArrTrim(ctx context.Context, key, path string) *IntSliceCmd
22 JSONArrTrimWithArgs(ctx context.Context, key, path string, options *JSONArrTrimArgs) *IntSliceCmd
23 JSONClear(ctx context.Context, key, path string) *IntCmd
24 JSONDebugMemory(ctx context.Context, key, path string) *IntCmd
25 JSONDel(ctx context.Context, key, path string) *IntCmd
26 JSONForget(ctx context.Context, key, path string) *IntCmd
27 JSONGet(ctx context.Context, key string, paths ...string) *JSONCmd
28 JSONGetWithArgs(ctx context.Context, key string, options *JSONGetArgs, paths ...string) *JSONCmd
29 JSONMerge(ctx context.Context, key, path string, value string) *StatusCmd
30 JSONMSetArgs(ctx context.Context, docs []JSONSetArgs) *StatusCmd
31 JSONMSet(ctx context.Context, params ...interface{}) *StatusCmd
32 JSONMGet(ctx context.Context, path string, keys ...string) *JSONSliceCmd
33 JSONNumIncrBy(ctx context.Context, key, path string, value float64) *JSONCmd
34 JSONObjKeys(ctx context.Context, key, path string) *SliceCmd
35 JSONObjLen(ctx context.Context, key, path string) *IntPointerSliceCmd
36 JSONSet(ctx context.Context, key, path string, value interface{}) *StatusCmd
37 JSONSetMode(ctx context.Context, key, path string, value interface{}, mode string) *StatusCmd
38 JSONStrAppend(ctx context.Context, key, path, value string) *IntPointerSliceCmd
39 JSONStrLen(ctx context.Context, key, path string) *IntPointerSliceCmd
40 JSONToggle(ctx context.Context, key, path string) *IntPointerSliceCmd
41 JSONType(ctx context.Context, key, path string) *JSONSliceCmd
42 }
43
44 type JSONSetArgs struct {
45 Key string
46 Path string
47 Value interface{}
48 }
49
50 type JSONArrIndexArgs struct {
51 Start int
52 Stop *int
53 }
54
55 type JSONArrTrimArgs struct {
56 Start int
57 Stop *int
58 }
59
60 type JSONCmd struct {
61 baseCmd
62 val string
63 expanded interface{}
64 }
65
66 var _ Cmder = (*JSONCmd)(nil)
67
68 func newJSONCmd(ctx context.Context, args ...interface{}) *JSONCmd {
69 return &JSONCmd{
70 baseCmd: baseCmd{
71 ctx: ctx,
72 args: args,
73 },
74 }
75 }
76
77 func (cmd *JSONCmd) String() string {
78 return cmdString(cmd, cmd.val)
79 }
80
81 func (cmd *JSONCmd) SetVal(val string) {
82 cmd.val = val
83 }
84
85
86 func (cmd *JSONCmd) Val() string {
87 if len(cmd.val) == 0 && cmd.expanded != nil {
88 val, err := json.Marshal(cmd.expanded)
89 if err != nil {
90 cmd.SetErr(err)
91 return ""
92 }
93 return string(val)
94
95 } else {
96 return cmd.val
97 }
98 }
99
100 func (cmd *JSONCmd) Result() (string, error) {
101 return cmd.Val(), cmd.Err()
102 }
103
104
105 func (cmd *JSONCmd) Expanded() (interface{}, error) {
106 if len(cmd.val) != 0 && cmd.expanded == nil {
107 err := json.Unmarshal([]byte(cmd.val), &cmd.expanded)
108 if err != nil {
109 return nil, err
110 }
111 }
112
113 return cmd.expanded, nil
114 }
115
116 func (cmd *JSONCmd) readReply(rd *proto.Reader) error {
117
118
119 if cmd.baseCmd.Err() == Nil {
120 cmd.val = ""
121 return Nil
122 }
123
124
125 if cmd.baseCmd.Err() != nil {
126 return cmd.baseCmd.Err()
127 }
128
129 if readType, err := rd.PeekReplyType(); err != nil {
130 return err
131 } else if readType == proto.RespArray {
132
133 size, err := rd.ReadArrayLen()
134 if err != nil {
135 return err
136 }
137
138
139
140 if size == 0 {
141 cmd.val = "[]"
142 return nil
143 }
144
145 expanded := make([]interface{}, size)
146
147 for i := 0; i < size; i++ {
148 if expanded[i], err = rd.ReadReply(); err != nil {
149 return err
150 }
151 }
152 cmd.expanded = expanded
153
154 } else {
155 if str, err := rd.ReadString(); err != nil && err != Nil {
156 return err
157 } else if str == "" || err == Nil {
158 cmd.val = ""
159 return Nil
160 } else {
161 cmd.val = str
162 }
163 }
164
165 return nil
166 }
167
168
169
170 type JSONSliceCmd struct {
171 baseCmd
172 val []interface{}
173 }
174
175 func NewJSONSliceCmd(ctx context.Context, args ...interface{}) *JSONSliceCmd {
176 return &JSONSliceCmd{
177 baseCmd: baseCmd{
178 ctx: ctx,
179 args: args,
180 },
181 }
182 }
183
184 func (cmd *JSONSliceCmd) String() string {
185 return cmdString(cmd, cmd.val)
186 }
187
188 func (cmd *JSONSliceCmd) SetVal(val []interface{}) {
189 cmd.val = val
190 }
191
192 func (cmd *JSONSliceCmd) Val() []interface{} {
193 return cmd.val
194 }
195
196 func (cmd *JSONSliceCmd) Result() ([]interface{}, error) {
197 return cmd.val, cmd.err
198 }
199
200 func (cmd *JSONSliceCmd) readReply(rd *proto.Reader) error {
201 if cmd.baseCmd.Err() == Nil {
202 cmd.val = nil
203 return Nil
204 }
205
206 if readType, err := rd.PeekReplyType(); err != nil {
207 return err
208 } else if readType == proto.RespArray {
209 response, err := rd.ReadReply()
210 if err != nil {
211 return nil
212 } else {
213 cmd.val = response.([]interface{})
214 }
215
216 } else {
217 n, err := rd.ReadArrayLen()
218 if err != nil {
219 return err
220 }
221 cmd.val = make([]interface{}, n)
222 for i := 0; i < len(cmd.val); i++ {
223 switch s, err := rd.ReadString(); {
224 case err == Nil:
225 cmd.val[i] = ""
226 case err != nil:
227 return err
228 default:
229 cmd.val[i] = s
230 }
231 }
232 }
233 return nil
234 }
235
236
242
243 type IntPointerSliceCmd struct {
244 baseCmd
245 val []*int64
246 }
247
248
249 func NewIntPointerSliceCmd(ctx context.Context, args ...interface{}) *IntPointerSliceCmd {
250 return &IntPointerSliceCmd{
251 baseCmd: baseCmd{
252 ctx: ctx,
253 args: args,
254 },
255 }
256 }
257
258 func (cmd *IntPointerSliceCmd) String() string {
259 return cmdString(cmd, cmd.val)
260 }
261
262 func (cmd *IntPointerSliceCmd) SetVal(val []*int64) {
263 cmd.val = val
264 }
265
266 func (cmd *IntPointerSliceCmd) Val() []*int64 {
267 return cmd.val
268 }
269
270 func (cmd *IntPointerSliceCmd) Result() ([]*int64, error) {
271 return cmd.val, cmd.err
272 }
273
274 func (cmd *IntPointerSliceCmd) readReply(rd *proto.Reader) error {
275 n, err := rd.ReadArrayLen()
276 if err != nil {
277 return err
278 }
279 cmd.val = make([]*int64, n)
280
281 for i := 0; i < len(cmd.val); i++ {
282 val, err := rd.ReadInt()
283 if err != nil && err != Nil {
284 return err
285 } else if err != Nil {
286 cmd.val[i] = &val
287 }
288 }
289
290 return nil
291 }
292
293
294
295
296
297 func (c cmdable) JSONArrAppend(ctx context.Context, key, path string, values ...interface{}) *IntSliceCmd {
298 args := []interface{}{"JSON.ARRAPPEND", key, path}
299 args = append(args, values...)
300 cmd := NewIntSliceCmd(ctx, args...)
301 _ = c(ctx, cmd)
302 return cmd
303 }
304
305
306
307 func (c cmdable) JSONArrIndex(ctx context.Context, key, path string, value ...interface{}) *IntSliceCmd {
308 args := []interface{}{"JSON.ARRINDEX", key, path}
309 args = append(args, value...)
310 cmd := NewIntSliceCmd(ctx, args...)
311 _ = c(ctx, cmd)
312 return cmd
313 }
314
315
316
317
318 func (c cmdable) JSONArrIndexWithArgs(ctx context.Context, key, path string, options *JSONArrIndexArgs, value ...interface{}) *IntSliceCmd {
319 args := []interface{}{"JSON.ARRINDEX", key, path}
320 args = append(args, value...)
321
322 if options != nil {
323 args = append(args, options.Start)
324 if options.Stop != nil {
325 args = append(args, *options.Stop)
326 }
327 }
328 cmd := NewIntSliceCmd(ctx, args...)
329 _ = c(ctx, cmd)
330 return cmd
331 }
332
333
334
335 func (c cmdable) JSONArrInsert(ctx context.Context, key, path string, index int64, values ...interface{}) *IntSliceCmd {
336 args := []interface{}{"JSON.ARRINSERT", key, path, index}
337 args = append(args, values...)
338 cmd := NewIntSliceCmd(ctx, args...)
339 _ = c(ctx, cmd)
340 return cmd
341 }
342
343
344
345 func (c cmdable) JSONArrLen(ctx context.Context, key, path string) *IntSliceCmd {
346 args := []interface{}{"JSON.ARRLEN", key, path}
347 cmd := NewIntSliceCmd(ctx, args...)
348 _ = c(ctx, cmd)
349 return cmd
350 }
351
352
353
354 func (c cmdable) JSONArrPop(ctx context.Context, key, path string, index int) *StringSliceCmd {
355 args := []interface{}{"JSON.ARRPOP", key, path, index}
356 cmd := NewStringSliceCmd(ctx, args...)
357 _ = c(ctx, cmd)
358 return cmd
359 }
360
361
362
363 func (c cmdable) JSONArrTrim(ctx context.Context, key, path string) *IntSliceCmd {
364 args := []interface{}{"JSON.ARRTRIM", key, path}
365 cmd := NewIntSliceCmd(ctx, args...)
366 _ = c(ctx, cmd)
367 return cmd
368 }
369
370
371
372 func (c cmdable) JSONArrTrimWithArgs(ctx context.Context, key, path string, options *JSONArrTrimArgs) *IntSliceCmd {
373 args := []interface{}{"JSON.ARRTRIM", key, path}
374
375 if options != nil {
376 args = append(args, options.Start)
377
378 if options.Stop != nil {
379 args = append(args, *options.Stop)
380 }
381 }
382 cmd := NewIntSliceCmd(ctx, args...)
383 _ = c(ctx, cmd)
384 return cmd
385 }
386
387
388
389 func (c cmdable) JSONClear(ctx context.Context, key, path string) *IntCmd {
390 args := []interface{}{"JSON.CLEAR", key, path}
391 cmd := NewIntCmd(ctx, args...)
392 _ = c(ctx, cmd)
393 return cmd
394 }
395
396
397
398 func (c cmdable) JSONDebugMemory(ctx context.Context, key, path string) *IntCmd {
399 panic("not implemented")
400 }
401
402
403
404 func (c cmdable) JSONDel(ctx context.Context, key, path string) *IntCmd {
405 args := []interface{}{"JSON.DEL", key, path}
406 cmd := NewIntCmd(ctx, args...)
407 _ = c(ctx, cmd)
408 return cmd
409 }
410
411
412
413 func (c cmdable) JSONForget(ctx context.Context, key, path string) *IntCmd {
414 args := []interface{}{"JSON.FORGET", key, path}
415 cmd := NewIntCmd(ctx, args...)
416 _ = c(ctx, cmd)
417 return cmd
418 }
419
420
421
422
423
424 func (c cmdable) JSONGet(ctx context.Context, key string, paths ...string) *JSONCmd {
425 args := make([]interface{}, len(paths)+2)
426 args[0] = "JSON.GET"
427 args[1] = key
428 for n, path := range paths {
429 args[n+2] = path
430 }
431 cmd := newJSONCmd(ctx, args...)
432 _ = c(ctx, cmd)
433 return cmd
434 }
435
436 type JSONGetArgs struct {
437 Indent string
438 Newline string
439 Space string
440 }
441
442
443
444
445
446 func (c cmdable) JSONGetWithArgs(ctx context.Context, key string, options *JSONGetArgs, paths ...string) *JSONCmd {
447 args := []interface{}{"JSON.GET", key}
448 if options != nil {
449 if options.Indent != "" {
450 args = append(args, "INDENT", options.Indent)
451 }
452 if options.Newline != "" {
453 args = append(args, "NEWLINE", options.Newline)
454 }
455 if options.Space != "" {
456 args = append(args, "SPACE", options.Space)
457 }
458 for _, path := range paths {
459 args = append(args, path)
460 }
461 }
462 cmd := newJSONCmd(ctx, args...)
463 _ = c(ctx, cmd)
464 return cmd
465 }
466
467
468
469 func (c cmdable) JSONMerge(ctx context.Context, key, path string, value string) *StatusCmd {
470 args := []interface{}{"JSON.MERGE", key, path, value}
471 cmd := NewStatusCmd(ctx, args...)
472 _ = c(ctx, cmd)
473 return cmd
474 }
475
476
477
478
479
480 func (c cmdable) JSONMGet(ctx context.Context, path string, keys ...string) *JSONSliceCmd {
481 args := make([]interface{}, len(keys)+1)
482 args[0] = "JSON.MGET"
483 for n, key := range keys {
484 args[n+1] = key
485 }
486 args = append(args, path)
487 cmd := NewJSONSliceCmd(ctx, args...)
488 _ = c(ctx, cmd)
489 return cmd
490 }
491
492
493
494 func (c cmdable) JSONMSetArgs(ctx context.Context, docs []JSONSetArgs) *StatusCmd {
495 args := []interface{}{"JSON.MSET"}
496 for _, doc := range docs {
497 args = append(args, doc.Key, doc.Path, doc.Value)
498 }
499 cmd := NewStatusCmd(ctx, args...)
500 _ = c(ctx, cmd)
501 return cmd
502 }
503
504 func (c cmdable) JSONMSet(ctx context.Context, params ...interface{}) *StatusCmd {
505 args := []interface{}{"JSON.MSET"}
506 args = append(args, params...)
507 cmd := NewStatusCmd(ctx, args...)
508 _ = c(ctx, cmd)
509 return cmd
510 }
511
512
513
514 func (c cmdable) JSONNumIncrBy(ctx context.Context, key, path string, value float64) *JSONCmd {
515 args := []interface{}{"JSON.NUMINCRBY", key, path, value}
516 cmd := newJSONCmd(ctx, args...)
517 _ = c(ctx, cmd)
518 return cmd
519 }
520
521
522
523 func (c cmdable) JSONObjKeys(ctx context.Context, key, path string) *SliceCmd {
524 args := []interface{}{"JSON.OBJKEYS", key, path}
525 cmd := NewSliceCmd(ctx, args...)
526 _ = c(ctx, cmd)
527 return cmd
528 }
529
530
531
532 func (c cmdable) JSONObjLen(ctx context.Context, key, path string) *IntPointerSliceCmd {
533 args := []interface{}{"JSON.OBJLEN", key, path}
534 cmd := NewIntPointerSliceCmd(ctx, args...)
535 _ = c(ctx, cmd)
536 return cmd
537 }
538
539
540
541
542
543 func (c cmdable) JSONSet(ctx context.Context, key, path string, value interface{}) *StatusCmd {
544 return c.JSONSetMode(ctx, key, path, value, "")
545 }
546
547
548
549
550
551 func (c cmdable) JSONSetMode(ctx context.Context, key, path string, value interface{}, mode string) *StatusCmd {
552 var bytes []byte
553 var err error
554 switch v := value.(type) {
555 case string:
556 bytes = []byte(v)
557 case []byte:
558 bytes = v
559 default:
560 bytes, err = json.Marshal(v)
561 }
562 args := []interface{}{"JSON.SET", key, path, util.BytesToString(bytes)}
563 if mode != "" {
564 switch strings.ToUpper(mode) {
565 case "XX", "NX":
566 args = append(args, strings.ToUpper(mode))
567
568 default:
569 panic("redis: JSON.SET mode must be NX or XX")
570 }
571 }
572 cmd := NewStatusCmd(ctx, args...)
573 if err != nil {
574 cmd.SetErr(err)
575 } else {
576 _ = c(ctx, cmd)
577 }
578 return cmd
579 }
580
581
582
583 func (c cmdable) JSONStrAppend(ctx context.Context, key, path, value string) *IntPointerSliceCmd {
584 args := []interface{}{"JSON.STRAPPEND", key, path, value}
585 cmd := NewIntPointerSliceCmd(ctx, args...)
586 _ = c(ctx, cmd)
587 return cmd
588 }
589
590
591
592 func (c cmdable) JSONStrLen(ctx context.Context, key, path string) *IntPointerSliceCmd {
593 args := []interface{}{"JSON.STRLEN", key, path}
594 cmd := NewIntPointerSliceCmd(ctx, args...)
595 _ = c(ctx, cmd)
596 return cmd
597 }
598
599
600
601 func (c cmdable) JSONToggle(ctx context.Context, key, path string) *IntPointerSliceCmd {
602 args := []interface{}{"JSON.TOGGLE", key, path}
603 cmd := NewIntPointerSliceCmd(ctx, args...)
604 _ = c(ctx, cmd)
605 return cmd
606 }
607
608
609
610 func (c cmdable) JSONType(ctx context.Context, key, path string) *JSONSliceCmd {
611 args := []interface{}{"JSON.TYPE", key, path}
612 cmd := NewJSONSliceCmd(ctx, args...)
613 _ = c(ctx, cmd)
614 return cmd
615 }
616
View as plain text