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}