mongodb_gridfs/bucket/
delete.rs

1use crate::{bucket::GridFSBucket, GridFSError};
2use bson::{doc, oid::ObjectId, Document};
3use mongodb::options::DeleteOptions;
4
5impl GridFSBucket {
6    /**
7    Given a @id, delete this stored file’s files collection document and
8    associated chunks from a GridFS bucket.
9     [Spec](https://github.com/mongodb/specifications/blob/master/source/gridfs/gridfs-spec.rst#file-deletion)
10
11
12    ```rust
13     # use mongodb::Client;
14     # use mongodb::Database;
15     # use mongodb_gridfs::{options::GridFSBucketOptions};
16     use mongodb_gridfs::{GridFSBucket, GridFSError};
17     # use uuid::Uuid;
18     # fn db_name_new() -> String {
19     #     "test_".to_owned()
20     #         + Uuid::new_v4()
21     #             .hyphenated()
22     #             .encode_lower(&mut Uuid::encode_buffer())
23     # }
24     #
25     # #[tokio::main]
26     # async fn main() -> Result<(), GridFSError> {
27     #     let client = Client::with_uri_str(
28     #         &std::env::var("MONGO_URI").unwrap_or("mongodb://localhost:27017/".to_string()),
29     #     )
30     #     .await?;
31     #     let dbname = db_name_new();
32     #     let db: Database = client.database(&dbname);
33     let bucket = GridFSBucket::new(db.clone(), Some(GridFSBucketOptions::default()));
34     #     let id = bucket
35     #         .clone()
36     #         .upload_from_stream("test.txt", "test data".as_bytes(), None)
37     #         .await?;
38     #
39     bucket.delete(id).await?;
40     #
41     #     db.drop(None).await?;
42     #     Ok(())
43     # }
44    ```
45     # Errors
46
47     Raise [`GridFSError::FileNotFound`] when the requested id doesn't exists.
48    */
49    pub async fn delete(&self, id: ObjectId) -> Result<(), GridFSError> {
50        let dboptions = self.options.clone().unwrap_or_default();
51        let bucket_name = dboptions.bucket_name;
52        let file_collection = bucket_name.clone() + ".files";
53        let files = self.db.collection::<Document>(&file_collection);
54        let chunk_collection = bucket_name + ".chunks";
55        let chunks = self.db.collection::<Document>(&chunk_collection);
56
57        let mut delete_option = DeleteOptions::default();
58        if let Some(write_concern) = dboptions.write_concern.clone() {
59            delete_option.write_concern = Some(write_concern);
60        }
61
62        let delete_result = files
63            .delete_one(doc! {"_id":id}, delete_option.clone())
64            .await?;
65
66        // If there is no such file listed in the files collection,
67        // drivers MUST raise an error.
68        if delete_result.deleted_count == 0 {
69            return Err(GridFSError::FileNotFound());
70        }
71
72        chunks
73            .delete_many(doc! {"files_id":id}, delete_option)
74            .await?;
75        Ok(())
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::GridFSBucket;
82    use crate::{options::GridFSBucketOptions, GridFSError};
83    use bson::doc;
84    use bson::oid::ObjectId;
85    use bson::Document;
86    use mongodb::Client;
87    use mongodb::Database;
88    use uuid::Uuid;
89    fn db_name_new() -> String {
90        "test_".to_owned()
91            + Uuid::new_v4()
92                .hyphenated()
93                .encode_lower(&mut Uuid::encode_buffer())
94    }
95
96    #[tokio::test]
97    async fn delete_a_file() -> Result<(), GridFSError> {
98        let client = Client::with_uri_str(
99            &std::env::var("MONGO_URI").unwrap_or("mongodb://localhost:27017/".to_string()),
100        )
101        .await?;
102        let dbname = db_name_new();
103        let db: Database = client.database(&dbname);
104        let bucket = &GridFSBucket::new(db.clone(), Some(GridFSBucketOptions::default()));
105        let id = bucket
106            .clone()
107            .upload_from_stream("test.txt", "test data".as_bytes(), None)
108            .await?;
109
110        assert_eq!(id.to_hex(), id.to_hex());
111
112        bucket.delete(id).await?;
113
114        let count = db
115            .collection::<Document>("fs.files")
116            .count_documents(doc! { "_id": id }, None)
117            .await?;
118        assert_eq!(count, 0, "File should be deleted");
119
120        let count = db
121            .collection::<Document>("fs.chunks")
122            .count_documents(doc! { "files_id": id }, None)
123            .await?;
124        assert_eq!(count, 0, "Chunks should be deleted");
125
126        db.drop(None).await?;
127        Ok(())
128    }
129
130    #[tokio::test]
131    async fn delete_a_non_existant_file() -> Result<(), GridFSError> {
132        let client = Client::with_uri_str(
133            &std::env::var("MONGO_URI").unwrap_or("mongodb://localhost:27017/".to_string()),
134        )
135        .await?;
136        let dbname = db_name_new();
137        let db: Database = client.database(&dbname);
138        let bucket = &GridFSBucket::new(db.clone(), Some(GridFSBucketOptions::default()));
139        let id = ObjectId::new();
140
141        let result = bucket.delete(id).await;
142        assert!(result.is_err());
143
144        let count = db
145            .collection::<Document>("fs.files")
146            .count_documents(doc! { "_id": id }, None)
147            .await?;
148        assert_eq!(count, 0, "File should be deleted");
149
150        let count = db
151            .collection::<Document>("fs.chunks")
152            .count_documents(doc! { "files_id": id }, None)
153            .await?;
154        assert_eq!(count, 0, "Chunks should be deleted");
155
156        db.drop(None).await?;
157        Ok(())
158    }
159}