rustis/commands/bloom_commands.rs
1use crate::{
2 client::{PreparedCommand, prepare_command},
3 resp::{BulkString, Response, cmd, serialize_flag},
4};
5use serde::{Deserialize, Serialize};
6
7/// A group of Redis commands related to [`Bloom filters`](https://redis.io/docs/stack/bloom/)
8///
9/// # See Also
10/// [Bloom Filter Commands](https://redis.io/commands/?group=bf)
11pub trait BloomCommands<'a>: Sized {
12 /// Adds an item to a bloom filter
13 ///
14 /// # Arguments
15 /// * `key` - The name of the filter
16 /// * `item` - The item to add
17 ///
18 /// # Return
19 /// * `true` - if the item did not exist in the filter,
20 /// * `false` - otherwise.
21 ///
22 /// # See Also
23 /// * [<https://redis.io/commands/bf.add/>](https://redis.io/commands/bf.add/)
24 #[must_use]
25 fn bf_add(self, key: impl Serialize, item: impl Serialize) -> PreparedCommand<'a, Self, bool> {
26 prepare_command(self, cmd("BF.ADD").key(key).arg(item))
27 }
28
29 /// Determines whether an item may exist in the Bloom Filter or not.
30 ///
31 /// # Arguments
32 /// * `key` - The name of the filter
33 /// * `item` - The item to check for
34 ///
35 /// # Return
36 /// * `true` - means the item may exist in the filter,
37 /// * `false` - means it does not exist in the filter..
38 ///
39 /// # See Also
40 /// * [<https://redis.io/commands/bf.exists/>](https://redis.io/commands/bf.exists/)
41 #[must_use]
42 fn bf_exists(
43 self,
44 key: impl Serialize,
45 item: impl Serialize,
46 ) -> PreparedCommand<'a, Self, bool> {
47 prepare_command(self, cmd("BF.EXISTS").key(key).arg(item))
48 }
49
50 /// Return information about key filter.
51 ///
52 /// # Arguments
53 /// * `key` - Name of the key to return information about
54 ///
55 /// # Return
56 /// an instance of [`BfInfoResult`](BfInfoResult)
57 ///
58 /// # See Also
59 /// [<https://redis.io/commands/bf.info/>](https://redis.io/commands/bf.info/)
60 #[must_use]
61 fn bf_info_all(self, key: impl Serialize) -> PreparedCommand<'a, Self, BfInfoResult> {
62 prepare_command(self, cmd("BF.INFO").key(key))
63 }
64
65 /// Return information about key filter for a specific information parameter
66 ///
67 /// # Arguments
68 /// * `key` - Name of the key to return information about
69 /// * `param` - specific information parameter to query
70 ///
71 /// # Return
72 /// The value of the requested parameter
73 ///
74 /// # See Also
75 /// [<https://redis.io/commands/bf.info/>](https://redis.io/commands/bf.info/)
76 #[must_use]
77 fn bf_info<R: Response>(
78 self,
79 key: impl Serialize,
80 param: BfInfoParameter,
81 ) -> PreparedCommand<'a, Self, R> {
82 prepare_command(self, cmd("BF.INFO").key(key).arg(param))
83 }
84
85 /// `bf_insert` is a sugarcoated combination of [`bf_reserve`](BloomCommands::bf_reserve) and [`bf_add`](BloomCommands::bf_add).
86 ///
87 /// It creates a new filter if the key does not exist using the relevant arguments (see [`bf_reserve`](BloomCommands::bf_reserve)).
88 /// Next, all ITEMS are inserted.
89 ///
90 /// # Arguments
91 /// * `key` - The name of the filter
92 /// * `items` - One or more items to add
93 /// * `options` - See [`BfInsertOptions`](BfInsertOptions)
94 ///
95 /// # Return
96 /// A collection of booleans (integers).
97 ///
98 /// Each element is either true or false depending on whether the corresponding input element was newly added to the filter or may have previously existed.
99 ///
100 /// # See Also
101 /// [<https://redis.io/commands/bf.insert/>](https://redis.io/commands/bf.insert/)
102 #[must_use]
103 fn bf_insert<R: Response>(
104 self,
105 key: impl Serialize,
106 items: impl Serialize,
107 options: BfInsertOptions,
108 ) -> PreparedCommand<'a, Self, R> {
109 prepare_command(
110 self,
111 cmd("BF.INSERT")
112 .key(key)
113 .arg(options)
114 .arg("ITEMS")
115 .arg(items),
116 )
117 }
118
119 /// Restores a filter previously saved using [`bf_scandump`](BloomCommands::bf_scandump).
120 ///
121 /// See the [`bf_scandump`](BloomCommands::bf_scandump) command for example usage.
122 ///
123 /// This command overwrites any bloom filter stored under key.
124 /// Make sure that the bloom filter is not be changed between invocations.
125 ///
126 /// # Arguments
127 /// * `key` - Name of the key to restore
128 /// * `iterator` - Iterator value associated with `data` (returned by [`bf_scandump`](BloomCommands::bf_scandump))
129 /// * `data` - Current data chunk (returned by [`bf_scandump`](BloomCommands::bf_scandump))
130 ///
131 /// # See Also
132 /// [<https://redis.io/commands/bf.loadchunk/>](https://redis.io/commands/bf.loadchunk/)
133 #[must_use]
134 fn bf_loadchunk(
135 self,
136 key: impl Serialize,
137 iterator: i64,
138 data: impl Serialize,
139 ) -> PreparedCommand<'a, Self, ()> {
140 prepare_command(self, cmd("BF.LOADCHUNK").key(key).arg(iterator).arg(data))
141 }
142
143 /// Adds one or more items to the Bloom Filter and creates the filter if it does not exist yet.
144 ///
145 /// This command operates identically to [`bf_add`](BloomCommands::bf_add) except that it allows multiple inputs and returns multiple values.
146 ///
147 /// # Arguments
148 /// * `key` - The name of the filter
149 /// * `items` - One or more items to add
150 ///
151 /// # Return
152 /// Collection reply of boolean - for each item which is either `true` or `false` depending
153 /// on whether the corresponding input element was newly added to the filter or may have previously existed.
154 ///
155 /// # See Also
156 /// [<https://redis.io/commands/bf.madd/>](https://redis.io/commands/bf.madd/)
157 #[must_use]
158 fn bf_madd<R: Response>(
159 self,
160 key: impl Serialize,
161 items: impl Serialize,
162 ) -> PreparedCommand<'a, Self, R> {
163 prepare_command(self, cmd("BF.MADD").key(key).arg(items))
164 }
165
166 /// Determines if one or more items may exist in the filter or not.
167 ///
168 /// # Arguments
169 /// * `key` - The name of the filter
170 /// * `items` - One or more items to check
171 ///
172 /// # Return
173 /// Collection reply of boolean - for each item where `true` value means the corresponding item
174 /// may exist in the filter, and a `false` value means it does not exist in the filter.
175 ///
176 /// # See Also
177 /// [<https://redis.io/commands/bf.mexists/>](https://redis.io/commands/bf.mexists/)
178 #[must_use]
179 fn bf_mexists<R: Response>(
180 self,
181 key: impl Serialize,
182 items: impl Serialize,
183 ) -> PreparedCommand<'a, Self, R> {
184 prepare_command(self, cmd("BF.MEXISTS").key(key).arg(items))
185 }
186
187 /// Creates an empty Bloom Filter with a single sub-filter
188 /// for the initial capacity requested and with an upper bound error_rate.
189 ///
190 /// By default, the filter auto-scales by creating additional sub-filters when capacity is reached.
191 /// The new sub-filter is created with size of the previous sub-filter multiplied by expansion.
192 ///
193 /// Though the filter can scale up by creating sub-filters,
194 /// it is recommended to reserve the estimated required capacity since maintaining and querying sub-filters requires additional memory
195 /// (each sub-filter uses an extra bits and hash function) and consume further CPU time
196 /// than an equivalent filter that had the right capacity at creation time.
197 ///
198 /// The number of hash functions is `log(error)/ln(2)^2`. The number of bits per item is `log(error)/ln(2)` ≈ 1.44.
199 /// * 1% error rate requires 7 hash functions and 10.08 bits per item.
200 /// * 0.1% error rate requires 10 hash functions and 14.4 bits per item.
201 /// * 0.01% error rate requires 14 hash functions and 20.16 bits per item.
202 ///
203 /// # Arguments
204 /// * `key` - The key under which the filter is found
205 /// * `error_rate` - The desired probability for false positives.
206 /// The rate is a decimal value between 0 and 1.
207 /// For example, for a desired false positive rate of 0.1% (1 in 1000),
208 /// error_rate should be set to 0.001.
209 /// * `capacity` - The number of entries intended to be added to the filter.
210 /// If your filter allows scaling, performance will begin to degrade after adding more items than this number.
211 /// The actual degradation depends on how far the limit has been exceeded.
212 /// Performance degrades linearly with the number of `sub-filters`.
213 /// * `options` - See [`BfReserveOptions`](BfReserveOptions)
214 ///
215 /// # See Also
216 /// [<https://redis.io/commands/bf.reserve/>](https://redis.io/commands/bf.reserve/)
217 #[must_use]
218 fn bf_reserve(
219 self,
220 key: impl Serialize,
221 error_rate: f64,
222 capacity: usize,
223 options: BfReserveOptions,
224 ) -> PreparedCommand<'a, Self, ()> {
225 prepare_command(
226 self,
227 cmd("BF.RESERVE")
228 .key(key)
229 .arg(error_rate)
230 .arg(capacity)
231 .arg(options),
232 )
233 }
234
235 /// Begins an incremental save of the bloom filter.
236 /// This is useful for large bloom filters which cannot fit into the normal [`dump`](crate::commands::GenericCommands::dump)
237 /// and [`restore`](crate::commands::GenericCommands::restore) model.
238 ///
239 /// # Arguments
240 /// * `key` - Name of the filter
241 /// * `iterator` - Iterator value; either 0 or the iterator from a previous invocation of this command.\
242 /// The first time this command is called, the value of `iterator` should be 0.
243 ///
244 /// # Return
245 /// This command returns successive `(iterator, data)` pairs until `(0, vec![])` to indicate completion.
246 ///
247 /// # See Also
248 /// [<https://redis.io/commands/bf.scandump/>](https://redis.io/commands/bf.scandump/)
249 #[must_use]
250 fn bf_scandump(
251 self,
252 key: impl Serialize,
253 iterator: i64,
254 ) -> PreparedCommand<'a, Self, BfScanDumpResult> {
255 prepare_command(self, cmd("BF.SCANDUMP").key(key).arg(iterator))
256 }
257}
258
259/// Optional parameter for the [`bf_info`](BloomCommands::bf_info) command.
260///
261/// Used to query a specific parameter.
262#[derive(Serialize)]
263pub enum BfInfoParameter {
264 #[serde(rename = "CAPACITY")]
265 Capacity,
266 #[serde(rename = "SIZE")]
267 Size,
268 #[serde(rename = "FILTERS")]
269 NumFilters,
270 #[serde(rename = "ITEMS")]
271 NumItemsInserted,
272 #[serde(rename = "EXPANSION")]
273 ExpansionRate,
274}
275
276/// Result for the [`bf_info`](BloomCommands::bf_info) command.
277#[derive(Debug, Deserialize)]
278pub struct BfInfoResult {
279 #[serde(rename = "Capacity")]
280 pub capacity: usize,
281 #[serde(rename = "Size")]
282 pub size: usize,
283 #[serde(rename = "Number of filters")]
284 pub num_filters: usize,
285 #[serde(rename = "Number of items inserted")]
286 pub num_items_inserted: usize,
287 #[serde(rename = "Expansion rate")]
288 pub expansion_rate: usize,
289}
290
291/// Options for the [`bf_insert`](BloomCommands::bf_insert) command.
292#[derive(Default, Serialize)]
293#[serde(rename_all(serialize = "UPPERCASE"))]
294pub struct BfInsertOptions {
295 #[serde(skip_serializing_if = "Option::is_none")]
296 capacity: Option<u32>,
297 #[serde(skip_serializing_if = "Option::is_none")]
298 error: Option<f64>,
299 #[serde(skip_serializing_if = "Option::is_none")]
300 expansion: Option<u32>,
301 #[serde(
302 skip_serializing_if = "std::ops::Not::not",
303 serialize_with = "serialize_flag"
304 )]
305 nocreate: bool,
306 #[serde(
307 skip_serializing_if = "std::ops::Not::not",
308 serialize_with = "serialize_flag"
309 )]
310 nonscaling: bool,
311}
312
313impl BfInsertOptions {
314 /// Specifies the desired capacity for the filter to be created.
315 ///
316 /// This parameter is ignored if the filter already exists.
317 /// If the filter is automatically created and this parameter is absent,
318 /// then the module-level capacity is used.
319 /// See [`bf_reserve`](BloomCommands::bf_reserve) for more information about the impact of this value.
320 #[must_use]
321 pub fn capacity(mut self, capacity: u32) -> Self {
322 self.capacity = Some(capacity);
323 self
324 }
325
326 /// Specifies the error ratio of the newly created filter if it does not yet exist.
327 ///
328 /// If the filter is automatically created and error is not specified then the module-level error rate is used.
329 /// See [`bf_reserve`](BloomCommands::bf_reserve) for more information about the format of this value.
330 #[must_use]
331 pub fn error(mut self, error_rate: f64) -> Self {
332 self.error = Some(error_rate);
333 self
334 }
335
336 /// When `capacity` is reached, an additional sub-filter is created.
337 /// The size of the new sub-filter is the size of the last sub-filter multiplied by `expansion`.
338 /// If the number of elements to be stored in the filter is unknown,
339 /// we recommend that you use an `expansion` of 2 or more to reduce the number of sub-filters.
340 /// Otherwise, we recommend that you use an `expansion` of 1 to reduce memory consumption.
341 /// The default expansion value is 2.
342 #[must_use]
343 pub fn expansion(mut self, expansion: u32) -> Self {
344 self.expansion = Some(expansion);
345 self
346 }
347
348 /// Indicates that the filter should not be created if it does not already exist.
349 ///
350 /// If the filter does not yet exist, an error is returned rather than creating it automatically.
351 /// This may be used where a strict separation between filter creation and filter addition is desired.
352 /// It is an error to specify `nocreate` together with either [`capacity`](BfInsertOptions::capacity) or [`error`](BfInsertOptions::error).
353 #[must_use]
354 pub fn nocreate(mut self) -> Self {
355 self.nocreate = true;
356 self
357 }
358
359 /// Prevents the filter from creating additional sub-filters if initial capacity is reached.
360 ///
361 /// Non-scaling filters require slightly less memory than their scaling counterparts.
362 /// The filter returns an error when `capacity` is reached.
363 #[must_use]
364 pub fn nonscaling(mut self) -> Self {
365 self.nonscaling = true;
366 self
367 }
368}
369
370/// Options for the [`bf_reserve`](BloomCommands::bf_reserve) command.
371#[derive(Default, Serialize)]
372#[serde(rename_all(serialize = "UPPERCASE"))]
373pub struct BfReserveOptions {
374 #[serde(skip_serializing_if = "Option::is_none")]
375 expansion: Option<u32>,
376 #[serde(
377 skip_serializing_if = "std::ops::Not::not",
378 serialize_with = "serialize_flag"
379 )]
380 nonscaling: bool,
381}
382
383impl BfReserveOptions {
384 /// When `capacity` is reached, an additional sub-filter is created.
385 /// The size of the new sub-filter is the size of the last sub-filter multiplied by `expansion`.
386 /// If the number of elements to be stored in the filter is unknown,
387 /// we recommend that you use an `expansion` of 2 or more to reduce the number of sub-filters.
388 /// Otherwise, we recommend that you use an `expansion` of 1 to reduce memory consumption.
389 /// The default expansion value is 2.
390 #[must_use]
391 pub fn expansion(mut self, expansion: u32) -> Self {
392 self.expansion = Some(expansion);
393 self
394 }
395
396 /// Prevents the filter from creating additional sub-filters if initial capacity is reached.
397 ///
398 /// Non-scaling filters require slightly less memory than their scaling counterparts.
399 /// The filter returns an error when `capacity` is reached.
400 #[must_use]
401 pub fn nonscaling(mut self) -> Self {
402 self.nonscaling = true;
403 self
404 }
405}
406
407/// Result for the [`bf_scandump`](BloomCommands::bf_scandump) command.
408#[derive(Debug, Deserialize)]
409pub struct BfScanDumpResult {
410 pub iterator: i64,
411 pub data: BulkString,
412}