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}