Skip to main content

rustis/commands/
t_disgest_commands.rs

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