rustis/commands/
t_disgest_commands.rs

1use crate::{
2    client::{PreparedCommand, prepare_command},
3    resp::{Args, CommandArgs, Response, Value, cmd},
4};
5use serde::Deserialize;
6use std::collections::HashMap;
7
8/// A group of Redis commands related to [`T-Digest`](https://redis.io/docs/stack/bloom/)
9///
10/// # See Also
11/// [T-Digest Commands](https://redis.io/commands/?group=tdigest)
12pub trait TDigestCommands<'a> {
13    /// Adds one or more observations to a t-digest sketch.
14    ///
15    /// # Arguments
16    /// * `key` - key name for an existing t-digest sketch.
17    /// * `values` - collection values of an observation (floating-point).
18    ///
19    /// # See Also
20    /// * [<https://redis.io/commands/tdigest.add/>](https://redis.io/commands/tdigest.add/)
21    #[must_use]
22    fn tdigest_add(self, key: impl Args, values: impl Args) -> PreparedCommand<'a, Self, ()>
23    where
24        Self: Sized,
25    {
26        prepare_command(self, cmd("TDIGEST.ADD").arg(key).arg(values))
27    }
28
29    /// Returns, for each input rank, an estimation of the value (floating-point) with that rank.
30    ///
31    /// Multiple estimations can be retrieved in a single call.
32    ///
33    /// # Arguments
34    /// * `key` - key name for an existing t-digest sketch.
35    /// * `ranks` - collection of ranks, for which the value should be retrieved.
36    ///   * `0` - is the rank of the value of the smallest observation.
37    ///   * `n-1` - is the rank of the value of the largest observation; `n` denotes the number of observations added to the sketch.
38    ///
39    /// # Return
40    /// a collection of floating-points populated with value_1, value_2, ..., value_R:
41    /// * Return an accurate result when rank is `0` (the value of the smallest observation)
42    /// * Return an accurate result when rank is `n-1` (the value of the largest observation), \
43    ///   where n denotes the number of observations added to the sketch.
44    /// * Return `inf` when rank is equal to n or larger than `n`
45    ///
46    /// # See Also
47    /// * [<https://redis.io/commands/tdigest.byrank/>](https://redis.io/commands/tdigest.byrank/)
48    #[must_use]
49    fn tdigest_byrank<R: Response>(
50        self,
51        key: impl Args,
52        ranks: impl Args,
53    ) -> PreparedCommand<'a, Self, R>
54    where
55        Self: Sized,
56    {
57        prepare_command(self, cmd("TDIGEST.BYRANK").arg(key).arg(ranks))
58    }
59
60    /// Returns, for each input reverse rank, an estimation of the value (floating-point) with that reverse rank.
61    ///
62    /// Multiple estimations can be retrieved in a single call.
63    ///
64    /// # Arguments
65    /// * `key` - key name for an existing t-digest sketch.
66    /// * `ranks` - collection of reverse ranks, for which the value should be retrieved.
67    ///   * `0` -  is the reverse rank of the value of the largest observation.
68    ///   * `n-1` - s the reverse rank of the value of the smallest observation; n denotes the number of observations added to the sketch.
69    ///
70    /// # Return
71    /// a collection of floating-points populated with value_1, value_2, ..., value_R:
72    /// * Return an accurate result when `revrank` is `0` (the value of the largest observation)
73    /// * Return an accurate result when `revrank` is `n-1` (the value of the smallest observation), \
74    ///   where `n` denotes the number of observations added to the sketch.
75    /// * Return 'inf' when `revrank` is equal to `n` or larger than `n`
76    ///
77    /// # See Also
78    /// * [<https://redis.io/commands/tdigest.byrevrank/>](https://redis.io/commands/tdigest.byrevrank/)
79    #[must_use]
80    fn tdigest_byrevrank<R: Response>(
81        self,
82        key: impl Args,
83        ranks: impl Args,
84    ) -> PreparedCommand<'a, Self, R>
85    where
86        Self: Sized,
87    {
88        prepare_command(self, cmd("TDIGEST.BYREVRANK").arg(key).arg(ranks))
89    }
90
91    /// Returns, for each input reverse rank, an estimation of the value (floating-point) with that reverse rank.
92    ///
93    /// Multiple estimations can be retrieved in a single call.
94    ///
95    /// # Arguments
96    /// * `key` - key name for an existing t-digest sketch.
97    /// * `values` - collection values for which the CDF \
98    ///   ([`Cumulative Distribution Function`](https://en.wikipedia.org/wiki/Cumulative_distribution_function)) should be retrieved.
99    ///
100    /// # Return
101    /// a collection of floating-points populated with fraction_1, fraction_2, ..., fraction_N.
102    ///
103    /// All values are `nan` if the sketch is empty.
104    ///
105    /// # See Also
106    /// * [<https://redis.io/commands/tdigest.cdf/>](https://redis.io/commands/tdigest.cdf/)
107    #[must_use]
108    fn tdigest_cdf<R: Response>(
109        self,
110        key: impl Args,
111        values: impl Args,
112    ) -> PreparedCommand<'a, Self, R>
113    where
114        Self: Sized,
115    {
116        prepare_command(self, cmd("TDIGEST.CDF").arg(key).arg(values))
117    }
118
119    /// Allocates memory and initializes a new t-digest sketch.
120    ///
121    /// # Arguments
122    /// * `key` - key name for this new t-digest sketch.
123    /// * `compression` - controllable tradeoff between accuracy and memory consumption. \
124    ///   100 is a common value for normal uses. 1000 is more accurate. \
125    ///   If no value is passed by default the compression will be 100. \
126    ///   For more information on scaling of accuracy versus the compression parameter,\
127    ///   see [`The t-digest: Efficient estimates of distributions`](https://www.sciencedirect.com/science/article/pii/S2665963820300403).
128    ///
129    /// # See Also
130    /// * [<https://redis.io/commands/tdigest.create/>](https://redis.io/commands/tdigest.create/)
131    #[must_use]
132    fn tdigest_create(
133        self,
134        key: impl Args,
135        compression: Option<i64>,
136    ) -> PreparedCommand<'a, Self, ()>
137    where
138        Self: Sized,
139    {
140        prepare_command(
141            self,
142            cmd("TDIGEST.CREATE")
143                .arg(key)
144                .arg(compression.map(|c| ("COMPRESSION", c))),
145        )
146    }
147
148    /// Returns information and statistics about a t-digest sketch
149    ///
150    /// # Arguments
151    /// * `key` - key name for an existing t-digest sketch.
152    ///
153    /// # Return
154    /// An instance of [`TDigestInfoResult`](TDigestInfoResult)
155    ///
156    /// # See Also
157    /// * [<https://redis.io/commands/tdigest.info/>](https://redis.io/commands/tdigest.info/)
158    #[must_use]
159    fn tdigest_info(self, key: impl Args) -> PreparedCommand<'a, Self, TDigestInfoResult>
160    where
161        Self: Sized,
162    {
163        prepare_command(self, cmd("TDIGEST.INFO").arg(key))
164    }
165
166    /// Returns the maximum observation value from a t-digest sketch.
167    ///
168    /// # Arguments
169    /// * `key` - key name for an existing t-digest sketch.
170    ///
171    /// # Return
172    /// maximum observation value from a sketch. The result is always accurate.
173    /// `nan` if the sketch is empty.
174    ///
175    /// # See Also
176    /// * [<https://redis.io/commands/tdigest.max/>](https://redis.io/commands/tdigest.max/)
177    #[must_use]
178    fn tdigest_max(self, key: impl Args) -> PreparedCommand<'a, Self, f64>
179    where
180        Self: Sized,
181    {
182        prepare_command(self, cmd("TDIGEST.MAX").arg(key))
183    }
184
185    /// Merges multiple t-digest sketches into a single sketch.
186    ///
187    /// # Arguments
188    /// * `destination` - key name for a t-digest sketch to merge observation values to.
189    ///   * If `destination` not exist, a new sketch is created.
190    ///   * If `destination` is an existing sketch, its values are merged with the values of the source keys.\
191    ///     To override the destination key contents use [`override`](TDigestMergeOptions::_override).
192    /// * `sources` - collection of key names for t-digest sketches to merge observation values from.
193    ///
194    /// # See Also
195    /// * [<https://redis.io/commands/tdigest.merge/>](https://redis.io/commands/tdigest.merge/)
196    #[must_use]
197    fn tdigest_merge(
198        self,
199        destination: impl Args,
200        sources: impl Args,
201        options: TDigestMergeOptions,
202    ) -> PreparedCommand<'a, Self, ()>
203    where
204        Self: Sized,
205    {
206        prepare_command(
207            self,
208            cmd("TDIGEST.MERGE")
209                .arg(destination)
210                .arg(sources.num_args())
211                .arg(sources)
212                .arg(options),
213        )
214    }
215
216    /// Returns the minimum observation value from a t-digest sketch.
217    ///
218    /// # Arguments
219    /// * `key` - key name for an existing t-digest sketch.
220    ///
221    /// # Return
222    /// minimum observation value from a sketch. The result is always accurate.
223    /// `nan` if the sketch is empty.
224    ///
225    /// # See Also
226    /// * [<https://redis.io/commands/tdigest.min/>](https://redis.io/commands/tdigest.min/)
227    #[must_use]
228    fn tdigest_min(self, key: impl Args) -> PreparedCommand<'a, Self, f64>
229    where
230        Self: Sized,
231    {
232        prepare_command(self, cmd("TDIGEST.MIN").arg(key))
233    }
234
235    /// Returns, for each input fraction, an estimation of the value
236    /// (floating point) that is smaller than the given fraction of observations.
237    ///
238    /// Multiple quantiles can be retrieved in a signle call.
239    ///
240    /// # Arguments
241    /// * `key` - key name for an existing t-digest sketch.
242    /// * `quantiles` - collection of quantiles which are input fractions (between 0 and 1 inclusively)
243    ///
244    /// # Return
245    /// a collection of estimates (floating-point) populated with value_1, value_2, ..., value_N.
246    /// * Return an accurate result when quantile is 0 (the value of the smallest observation)
247    /// * Return an accurate result when quantile is 1 (the value of the largest observation)
248    ///
249    /// All values are `nan` if the sketch is empty.
250    ///
251    /// # See Also
252    /// * [<https://redis.io/commands/tdigest.quantile/>](https://redis.io/commands/tdigest.quantile/)
253    #[must_use]
254    fn tdigest_quantile<R: Response>(
255        self,
256        key: impl Args,
257        quantiles: impl Args,
258    ) -> PreparedCommand<'a, Self, R>
259    where
260        Self: Sized,
261    {
262        prepare_command(self, cmd("TDIGEST.QUANTILE").arg(key).arg(quantiles))
263    }
264
265    /// Returns, for each input value (floating-point), the estimated rank of the value
266    /// (the number of observations in the sketch that are smaller than the value + half the number of observations that are equal to the value).
267    ///
268    /// Multiple ranks can be retrieved in a signle call.
269    ///
270    /// # Arguments
271    /// * `key` - key name for an existing t-digest sketch.
272    /// * `values` - collection of values for which the rank should be estimated.
273    ///
274    /// # Return
275    /// a collection of integers populated with rank_1, rank_2, ..., rank_V:
276    /// * `-1` - when `value` is smaller than the value of the smallest observation.
277    /// * The number of observations - when `value` is larger than the value of the largest observation.
278    /// * Otherwise: an estimation of the number of (observations smaller than `value` + half the observations equal to `value`).
279    ///
280    /// `0` is the rank of the value of the smallest observation.
281    ///
282    /// `n-1` is the rank of the value of the largest observation; `n` denotes the number of observations added to the sketch.
283    ///
284    /// All values are `-2` if the sketch is empty.
285    ///
286    /// # See Also
287    /// * [<https://redis.io/commands/tdigest.rank/>](https://redis.io/commands/tdigest.rank/)
288    #[must_use]
289    fn tdigest_rank<R: Response>(
290        self,
291        key: impl Args,
292        values: impl Args,
293    ) -> PreparedCommand<'a, Self, R>
294    where
295        Self: Sized,
296    {
297        prepare_command(self, cmd("TDIGEST.RANK").arg(key).arg(values))
298    }
299
300    /// Resets a t-digest sketch: empty the sketch and re-initializes it.
301    ///
302    /// # Arguments
303    /// * `key` - key name for an existing t-digest sketch.
304    ///
305    /// # See Also
306    /// * [<https://redis.io/commands/tdigest.reset/>](https://redis.io/commands/tdigest.reset/)
307    #[must_use]
308    fn tdigest_reset(self, key: impl Args) -> PreparedCommand<'a, Self, ()>
309    where
310        Self: Sized,
311    {
312        prepare_command(self, cmd("TDIGEST.RESET").arg(key))
313    }
314
315    /// Returns, for each input value (floating-point), the estimated reverse rank of the value
316    /// (the number of observations in the sketch that are smaller than the value + half the number of observations that are equal to the value).
317    ///
318    /// Multiple reverse ranks can be retrieved in a signle call.
319    ///
320    /// # Arguments
321    /// * `key` - key name for an existing t-digest sketch.
322    /// * `values` - collection of values for which the reverse rank should be estimated.
323    ///
324    /// # Return
325    /// a collection of integers populated with revrank_1, revrank_2, ..., revrank_V:
326    /// * `-1` - when `value` is smaller than the value of the smallest observation.
327    /// * The number of observations - when `value` is larger than the value of the largest observation.
328    /// * Otherwise: an estimation of the number of (observations smaller than `value` + half the observations equal to `value`).
329    ///
330    /// `0` is the reverse rank of the value of the smallest observation.
331    ///
332    /// `n-1` is the reverse rank of the value of the largest observation; `n` denotes the number of observations added to the sketch.
333    ///
334    /// All values are `-2` if the sketch is empty.
335    ///
336    /// # See Also
337    /// * [<https://redis.io/commands/tdigest.revrank/>](https://redis.io/commands/tdigest.revrank/)
338    #[must_use]
339    fn tdigest_revrank<R: Response>(
340        self,
341        key: impl Args,
342        values: impl Args,
343    ) -> PreparedCommand<'a, Self, R>
344    where
345        Self: Sized,
346    {
347        prepare_command(self, cmd("TDIGEST.REVRANK").arg(key).arg(values))
348    }
349
350    /// Returns an estimation of the mean value from the sketch, excluding observation values outside the low and high cutoff quantiles.
351    ///
352    /// # Arguments
353    /// * `key` - key name for an existing t-digest sketch.
354    /// * `low_cut_quantile` - Foating-point value in the range [0..1], should be lower than `high_cut_quantile` \
355    ///   When equal to 0: No low cut. \
356    ///   When higher than 0: Exclude observation values lower than this quantile.
357    /// * `high_cut_quantile` - Floating-point value in the range [0..1], should be higher than `low_cut_quantile` \
358    ///   When lower than 1: Exclude observation values higher than or equal to this quantile. \
359    ///   When equal to 1: No high cut.
360    ///
361    /// # Return
362    /// estimation of the mean value. 'nan' if the sketch is empty.
363    ///
364    /// # See Also
365    /// * [<https://redis.io/commands/tdigest.trimmed_mean/>](https://redis.io/commands/tdigest.trimmed_mean/)
366    #[must_use]
367    fn tdigest_trimmed_mean(
368        self,
369        key: impl Args,
370        low_cut_quantile: f64,
371        high_cut_quantile: f64,
372    ) -> PreparedCommand<'a, Self, f64>
373    where
374        Self: Sized,
375    {
376        prepare_command(
377            self,
378            cmd("TDIGEST.TRIMMED_MEAN")
379                .arg(key)
380                .arg(low_cut_quantile)
381                .arg(high_cut_quantile),
382        )
383    }
384}
385
386/// Result for the [`tdigest_info`](TDigestCommands::tdigest_info) command.
387#[derive(Debug, Deserialize)]
388pub struct TDigestInfoResult {
389    /// The compression (controllable trade-off between accuracy and memory consumption) of the sketch
390    #[serde(rename = "Compression")]
391    pub compression: usize,
392    /// Size of the buffer used for storing the centroids and for the incoming unmerged observations
393    #[serde(rename = "Capacity")]
394    pub capacity: usize,
395    /// Number of merged observations
396    #[serde(rename = "Merged nodes")]
397    pub merged_nodes: usize,
398    /// Number of buffered nodes (uncompressed observations)
399    #[serde(rename = "Unmerged nodes")]
400    pub unmerged_nodes: usize,
401    /// Weight of values of the merged nodes
402    #[serde(rename = "Merged weight")]
403    pub merged_weight: usize,
404    /// Weight of values of the unmerged nodes (uncompressed observations)
405    #[serde(rename = "Unmerged weight")]
406    pub unmerged_weight: usize,
407    /// Number of observations added to the sketch
408    #[serde(rename = "Observations")]
409    pub observations: usize,
410    /// Number of times this sketch compressed data together
411    #[serde(rename = "Total compressions")]
412    pub total_compressions: usize,
413    /// Number of bytes allocated for the sketch
414    #[serde(rename = "Memory usage")]
415    pub memory_usage: usize,
416    /// Additional information
417    #[serde(flatten)]
418    pub additional_info: HashMap<String, Value>,
419}
420
421/// Options for the [`tdigest_merge`](TDigestCommands::tdigest_merge) command.
422#[derive(Default)]
423pub struct TDigestMergeOptions {
424    command_args: CommandArgs,
425}
426
427impl TDigestMergeOptions {
428    /// controllable tradeoff between accuracy and memory consumption.
429    ///
430    /// 100 is a common value for normal uses.
431    /// 1000 is more accurate.
432    /// If no value is passed by default the compression will be 100.
433    /// For more information on scaling of accuracy versus the compression parameter
434    /// see [`The t-digest: Efficient estimates of distributions`](https://www.sciencedirect.com/science/article/pii/S2665963820300403).
435    ///
436    /// When COMPRESSION is not specified:
437    /// * If `destination` does not exist or if [`override`](TDigestMergeOptions::_override) is specified, \
438    ///   the compression is set to the maximal value among all source sketches.
439    /// * If `destination` already exists and [`override`](TDigestMergeOptions::_override) is not specified, \
440    ///   its compression is not changed.
441    #[must_use]
442    pub fn compression(mut self, compression: usize) -> Self {
443        Self {
444            command_args: self
445                .command_args
446                .arg("COMPRESSION")
447                .arg(compression)
448                .build(),
449        }
450    }
451
452    /// When specified, if `destination` already exists, it is overwritten.
453    #[must_use]
454    pub fn _override(mut self) -> Self {
455        Self {
456            command_args: self.command_args.arg("OVERRIDE").build(),
457        }
458    }
459}
460
461impl Args for TDigestMergeOptions {
462    fn write_args(&self, args: &mut CommandArgs) {
463        args.arg(&self.command_args);
464    }
465}