Skip to content

Commit c72830c

Browse files
authored
RUST-2090/gridfs helpers (#1351)
1 parent 3be42c8 commit c72830c

15 files changed

+966
-22
lines changed

src/action/gridfs.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ mod find;
77
mod rename;
88
mod upload;
99

10-
pub use delete::Delete;
10+
pub use delete::{Delete, DeleteByName};
1111
pub use download::{OpenDownloadStream, OpenDownloadStreamByName};
1212
pub use drop::Drop;
1313
pub use find::{Find, FindOne};
14-
pub use rename::Rename;
14+
pub use rename::{Rename, RenameByName};
1515
pub use upload::OpenUploadStream;

src/action/gridfs/delete.rs

+73
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,18 @@ impl GridFsBucket {
1717
pub fn delete(&self, id: Bson) -> Delete {
1818
Delete { bucket: self, id }
1919
}
20+
21+
/// Deletes the [`FilesCollectionDocument`] with the given name and its associated chunks from
22+
/// this bucket. This method returns an error if the name does not match any files in the
23+
/// bucket.
24+
///
25+
/// `await` will return [`Result<()>`].
26+
pub fn delete_by_name(&self, filename: impl Into<String>) -> DeleteByName {
27+
DeleteByName {
28+
bucket: self,
29+
filename: filename.into(),
30+
}
31+
}
2032
}
2133

2234
#[cfg(feature = "sync")]
@@ -29,6 +41,15 @@ impl crate::sync::gridfs::GridFsBucket {
2941
pub fn delete(&self, id: Bson) -> Delete {
3042
self.async_bucket.delete(id)
3143
}
44+
45+
/// Deletes the [`FilesCollectionDocument`] with the given name and its associated chunks from
46+
/// this bucket. This method returns an error if the name does not match any files in the
47+
/// bucket.
48+
///
49+
/// [`run`](DeleteByName::run) will return [`Result<()>`].
50+
pub fn delete_by_name(&self, filename: impl Into<String>) -> DeleteByName {
51+
self.async_bucket.delete_by_name(filename)
52+
}
3253
}
3354

3455
/// Deletes a specific [`FilesCollectionDocument`] and its associated chunks. Construct with
@@ -66,3 +87,55 @@ impl<'a> Action for Delete<'a> {
6687
Ok(())
6788
}
6889
}
90+
91+
/// Deletes a named [`FilesCollectionDocument`] and its associated chunks. Construct with
92+
/// [`GridFsBucket::delete_by_name`].
93+
#[must_use]
94+
pub struct DeleteByName<'a> {
95+
bucket: &'a GridFsBucket,
96+
filename: String,
97+
}
98+
99+
#[action_impl]
100+
impl<'a> Action for DeleteByName<'a> {
101+
type Future = DeleteByNameFuture;
102+
103+
async fn execute(self) -> Result<()> {
104+
use futures_util::stream::{StreamExt, TryStreamExt};
105+
let ids: Vec<_> = self
106+
.bucket
107+
.files()
108+
.find(doc! { "filename": self.filename.clone() })
109+
.projection(doc! { "_id": 1 })
110+
.await?
111+
.with_type::<bson::Document>()
112+
.map(|r| match r {
113+
Ok(mut d) => d
114+
.remove("_id")
115+
.ok_or_else(|| crate::error::Error::internal("_id field expected")),
116+
Err(e) => Err(e),
117+
})
118+
.try_collect()
119+
.await?;
120+
121+
let count = self
122+
.bucket
123+
.files()
124+
.delete_many(doc! { "_id": { "$in": ids.clone() } })
125+
.await?
126+
.deleted_count;
127+
self.bucket
128+
.chunks()
129+
.delete_many(doc! { "files_id": { "$in": ids } })
130+
.await?;
131+
132+
if count == 0 {
133+
return Err(ErrorKind::GridFs(GridFsErrorKind::FileNotFound {
134+
identifier: GridFsFileIdentifier::Filename(self.filename),
135+
})
136+
.into());
137+
}
138+
139+
Ok(())
140+
}
141+
}

src/action/gridfs/rename.rs

+66-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
use bson::{doc, Bson};
22

3-
use crate::{action::action_impl, error::Result, gridfs::GridFsBucket};
3+
use crate::{
4+
action::action_impl,
5+
error::{ErrorKind, GridFsErrorKind, GridFsFileIdentifier, Result},
6+
gridfs::GridFsBucket,
7+
};
48

59
impl GridFsBucket {
610
/// Renames the file with the given 'id' to the provided `new_filename`. This method returns an
@@ -14,6 +18,22 @@ impl GridFsBucket {
1418
new_filename: new_filename.into(),
1519
}
1620
}
21+
22+
/// Renames all revisions of the file with the given name to the provided `new_filename`. This
23+
/// method returns an error if the name does not match any files in the bucket.
24+
///
25+
/// `await` will return [`Result<()>`].
26+
pub fn rename_by_name(
27+
&self,
28+
filename: impl Into<String>,
29+
new_filename: impl Into<String>,
30+
) -> RenameByName {
31+
RenameByName {
32+
bucket: self,
33+
filename: filename.into(),
34+
new_filename: new_filename.into(),
35+
}
36+
}
1737
}
1838

1939
#[cfg(feature = "sync")]
@@ -25,6 +45,18 @@ impl crate::sync::gridfs::GridFsBucket {
2545
pub fn rename(&self, id: Bson, new_filename: impl Into<String>) -> Rename {
2646
self.async_bucket.rename(id, new_filename)
2747
}
48+
49+
/// Renames all revisions of the file with the given name to the provided `new_filename`. This
50+
/// method returns an error if the name does not match any files in the bucket.
51+
///
52+
/// [`run`](RenameByName::run) will return [`Result<()>`].
53+
pub fn rename_by_name(
54+
&self,
55+
filename: impl Into<String>,
56+
new_filename: impl Into<String>,
57+
) -> RenameByName {
58+
self.async_bucket.rename_by_name(filename, new_filename)
59+
}
2860
}
2961

3062
/// Renames a file. Construct with [`GridFsBucket::rename`].
@@ -51,3 +83,36 @@ impl<'a> Action for Rename<'a> {
5183
Ok(())
5284
}
5385
}
86+
87+
/// Renames a file selected by name. Construct with [`GridFsBucket::rename_by_name`].
88+
#[must_use]
89+
pub struct RenameByName<'a> {
90+
bucket: &'a GridFsBucket,
91+
filename: String,
92+
new_filename: String,
93+
}
94+
95+
#[action_impl]
96+
impl<'a> Action for RenameByName<'a> {
97+
type Future = RenameByNameFuture;
98+
99+
async fn execute(self) -> Result<()> {
100+
let count = self
101+
.bucket
102+
.files()
103+
.update_many(
104+
doc! { "filename": self.filename.clone() },
105+
doc! { "$set": { "filename": self.new_filename } },
106+
)
107+
.await?
108+
.matched_count;
109+
if count == 0 {
110+
return Err(ErrorKind::GridFs(GridFsErrorKind::FileNotFound {
111+
identifier: GridFsFileIdentifier::Filename(self.filename),
112+
})
113+
.into());
114+
}
115+
116+
Ok(())
117+
}
118+
}

src/test/spec/json/gridfs/delete.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@
497497
}
498498
},
499499
"expectError": {
500-
"isError": true
500+
"isClientError": true
501501
}
502502
}
503503
],
@@ -650,7 +650,7 @@
650650
}
651651
},
652652
"expectError": {
653-
"isError": true
653+
"isClientError": true
654654
}
655655
}
656656
],

src/test/spec/json/gridfs/delete.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ tests:
141141
object: *bucket0
142142
arguments:
143143
id: { $oid: "000000000000000000000000" }
144-
expectError: { isError: true } # FileNotFound
144+
expectError: { isClientError: true } # FileNotFound
145145
outcome:
146146
- collectionName: *bucket0_files_collectionName
147147
databaseName: *database0Name
@@ -170,7 +170,7 @@ tests:
170170
object: *bucket0
171171
arguments:
172172
id: { $oid: "000000000000000000000004" }
173-
expectError: { isError: true } # FileNotFound
173+
expectError: { isClientError: true } # FileNotFound
174174
outcome:
175175
- collectionName: *bucket0_files_collectionName
176176
databaseName: *database0Name

0 commit comments

Comments
 (0)