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}