redis/commands/
json.rs

1use crate::cmd::{cmd, Cmd};
2use crate::connection::ConnectionLike;
3use crate::pipeline::Pipeline;
4use crate::types::{FromRedisValue, RedisResult, ToRedisArgs, ToSingleRedisArg};
5use crate::RedisError;
6
7#[cfg(feature = "cluster")]
8use crate::commands::ClusterPipeline;
9
10use serde::ser::Serialize;
11
12macro_rules! implement_json_commands {
13    (
14        $lifetime: lifetime
15        $(
16            $(#[$attr:meta])+
17            fn $name:ident<$($tyargs:ident : $ty:ident),*>(
18                $($argname:ident: $argty:ty),*) $body:block
19        )*
20    ) => (
21
22        /// Implements RedisJSON commands for connection like objects.  This
23        /// allows you to send commands straight to a connection or client.  It
24        /// is also implemented for redis results of clients which makes for
25        /// very convenient access in some basic cases.
26        ///
27        /// This allows you to use nicer syntax for some common operations.
28        /// For instance this code:
29        ///
30        /// ```rust,no_run
31        /// use redis::JsonCommands;
32        /// use serde_json::json;
33        /// # fn do_something() -> redis::RedisResult<()> {
34        /// let client = redis::Client::open("redis://127.0.0.1/")?;
35        /// let mut con = client.get_connection()?;
36        /// redis::cmd("JSON.SET").arg("my_key").arg("$").arg(&json!({"item": 42i32}).to_string()).exec(&mut con).unwrap();
37        /// assert_eq!(redis::cmd("JSON.GET").arg("my_key").arg("$").query(&mut con), Ok(String::from(r#"[{"item":42}]"#)));
38        /// # Ok(()) }
39        /// ```
40        ///
41        /// Will become this:
42        ///
43        /// ```rust,no_run
44        /// use redis::JsonCommands;
45        /// use serde_json::json;
46        /// # fn do_something() -> redis::RedisResult<()> {
47        /// let client = redis::Client::open("redis://127.0.0.1/")?;
48        /// let mut con = client.get_connection()?;
49        /// let _: () = con.json_set("my_key", "$", &json!({"item": 42i32}).to_string())?;
50        /// assert_eq!(con.json_get("my_key", "$"), Ok(String::from(r#"[{"item":42}]"#)));
51        /// assert_eq!(con.json_get("my_key", "$.item"), Ok(String::from(r#"[42]"#)));
52        /// # Ok(()) }
53        /// ```
54        ///
55        /// With RedisJSON commands, you have to note that all results will be wrapped
56        /// in square brackets (or empty brackets if not found). If you want to deserialize it
57        /// with e.g. `serde_json` you have to use `Vec<T>` for your output type instead of `T`.
58        pub trait JsonCommands : ConnectionLike + Sized {
59            $(
60                $(#[$attr])*
61                #[inline]
62                #[allow(clippy::extra_unused_lifetimes, clippy::needless_lifetimes)]
63                fn $name<$lifetime, $($tyargs: $ty, )* RV: FromRedisValue>(
64                    &mut self $(, $argname: $argty)*) -> RedisResult<RV>
65                    { Cmd::$name($($argname),*)?.query(self) }
66            )*
67        }
68
69        impl Cmd {
70            $(
71                $(#[$attr])*
72                #[allow(clippy::extra_unused_lifetimes, clippy::needless_lifetimes)]
73                pub fn $name<$lifetime, $($tyargs: $ty),*>($($argname: $argty),*) -> RedisResult<Self> {
74					$body
75                }
76            )*
77        }
78
79		/// Implements RedisJSON commands over asynchronous connections. This
80        /// allows you to send commands straight to a connection or client.
81        ///
82        /// This allows you to use nicer syntax for some common operations.
83        /// For instance this code:
84        ///
85        /// ```rust,no_run
86        /// use redis::JsonAsyncCommands;
87        /// use serde_json::json;
88        /// # async fn do_something() -> redis::RedisResult<()> {
89        /// let client = redis::Client::open("redis://127.0.0.1/")?;
90        /// let mut con = client.get_multiplexed_async_connection().await?;
91        /// redis::cmd("JSON.SET").arg("my_key").arg("$").arg(&json!({"item": 42i32}).to_string()).exec_async(&mut con).await?;
92        /// assert_eq!(redis::cmd("JSON.GET").arg("my_key").arg("$").query_async(&mut con).await, Ok(String::from(r#"[{"item":42}]"#)));
93        /// # Ok(()) }
94        /// ```
95        ///
96        /// Will become this:
97        ///
98        /// ```rust,no_run
99        /// use redis::JsonAsyncCommands;
100        /// use serde_json::json;
101        /// # async fn do_something() -> redis::RedisResult<()> {
102        /// use redis::Commands;
103        /// let client = redis::Client::open("redis://127.0.0.1/")?;
104        /// let mut con = client.get_multiplexed_async_connection().await?;
105        /// let _: () = con.json_set("my_key", "$", &json!({"item": 42i32}).to_string()).await?;
106        /// assert_eq!(con.json_get("my_key", "$").await, Ok(String::from(r#"[{"item":42}]"#)));
107        /// assert_eq!(con.json_get("my_key", "$.item").await, Ok(String::from(r#"[42]"#)));
108        /// # Ok(()) }
109        /// ```
110        ///
111        /// With RedisJSON commands, you have to note that all results will be wrapped
112        /// in square brackets (or empty brackets if not found). If you want to deserialize it
113        /// with e.g. `serde_json` you have to use `Vec<T>` for your output type instead of `T`.
114        ///
115		#[cfg(feature = "aio")]
116        pub trait JsonAsyncCommands : crate::aio::ConnectionLike + Send + Sized {
117            $(
118                $(#[$attr])*
119                #[inline]
120                #[allow(clippy::extra_unused_lifetimes, clippy::needless_lifetimes)]
121                fn $name<$lifetime, $($tyargs: $ty + Send + Sync + $lifetime,)* RV>(
122                    & $lifetime mut self
123                    $(, $argname: $argty)*
124                ) -> $crate::types::RedisFuture<'a, RV>
125                where
126                    RV: FromRedisValue,
127                {
128                    Box::pin(async move {
129                        $body?.query_async(self).await
130                    })
131                }
132            )*
133		}
134
135		/// Implements RedisJSON commands for pipelines.  Unlike the regular
136        /// commands trait, this returns the pipeline rather than a result
137        /// directly.  Other than that it works the same however.
138        impl Pipeline {
139            $(
140                $(#[$attr])*
141                #[inline]
142                #[allow(clippy::extra_unused_lifetimes, clippy::needless_lifetimes)]
143                pub fn $name<$lifetime, $($tyargs: $ty),*>(
144                    &mut self $(, $argname: $argty)*
145                ) -> RedisResult<&mut Self> {
146                    self.add_command($body?);
147					Ok(self)
148                }
149            )*
150        }
151
152		/// Implements RedisJSON commands for cluster pipelines.  Unlike the regular
153        /// commands trait, this returns the cluster pipeline rather than a result
154        /// directly.  Other than that it works the same however.
155        #[cfg(feature = "cluster")]
156        impl ClusterPipeline {
157            $(
158                $(#[$attr])*
159                #[inline]
160                #[allow(clippy::extra_unused_lifetimes, clippy::needless_lifetimes)]
161                pub fn $name<$lifetime, $($tyargs: $ty),*>(
162                    &mut self $(, $argname: $argty)*
163                ) -> RedisResult<&mut Self> {
164                    self.add_command($body?);
165					Ok(self)
166                }
167            )*
168        }
169
170    )
171}
172
173implement_json_commands! {
174    'a
175
176    /// Append the JSON `value` to the array at `path` after the last element in it.
177    fn json_arr_append<K: ToSingleRedisArg, P: ToSingleRedisArg, V: Serialize>(key: K, path: P, value: &'a V) {
178        let mut cmd = cmd("JSON.ARRAPPEND");
179
180        cmd.arg(key)
181           .arg(path)
182           .arg(serde_json::to_string(value)?);
183
184        Ok::<_, RedisError>(cmd)
185    }
186
187    /// Index array at `path`, returns first occurrence of `value`
188    fn json_arr_index<K: ToSingleRedisArg, P: ToSingleRedisArg, V: Serialize>(key: K, path: P, value: &'a V) {
189        let mut cmd = cmd("JSON.ARRINDEX");
190
191        cmd.arg(key)
192           .arg(path)
193           .arg(serde_json::to_string(value)?);
194
195        Ok::<_, RedisError>(cmd)
196    }
197
198    /// Same as `json_arr_index` except takes a `start` and a `stop` value, setting these to `0` will mean
199    /// they make no effect on the query
200    ///
201    /// The default values for `start` and `stop` are `0`, so pass those in if you want them to take no effect
202    fn json_arr_index_ss<K: ToSingleRedisArg, P: ToSingleRedisArg, V: Serialize>(key: K, path: P, value: &'a V, start: &'a isize, stop: &'a isize) {
203        let mut cmd = cmd("JSON.ARRINDEX");
204
205        cmd.arg(key)
206           .arg(path)
207           .arg(serde_json::to_string(value)?)
208           .arg(start)
209           .arg(stop);
210
211        Ok::<_, RedisError>(cmd)
212    }
213
214    /// Inserts the JSON `value` in the array at `path` before the `index` (shifts to the right).
215    ///
216    /// `index` must be within the array's range.
217    fn json_arr_insert<K: ToSingleRedisArg, P: ToSingleRedisArg, V: Serialize>(key: K, path: P, index: i64, value: &'a V) {
218        let mut cmd = cmd("JSON.ARRINSERT");
219
220        cmd.arg(key)
221           .arg(path)
222           .arg(index)
223           .arg(serde_json::to_string(value)?);
224
225        Ok::<_, RedisError>(cmd)
226
227    }
228
229    /// Reports the length of the JSON Array at `path` in `key`.
230    fn json_arr_len<K: ToSingleRedisArg, P: ToSingleRedisArg>(key: K, path: P) {
231        let mut cmd = cmd("JSON.ARRLEN");
232
233        cmd.arg(key)
234           .arg(path);
235
236        Ok::<_, RedisError>(cmd)
237    }
238
239    /// Removes and returns an element from the `index` in the array.
240    ///
241    /// `index` defaults to `-1` (the end of the array).
242    fn json_arr_pop<K: ToSingleRedisArg, P: ToSingleRedisArg>(key: K, path: P, index: i64) {
243        let mut cmd = cmd("JSON.ARRPOP");
244
245        cmd.arg(key)
246           .arg(path)
247           .arg(index);
248
249        Ok::<_, RedisError>(cmd)
250    }
251
252    /// Trims an array so that it contains only the specified inclusive range of elements.
253    ///
254    /// This command is extremely forgiving and using it with out-of-range indexes will not produce an error.
255    /// There are a few differences between how RedisJSON v2.0 and legacy versions handle out-of-range indexes.
256    fn json_arr_trim<K: ToSingleRedisArg, P: ToSingleRedisArg>(key: K, path: P, start: i64, stop: i64) {
257        let mut cmd = cmd("JSON.ARRTRIM");
258
259        cmd.arg(key)
260           .arg(path)
261           .arg(start)
262           .arg(stop);
263
264        Ok::<_, RedisError>(cmd)
265    }
266
267    /// Clears container values (Arrays/Objects), and sets numeric values to 0.
268    fn json_clear<K: ToSingleRedisArg, P: ToSingleRedisArg>(key: K, path: P) {
269        let mut cmd = cmd("JSON.CLEAR");
270
271        cmd.arg(key)
272           .arg(path);
273
274        Ok::<_, RedisError>(cmd)
275    }
276
277    /// Deletes a value at `path`.
278    fn json_del<K: ToSingleRedisArg, P: ToSingleRedisArg>(key: K, path: P) {
279        let mut cmd = cmd("JSON.DEL");
280
281        cmd.arg(key)
282           .arg(path);
283
284        Ok::<_, RedisError>(cmd)
285    }
286
287    /// Gets JSON Value at `path`.
288    ///
289    /// With RedisJSON commands, you have to note that all results will be wrapped
290    /// in square brackets (or empty brackets if not found). If you want to deserialize it
291    /// with e.g. `serde_json` you have to use `Vec<T>` for your output type instead of `T`.
292    fn json_get<K: ToSingleRedisArg, P: ToRedisArgs>(key: K, path: P) {
293        let mut cmd = cmd("JSON.GET");
294
295        cmd.arg(key)
296           .arg(path);
297
298        Ok::<_, RedisError>(cmd)
299    }
300
301    /// Gets JSON Values at `path`.
302    ///
303    /// With RedisJSON commands, you have to note that all results will be wrapped
304    /// in square brackets (or empty brackets if not found). If you want to deserialize it
305    /// with e.g. `serde_json` you have to use `Vec<T>` for your output type instead of `T`.
306    fn json_mget<K: ToRedisArgs, P: ToSingleRedisArg>(key: K, path: P) {
307        let mut cmd = cmd("JSON.MGET");
308
309        cmd.arg(key)
310           .arg(path);
311
312        Ok::<_, RedisError>(cmd)
313    }
314
315    /// Increments the number value stored at `path` by `number`.
316    fn json_num_incr_by<K: ToSingleRedisArg, P: ToSingleRedisArg>(key: K, path: P, value: i64) {
317        let mut cmd = cmd("JSON.NUMINCRBY");
318
319        cmd.arg(key)
320           .arg(path)
321           .arg(value);
322
323        Ok::<_, RedisError>(cmd)
324    }
325
326    /// Returns the keys in the object that's referenced by `path`.
327    fn json_obj_keys<K: ToSingleRedisArg, P: ToSingleRedisArg>(key: K, path: P) {
328        let mut cmd = cmd("JSON.OBJKEYS");
329
330        cmd.arg(key)
331           .arg(path);
332
333        Ok::<_, RedisError>(cmd)
334    }
335
336    /// Reports the number of keys in the JSON Object at `path` in `key`.
337    fn json_obj_len<K: ToSingleRedisArg, P: ToSingleRedisArg>(key: K, path: P) {
338        let mut cmd = cmd("JSON.OBJLEN");
339
340        cmd.arg(key)
341           .arg(path);
342
343        Ok::<_, RedisError>(cmd)
344    }
345
346    /// Sets the JSON Value at `path` in `key`.
347    fn json_set<K: ToSingleRedisArg, P: ToSingleRedisArg, V: Serialize>(key: K, path: P, value: &'a V) {
348        let mut cmd = cmd("JSON.SET");
349
350        cmd.arg(key)
351           .arg(path)
352           .arg(serde_json::to_string(value)?);
353
354        Ok::<_, RedisError>(cmd)
355    }
356
357        /// Sets the value at the path per key, for every given tuple.
358    fn json_mset<K: ToSingleRedisArg, P: ToSingleRedisArg, V: Serialize>(key_path_values: &'a [(K,P,V)]) {
359        let mut cmd = cmd("JSON.MSET");
360
361        for (key, path, value) in key_path_values {
362            cmd.arg(key)
363               .arg(path)
364               .arg(serde_json::to_string(value)?);
365        }
366
367        Ok::<_, RedisError>(cmd)
368    }
369
370    /// Appends the `json-string` values to the string at `path`.
371    fn json_str_append<K: ToSingleRedisArg, P: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, path: P, value: V) {
372        let mut cmd = cmd("JSON.STRAPPEND");
373
374        cmd.arg(key)
375           .arg(path)
376           .arg(value);
377
378        Ok::<_, RedisError>(cmd)
379    }
380
381    /// Reports the length of the JSON String at `path` in `key`.
382    fn json_str_len<K: ToSingleRedisArg, P: ToSingleRedisArg>(key: K, path: P) {
383        let mut cmd = cmd("JSON.STRLEN");
384
385        cmd.arg(key)
386           .arg(path);
387
388        Ok::<_, RedisError>(cmd)
389    }
390
391    /// Toggle a `boolean` value stored at `path`.
392    fn json_toggle<K: ToSingleRedisArg, P: ToSingleRedisArg>(key: K, path: P) {
393        let mut cmd = cmd("JSON.TOGGLE");
394
395        cmd.arg(key)
396           .arg(path);
397
398        Ok::<_, RedisError>(cmd)
399    }
400
401    /// Reports the type of JSON value at `path`.
402    fn json_type<K: ToSingleRedisArg, P: ToSingleRedisArg>(key: K, path: P) {
403        let mut cmd = cmd("JSON.TYPE");
404
405        cmd.arg(key)
406           .arg(path);
407
408        Ok::<_, RedisError>(cmd)
409    }
410}
411
412impl<T> JsonCommands for T where T: ConnectionLike {}
413
414#[cfg(feature = "aio")]
415impl<T> JsonAsyncCommands for T where T: crate::aio::ConnectionLike + Send + Sized {}