reduct_rs/bucket/
remove.rs

1// Copyright 2024 ReductStore
2// This Source Code Form is subject to the terms of the Mozilla Public
3//    License, v. 2.0. If a copy of the MPL was not distributed with this
4//    file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
6use crate::record::query::RemoveQueryBuilder;
7use crate::record::write_batched_records::WriteBatchType;
8use crate::{Bucket, RemoveRecordBuilder, WriteBatchBuilder};
9use http::Method;
10use reduct_base::error::ReductError;
11
12impl Bucket {
13    /// Remove an entry from the bucket.
14    ///
15    /// # Arguments
16    ///
17    /// * `entry` - The entry to remove.
18    ///
19    /// # Returns
20    ///
21    /// Returns an error if the entry could not be removed.
22    pub async fn remove_entry(&self, entry: &str) -> Result<(), ReductError> {
23        let request = self
24            .http_client
25            .request(Method::DELETE, &format!("/b/{}/{}", self.name, entry));
26        self.http_client.send_request(request).await?;
27        Ok(())
28    }
29
30    /// Remove a record from an entry.
31    ///
32    /// # Arguments
33    ///
34    /// * `entry` - The entry to remove.
35    ///
36    /// # Returns
37    ///
38    /// Returns an error if the record could not be removed.
39    pub fn remove_record(&self, entry: &str) -> RemoveRecordBuilder {
40        RemoveRecordBuilder::new(
41            self.name.clone(),
42            entry.to_string(),
43            self.http_client.clone(),
44        )
45    }
46
47    /// Remove records in a batch.
48    ///
49    /// # Arguments
50    ///
51    /// * `entry` - The entry to remove.
52    ///
53    /// # Returns
54    ///
55    /// Returns a write batch builder.
56    ///
57    /// # Example
58    ///
59    /// ```no_run
60    ///
61    /// use reduct_rs::{ReductClient, ReductError};
62    ///
63    /// #[tokio::main]
64    /// async fn main() -> Result<(), ReductError> {
65    ///     let client = ReductClient::builder()
66    ///         .url("https://play.reduct.store")
67    ///         .api_token("reductstore")
68    ///         .build();
69    ///     let bucket = client.get_bucket("datasets").await?;
70    ///     let batch = bucket.remove_batch("cats");
71    ///     let errors = batch.add_timestamp_us(1000).add_timestamp_us(5000).send().await?;
72    ///     Ok(())
73    /// }
74    /// ```
75    pub fn remove_batch(&self, entry: &str) -> WriteBatchBuilder {
76        WriteBatchBuilder::new(
77            self.name.clone(),
78            entry.to_string(),
79            self.http_client.clone(),
80            WriteBatchType::Remove,
81        )
82    }
83
84    /// Remove records in a query.
85    ///
86    /// # Arguments
87    ///
88    /// * `entry` - The entry to remove.
89    ///
90    /// # Returns
91    ///
92    /// Returns a remove query builder.
93    ///
94    /// # Example
95    ///
96    /// ```no_run
97    ///
98    /// use reduct_rs::{ReductClient, ReductError};
99    ///
100    /// #[tokio::main]
101    /// async fn main() -> Result<(), ReductError> {
102    ///     let client = ReductClient::builder()
103    ///         .url("https://play.reduct.store")
104    ///         .api_token("reductstore")
105    ///         .build();
106    ///
107    ///     let bucket = client.get_bucket("datasets").await?;
108    ///     let query = bucket.remove_query("cats");
109    ///     let removed_records = query.start_us(1000).stop_us(5000).send().await?;
110    ///     Ok(())
111    /// }
112    /// ```
113    pub fn remove_query(&self, entry: &str) -> RemoveQueryBuilder {
114        RemoveQueryBuilder::new(
115            self.name.clone(),
116            entry.to_string(),
117            self.http_client.clone(),
118        )
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125    use crate::bucket::tests::bucket;
126    use reduct_base::error::ErrorCode;
127    use rstest::rstest;
128    use serde_json::json;
129
130    #[rstest]
131    #[tokio::test]
132    async fn remove_entry(#[future] bucket: Bucket) {
133        let bucket: Bucket = bucket.await;
134        bucket.remove_entry("entry-1").await.unwrap();
135        assert_eq!(
136            bucket
137                .read_record("entry-1")
138                .send()
139                .await
140                .err()
141                .unwrap()
142                .status,
143            ErrorCode::NotFound
144        );
145    }
146
147    #[rstest]
148    #[tokio::test]
149    async fn remove_record(#[future] bucket: Bucket) {
150        let bucket: Bucket = bucket.await;
151        bucket
152            .remove_record("entry-1")
153            .timestamp_us(1000)
154            .send()
155            .await
156            .unwrap();
157        assert_eq!(
158            bucket
159                .read_record("entry-1")
160                .timestamp_us(1000)
161                .send()
162                .await
163                .err()
164                .unwrap()
165                .status,
166            ErrorCode::NotFound
167        );
168    }
169
170    #[rstest]
171    #[tokio::test]
172    async fn remove_batch(#[future] bucket: Bucket) {
173        let bucket: Bucket = bucket.await;
174
175        let batch = bucket.remove_batch("entry-1");
176        let errors = batch
177            .add_timestamp_us(1000)
178            .add_timestamp_us(5000)
179            .send()
180            .await
181            .unwrap();
182
183        assert_eq!(
184            bucket
185                .read_record("entry-1")
186                .send()
187                .await
188                .err()
189                .unwrap()
190                .status,
191            ErrorCode::NotFound
192        );
193
194        assert_eq!(errors.len(), 1);
195        assert_eq!(errors[&5000].status, ErrorCode::NotFound);
196    }
197
198    #[rstest]
199    #[tokio::test]
200    async fn remove_query(#[future] bucket: Bucket) {
201        let bucket: Bucket = bucket.await;
202
203        let query = bucket.remove_query("entry-1");
204        let removed_records = query.start_us(1000).stop_us(5000).send().await.unwrap();
205
206        assert_eq!(
207            bucket
208                .read_record("entry-1")
209                .timestamp_us(1000)
210                .send()
211                .await
212                .err()
213                .unwrap()
214                .status,
215            ErrorCode::NotFound
216        );
217
218        assert_eq!(removed_records, 1);
219    }
220
221    #[rstest]
222    #[tokio::test]
223    async fn test_remove_query_when(#[future] bucket: Bucket) {
224        let bucket: Bucket = bucket.await;
225        let query = bucket
226            .remove_query("entry-1")
227            .when(json!({
228                "&entry": { "$eq": 1}
229            }))
230            .send()
231            .await;
232
233        let removed_records = query.unwrap();
234        assert_eq!(removed_records, 1);
235    }
236
237    #[rstest]
238    #[tokio::test]
239    async fn test_remove_query_when_strict(#[future] bucket: Bucket) {
240        let bucket: Bucket = bucket.await;
241        let query = bucket
242            .remove_query("entry-1")
243            .when(json!({
244                "&NOT_EXIST": { "$eq": 1}
245            }))
246            .send()
247            .await;
248
249        let removed_records = query.unwrap();
250        assert_eq!(removed_records, 0);
251
252        let query = bucket
253            .remove_query("entry-1")
254            .when(json!({
255                "&NOT_EXIST": { "$eq": 1}
256            }))
257            .strict(true)
258            .send()
259            .await;
260
261        assert!(query.is_err());
262    }
263}