rustis/commands/json_commands.rs
1use crate::{
2 client::{prepare_command, PreparedCommand},
3 commands::SetCondition,
4 resp::{
5 cmd, CollectionResponse, CommandArgs, PrimitiveResponse, SingleArg, SingleArgCollection,
6 ToArgs, Value,
7 },
8};
9use serde::de::DeserializeOwned;
10
11/// A group of Redis commands related to [`RedisJson`](https://redis.io/docs/stack/json/)
12///
13/// # See Also
14/// [RedisJson Commands](https://redis.io/commands/?group=json)
15pub trait JsonCommands<'a> {
16 /// Append the json `values` into the array at `path` after the last element in it
17 ///
18 /// # Arguments
19 /// * `key` - The key to modify.
20 /// * `path`- The JSONPath to specify.
21 /// * `values` - one or more values to append to one or more arrays.
22 ///
23 /// # Return
24 /// A collection of integer replies for each path, the array's new size,
25 /// or nil, if the matching JSON value is not an array.
26 ///
27 /// # See Also
28 /// [<https://redis.io/commands/json.arrappend/>](https://redis.io/commands/json.arrappend/)
29 #[must_use]
30 fn json_arrappend<K, P, V, VV, R>(
31 self,
32 key: K,
33 path: P,
34 values: VV,
35 ) -> PreparedCommand<'a, Self, R>
36 where
37 Self: Sized,
38 K: SingleArg,
39 P: SingleArg,
40 V: SingleArg,
41 VV: SingleArgCollection<V>,
42 R: CollectionResponse<Option<usize>>,
43 {
44 prepare_command(self, cmd("JSON.ARRAPPEND").arg(key).arg(path).arg(values))
45 }
46
47 /// Search for the first occurrence of a scalar JSON value in an array
48 ///
49 /// # Arguments
50 /// * `key` - The key to parse.
51 /// * `path`- The JSONPath to specify.
52 /// * `value` - value index to find in one or more arrays.
53 ///
54 /// # Return
55 /// A collection of integer replies for each path,
56 ///
57 /// the first position in the array of each JSON value that matches the path,
58 /// -1 if unfound in the array, or nil, if the matching JSON value is not an array.
59 ///
60 /// # See Also
61 /// [<https://redis.io/commands/json.arrindex/>](https://redis.io/commands/json.arrindex/)
62 #[must_use]
63 fn json_arrindex<K, P, V, R>(
64 self,
65 key: K,
66 path: P,
67 value: V,
68 options: JsonArrIndexOptions,
69 ) -> PreparedCommand<'a, Self, R>
70 where
71 Self: Sized,
72 K: SingleArg,
73 P: SingleArg,
74 V: SingleArg,
75 R: CollectionResponse<Option<isize>>,
76 {
77 prepare_command(
78 self,
79 cmd("JSON.ARRINDEX")
80 .arg(key)
81 .arg(path)
82 .arg(value)
83 .arg(options),
84 )
85 }
86
87 /// Insert the json `values` into the array at `path` before the `index` (shifts to the right)
88 ///
89 /// # Arguments
90 /// * `key` - The key to modify.
91 /// * `path`- The JSONPath to specify.
92 /// * `index`- The position in the array where you want to insert a value.
93 /// The index must be in the array's range.
94 /// Inserting at index 0 prepends to the array.
95 /// Negative index values start from the end of the array.
96 /// * `values` - one or more values to insert in one or more arrays.
97 ///
98 /// # Return
99 /// A collection of integer replies for each path,
100 /// the array's new size, or nil,
101 /// if the matching JSON value is not an array.
102 ///
103 /// # See Also
104 /// [<https://redis.io/commands/json.arrinsert/>](https://redis.io/commands/json.arrinsert/)
105 #[must_use]
106 fn json_arrinsert<K, P, V, VV, R>(
107 self,
108 key: K,
109 path: P,
110 index: isize,
111 values: VV,
112 ) -> PreparedCommand<'a, Self, R>
113 where
114 Self: Sized,
115 K: SingleArg,
116 P: SingleArg,
117 V: SingleArg,
118 VV: SingleArgCollection<V>,
119 R: CollectionResponse<Option<usize>>,
120 {
121 prepare_command(
122 self,
123 cmd("JSON.ARRINSERT")
124 .arg(key)
125 .arg(path)
126 .arg(index)
127 .arg(values),
128 )
129 }
130
131 /// Report the length of the JSON array at `path` in `key`
132 ///
133 /// # Arguments
134 /// * `key` - The key to parse.
135 /// * `path`- The JSONPath to specify.
136 ///
137 /// # Return
138 /// A collection of integer replies, an integer for each matching value,
139 /// each is the array's length, or nil, if the matching value is not an array.
140 ///
141 /// # See Also
142 /// [<https://redis.io/commands/json.arrlen/>](https://redis.io/commands/json.arrlen/)
143 #[must_use]
144 fn json_arrlen<K, P, R>(self, key: K, path: P) -> PreparedCommand<'a, Self, R>
145 where
146 Self: Sized,
147 K: SingleArg,
148 P: SingleArg,
149 R: CollectionResponse<Option<usize>>,
150 {
151 prepare_command(self, cmd("JSON.ARRLEN").arg(key).arg(path))
152 }
153
154 /// Remove and return an element from the index in the array
155 ///
156 /// # Arguments
157 /// * `key` - The key to modify.
158 /// * `path`- The JSONPath to specify.
159 /// * `index`- is position in the array to start popping from.
160 /// Default is -1, meaning the last element.
161 /// Out-of-range indexes round to their respective array ends.
162 /// Popping an empty array returns null.
163 ///
164 /// # Return
165 /// A collection of bulk string replies for each path, each reply is the popped JSON value,
166 /// or nil, if the matching JSON value is not an array.
167 ///
168 /// # See Also
169 /// [<https://redis.io/commands/json.arrpop/>](https://redis.io/commands/json.arrpop/)
170 #[must_use]
171 fn json_arrpop<K, P, R, RR>(
172 self,
173 key: K,
174 path: P,
175 index: isize,
176 ) -> PreparedCommand<'a, Self, RR>
177 where
178 Self: Sized,
179 K: SingleArg,
180 P: SingleArg,
181 R: PrimitiveResponse + DeserializeOwned,
182 RR: CollectionResponse<R>,
183 {
184 prepare_command(self, cmd("JSON.ARRPOP").arg(key).arg(path).arg(index))
185 }
186
187 /// Remove and return an element from the index in the array
188 ///
189 /// # Arguments
190 /// * `key` - The key to modify.
191 /// * `path`- The JSONPath to specify.
192 /// * `start`- The index of the first element to keep (previous elements are trimmed).
193 /// * `stop` - the index of the last element to keep (following elements are trimmed), including the last element.
194 /// Negative values are interpreted as starting from the end.
195 ///
196 /// # Return
197 /// A collection of integer replies for each path, the array's new size,
198 /// or nil, if the matching JSON value is not an array.
199 ///
200 /// # See Also
201 /// [<https://redis.io/commands/json.arrtrim/>](https://redis.io/commands/json.arrtrim/)
202 #[must_use]
203 fn json_arrtrim<K, P, R>(
204 self,
205 key: K,
206 path: P,
207 start: isize,
208 stop: isize,
209 ) -> PreparedCommand<'a, Self, R>
210 where
211 Self: Sized,
212 K: SingleArg,
213 P: SingleArg,
214 R: CollectionResponse<Option<usize>>,
215 {
216 prepare_command(
217 self,
218 cmd("JSON.ARRTRIM").arg(key).arg(path).arg(start).arg(stop),
219 )
220 }
221
222 /// Clear container values (arrays/objects) and set numeric values to 0
223 ///
224 /// # Arguments
225 /// * `key` - The key to modify.
226 /// * `path`- The JSONPath to specify.
227 ///
228 /// # Return
229 /// The number of values cleared.
230 ///
231 /// # See Also
232 /// [<https://redis.io/commands/json.clear/>](https://redis.io/commands/json.clear/)
233 #[must_use]
234 fn json_clear<K, P>(self, key: K, path: P) -> PreparedCommand<'a, Self, usize>
235 where
236 Self: Sized,
237 K: SingleArg,
238 P: SingleArg,
239 {
240 prepare_command(self, cmd("JSON.CLEAR").arg(key).arg(path))
241 }
242
243 /// Report a value's memory usage in bytes
244 ///
245 /// # Arguments
246 /// * `key` - The key to modify.
247 /// * `path`- The JSONPath to specify.
248 ///
249 /// # Return
250 /// A collection of integer replies for each path, the value size in bytes
251 ///
252 /// # See Also
253 /// [<https://redis.io/commands/json.debug-memory/>](https://redis.io/commands/json.debug-memory/)
254 #[must_use]
255 fn json_debug_memory<K, P, R>(self, key: K, path: P) -> PreparedCommand<'a, Self, R>
256 where
257 Self: Sized,
258 K: SingleArg,
259 P: SingleArg,
260 R: CollectionResponse<usize>,
261 {
262 prepare_command(self, cmd("JSON.DEBUG").arg("MEMORY").arg(key).arg(path))
263 }
264
265 /// Delete a value
266 ///
267 /// # Arguments
268 /// * `key` - The key to modify.
269 /// * `path`- The JSONPath to specify.
270 ///
271 /// # Return
272 /// The number of paths deleted (0 or more).
273 ///
274 /// # See Also
275 /// [<https://redis.io/commands/json.del/>](https://redis.io/commands/json.del/)
276 #[must_use]
277 fn json_del<K, P>(self, key: K, path: P) -> PreparedCommand<'a, Self, usize>
278 where
279 Self: Sized,
280 K: SingleArg,
281 P: SingleArg,
282 {
283 prepare_command(self, cmd("JSON.DEL").arg(key).arg(path))
284 }
285
286 /// See [`json_del`](JsonCommands::json_del)
287 ///
288 /// # Arguments
289 /// * `key` - The key to modify.
290 /// * `path`- The JSONPath to specify.
291 ///
292 /// # Return
293 /// The number of paths deleted (0 or more).
294 ///
295 /// # See Also
296 /// [<https://redis.io/commands/json.forget/>](https://redis.io/commands/json.forget/)
297 #[must_use]
298 fn json_forget<K, P>(self, key: K, path: P) -> PreparedCommand<'a, Self, usize>
299 where
300 Self: Sized,
301 K: SingleArg,
302 P: SingleArg,
303 {
304 prepare_command(self, cmd("JSON.FORGET").arg(key).arg(path))
305 }
306
307 /// Return the value at path in JSON serialized form
308 ///
309 /// # Arguments
310 /// * `key` - The key to parse.
311 /// * `options`- See [`JsonOptions`](JsonGetOptions)
312 ///
313 /// # Return
314 /// A collection of bulk string replies. Each string is the JSON serialization of each JSON value that matches a path
315 ///
316 /// # See Also
317 /// [<https://redis.io/commands/json.get/>](https://redis.io/commands/json.get/)
318 #[must_use]
319 fn json_get<K, V>(self, key: K, options: JsonGetOptions) -> PreparedCommand<'a, Self, V>
320 where
321 Self: Sized,
322 K: SingleArg,
323 V: PrimitiveResponse,
324 {
325 prepare_command(self, cmd("JSON.GET").arg(key).arg(options))
326 }
327
328 /// Return the values at `path` from multiple `key` arguments
329 ///
330 /// # Arguments
331 /// * `key` - The key to parse.
332 /// * `options`- See [`JsonOptions`](JsonGetOptions)
333 ///
334 /// # Return
335 /// A collection of bulk string replies specified as the JSON serialization of the value at each key's path.
336 ///
337 /// # See Also
338 /// [<https://redis.io/commands/json.mget/>](https://redis.io/commands/json.mget/)
339 #[must_use]
340 fn json_mget<K, KK, P, V, VV>(self, keys: KK, path: P) -> PreparedCommand<'a, Self, VV>
341 where
342 Self: Sized,
343 K: SingleArg,
344 KK: SingleArgCollection<K>,
345 P: SingleArg,
346 V: PrimitiveResponse + DeserializeOwned,
347 VV: CollectionResponse<V>,
348 {
349 prepare_command(self, cmd("JSON.MGET").arg(keys).arg(path))
350 }
351
352 /// Increment the number value stored at path by number
353 ///
354 /// # Arguments
355 /// * `key` - The key to modify.
356 /// * `path`- The JSONPath to specify.
357 /// * `value` - number value to increment.
358 ///
359 /// # Return
360 /// A bulk string reply specified as a stringified new value for each path,
361 /// or nil, if the matching JSON value is not a number.
362 ///
363 /// # See Also
364 /// [<https://redis.io/commands/json.numincrby/>](https://redis.io/commands/json.numincrby/)
365 #[must_use]
366 fn json_numincrby<K, P, V, R>(self, key: K, path: P, value: V) -> PreparedCommand<'a, Self, R>
367 where
368 Self: Sized,
369 K: SingleArg,
370 P: SingleArg,
371 V: SingleArg,
372 R: PrimitiveResponse,
373 {
374 prepare_command(self, cmd("JSON.NUMINCRBY").arg(key).arg(path).arg(value))
375 }
376
377 /// Multiply the number value stored at path by number
378 ///
379 /// # Arguments
380 /// * `key` - The key to modify.
381 /// * `path`- The JSONPath to specify.
382 /// * `value` - number value to increment.
383 ///
384 /// # Return
385 /// A bulk string reply specified as a stringified new value for each path,
386 /// or nil, if the matching JSON value is not a number.
387 ///
388 /// # See Also
389 /// [<https://redis.io/commands/json.nummultby/>](https://redis.io/commands/json.nummultby/)
390 #[must_use]
391 fn json_nummultby<K, P, V, R>(self, key: K, path: P, value: V) -> PreparedCommand<'a, Self, R>
392 where
393 Self: Sized,
394 K: SingleArg,
395 P: SingleArg,
396 V: SingleArg,
397 R: PrimitiveResponse,
398 {
399 prepare_command(self, cmd("JSON.NUMMULTBY").arg(key).arg(path).arg(value))
400 }
401
402 /// Return the keys in the object that's referenced by `path`
403 ///
404 /// # Arguments
405 /// * `key` - The key to parse.
406 /// * `path`- The JSONPath to specify.
407 ///
408 /// # Return
409 /// A collection of collection replies for each path,
410 /// a collection of the key names in the object as a bulk string reply,
411 /// or an empty collection if the matching JSON value is not an object.
412 ///
413 /// # See Also
414 /// [<https://redis.io/commands/json.objkeys/>](https://redis.io/commands/json.objkeys/)
415 #[must_use]
416 fn json_objkeys<K, P, R, RR>(self, key: K, path: P) -> PreparedCommand<'a, Self, RR>
417 where
418 Self: Sized,
419 K: SingleArg,
420 P: SingleArg,
421 R: PrimitiveResponse + DeserializeOwned,
422 RR: CollectionResponse<Vec<R>>,
423 {
424 prepare_command(self, cmd("JSON.OBJKEYS").arg(key).arg(path))
425 }
426
427 /// Report the number of keys in the JSON object at `path` in `key`
428 ///
429 /// # Arguments
430 /// * `key` - The key to parse.
431 /// * `path`- The JSONPath to specify.
432 ///
433 /// # Return
434 /// A collection of integer replies for each path specified as the number of keys in the object or nil,
435 /// if the matching JSON value is not an object.
436 ///
437 /// # See Also
438 /// [<https://redis.io/commands/json.objlen/>](https://redis.io/commands/json.objlen/)
439 #[must_use]
440 fn json_objlen<K, P, R>(self, key: K, path: P) -> PreparedCommand<'a, Self, R>
441 where
442 Self: Sized,
443 K: SingleArg,
444 P: SingleArg,
445 R: CollectionResponse<Option<usize>>,
446 {
447 prepare_command(self, cmd("JSON.OBJLEN").arg(key).arg(path))
448 }
449
450 /// Return the JSON in key in
451 /// [`Redis serialization protocol specification`](https://redis.io/docs/reference/protocol-spec) form
452 ///
453 /// # Arguments
454 /// * `key` - The key to parse.
455 /// * `path`- The JSONPath to specify.
456 ///
457 /// # Return
458 /// A collection of [`Values`](crate::resp::Value)
459 ///
460 /// This command uses the following mapping from JSON to RESP:
461 /// * JSON `null` maps to the bulk string reply.
462 /// * JSON `false` and `true` values map to the simple string reply.
463 /// * JSON number maps to the integer reply or bulk string reply, depending on type.
464 /// * JSON string maps to the bulk string reply.
465 /// * JSON array is represented as an array reply in which the first element is the simple string reply `[`, followed by the array's elements.
466 /// * JSON object is represented as an array reply in which the first element is the simple string reply `{`.
467 /// Each successive entry represents a key-value pair as a two-entry array reply of the bulk string reply.
468 ///
469 /// # See Also
470 /// [<https://redis.io/commands/json.resp/>](https://redis.io/commands/json.resp/)
471 #[must_use]
472 fn json_resp<K, P, VV>(self, key: K, path: P) -> PreparedCommand<'a, Self, VV>
473 where
474 Self: Sized,
475 K: SingleArg,
476 P: SingleArg,
477 VV: CollectionResponse<Value>,
478 {
479 prepare_command(self, cmd("JSON.RESP").arg(key).arg(path))
480 }
481
482 /// Set the JSON value at `path` in `key`
483 ///
484 /// # Arguments
485 /// * `key` - The key to modify.
486 /// * `path` - JSONPath to specify.\
487 /// For new Redis keys the path must be the root.\
488 /// For existing keys, when the entire path exists, the value that it contains is replaced with the json value.\
489 /// For existing keys, when the path exists, except for the last element, a new child is added with the json value.
490 /// * `value`- The value to set at the specified path
491 /// * `condition`- See [`SetCondition`](crate::commands::SetCondition)
492 ///
493 /// # See Also
494 /// [<https://redis.io/commands/json.set/>](https://redis.io/commands/json.set/)
495 #[must_use]
496 fn json_set<K, P, V>(
497 self,
498 key: K,
499 path: P,
500 value: V,
501 condition: SetCondition,
502 ) -> PreparedCommand<'a, Self, ()>
503 where
504 Self: Sized,
505 K: SingleArg,
506 P: SingleArg,
507 V: SingleArg,
508 {
509 prepare_command(
510 self,
511 cmd("JSON.SET").arg(key).arg(path).arg(value).arg(condition),
512 )
513 }
514
515 /// Append the json-string values to the string at path
516 ///
517 /// # Arguments
518 /// * `key` - The key to modify.
519 /// * `path`- The JSONPath to specify.
520 /// * `value` - number value to increment.
521 ///
522 /// # Return
523 /// A collection of integer replies for each path, the string's new length, or nil, if the matching JSON value is not a string.
524 ///
525 /// # See Also
526 /// [<https://redis.io/commands/json.strappend/>](https://redis.io/commands/json.strappend/)
527 #[must_use]
528 fn json_strappend<K, P, V, R>(self, key: K, path: P, value: V) -> PreparedCommand<'a, Self, R>
529 where
530 Self: Sized,
531 K: SingleArg,
532 P: SingleArg,
533 V: SingleArg,
534 R: CollectionResponse<Option<usize>>,
535 {
536 prepare_command(self, cmd("JSON.STRAPPEND").arg(key).arg(path).arg(value))
537 }
538
539 /// Report the length of the JSON String at `path` in `key`
540 ///
541 /// # Arguments
542 /// * `key` - The key to parse.
543 /// * `path`- The JSONPath to specify.
544 ///
545 /// # Return
546 /// returns by recursive descent a collection of integer replies for each path,
547 /// the array's length, or nil, if the matching JSON value is not a string.
548 ///
549 /// # See Also
550 /// [<https://redis.io/commands/json.strlen/>](https://redis.io/commands/json.strlen/)
551 #[must_use]
552 fn json_strlen<K, P, R>(self, key: K, path: P) -> PreparedCommand<'a, Self, R>
553 where
554 Self: Sized,
555 K: SingleArg,
556 P: SingleArg,
557 R: CollectionResponse<Option<usize>>,
558 {
559 prepare_command(self, cmd("JSON.STRLEN").arg(key).arg(path))
560 }
561
562 /// Toggle a Boolean value stored at `path`
563 ///
564 /// # Arguments
565 /// * `key` - The key to modify.
566 /// * `path`- The JSONPath to specify.
567 ///
568 /// # Return
569 /// A collection of integer replies for each path, the new value (0 if false or 1 if true),
570 /// or nil for JSON values matching the path that are not Boolean.
571 ///
572 /// # See Also
573 /// [<https://redis.io/commands/json.toggle/>](https://redis.io/commands/json.toggle/)
574 #[must_use]
575 fn json_toggle<K, P, R>(self, key: K, path: P) -> PreparedCommand<'a, Self, R>
576 where
577 Self: Sized,
578 K: SingleArg,
579 P: SingleArg,
580 R: CollectionResponse<Option<usize>>,
581 {
582 prepare_command(self, cmd("JSON.TOGGLE").arg(key).arg(path))
583 }
584
585 /// Report the type of JSON value at `path`
586 ///
587 /// # Arguments
588 /// * `key` - The key to parse.
589 /// * `path`- The JSONPath to specify.
590 ///
591 /// # Return
592 /// A collection of string replies for each path, specified as the value's type.
593 ///
594 /// # See Also
595 /// [<https://redis.io/commands/json.type/>](https://redis.io/commands/json.type/)
596 #[must_use]
597 fn json_type<K, P, R, RR>(self, key: K, path: P) -> PreparedCommand<'a, Self, RR>
598 where
599 Self: Sized,
600 K: SingleArg,
601 P: SingleArg,
602 R: PrimitiveResponse + DeserializeOwned,
603 RR: CollectionResponse<R>,
604 {
605 prepare_command(self, cmd("JSON.TYPE").arg(key).arg(path))
606 }
607}
608
609/// Options for the [`json_get`](JsonCommands::json_get) command
610#[derive(Default)]
611pub struct JsonGetOptions {
612 command_args: CommandArgs,
613}
614
615impl JsonGetOptions {
616 /// Sets the indentation string for nested levels.
617 #[must_use]
618 pub fn indent<I: SingleArg>(mut self, indent: I) -> Self {
619 Self {
620 command_args: self.command_args.arg("INDENT").arg(indent).build(),
621 }
622 }
623
624 /// Sets the string that's printed at the end of each line.
625 #[must_use]
626 pub fn newline<NL: SingleArg>(mut self, newline: NL) -> Self {
627 Self {
628 command_args: self.command_args.arg("NEWLINE").arg(newline).build(),
629 }
630 }
631
632 /// Sets the string that's put between a key and a value.
633 #[must_use]
634 pub fn space<S: SingleArg>(mut self, space: S) -> Self {
635 Self {
636 command_args: self.command_args.arg("SPACE").arg(space).build(),
637 }
638 }
639
640 /// JSONPath to specify
641 #[must_use]
642 pub fn path<P: SingleArg, PP: SingleArgCollection<P>>(mut self, paths: PP) -> Self {
643 Self {
644 command_args: self.command_args.arg(paths).build(),
645 }
646 }
647}
648
649impl ToArgs for JsonGetOptions {
650 fn write_args(&self, args: &mut CommandArgs) {
651 args.arg(&self.command_args);
652 }
653}
654
655/// Options for the [`json_arrindex`](JsonCommands::json_arrindex) command
656#[derive(Default)]
657pub struct JsonArrIndexOptions {
658 command_args: CommandArgs,
659}
660
661impl JsonArrIndexOptions {
662 /// Inclusive start value to specify in a slice of the array to search.
663 ///
664 /// Default is 0.
665 #[must_use]
666 pub fn start(mut self, start: usize) -> Self {
667 Self {
668 command_args: self.command_args.arg(start).build(),
669 }
670 }
671
672 /// Exclusive stop value to specify in a slice of the array to search, including the last element.
673 ///
674 /// Default is 0.
675 /// Negative values are interpreted as starting from the end.
676 #[must_use]
677 pub fn stop(mut self, stop: isize) -> Self {
678 Self {
679 command_args: self.command_args.arg(stop).build(),
680 }
681 }
682}
683
684impl ToArgs for JsonArrIndexOptions {
685 fn write_args(&self, args: &mut CommandArgs) {
686 args.arg(&self.command_args);
687 }
688}