From 7b5206c25d367f33f57783d837abe0a616c28685 Mon Sep 17 00:00:00 2001 From: Kyle Kloberdanz Date: Wed, 24 Jan 2024 15:09:53 -0600 Subject: [PATCH 1/5] RUST-1652 Add a find_one method to GridFsBucket This also creates a new struct GridFsFindOneOptions, which is used to pass options to the new find_one method. --- src/gridfs.rs | 22 ++++++++++++++++++++-- src/gridfs/options.rs | 30 +++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/gridfs.rs b/src/gridfs.rs index 29c757581..d42e96599 100644 --- a/src/gridfs.rs +++ b/src/gridfs.rs @@ -13,7 +13,14 @@ use crate::{ bson::{doc, oid::ObjectId, Bson, DateTime, Document, RawBinaryRef}, cursor::Cursor, error::{Error, ErrorKind, GridFsErrorKind, GridFsFileIdentifier, Result}, - options::{CollectionOptions, FindOptions, ReadConcern, SelectionCriteria, WriteConcern}, + options::{ + CollectionOptions, + FindOneOptions, + FindOptions, + ReadConcern, + SelectionCriteria, + WriteConcern, + }, Collection, Database, }; @@ -221,7 +228,7 @@ impl GridFsBucket { Ok(()) } - /// Finds and returns the [`FilesCollectionDocument`]s within this bucket that match the given + /// Finds and returns the [`FilesCollectionDocument`]s within this bucket that matches the given /// filter. pub async fn find( &self, @@ -232,6 +239,17 @@ impl GridFsBucket { self.files().find(filter, find_options).await } + /// Finds and returns a single [`FilesCollectionDocument`] within this bucket that matches the + /// given filter. + pub async fn find_one( + &self, + filter: Document, + options: impl Into>, + ) -> Result> { + let find_options = options.into().map(FindOneOptions::from); + self.files().find_one(filter, find_options).await + } + /// Renames the file with the given 'id' to the provided `new_filename`. This method returns an /// error if the `id` does not match any files in the bucket. pub async fn rename(&self, id: Bson, new_filename: impl AsRef) -> Result<()> { diff --git a/src/gridfs/options.rs b/src/gridfs/options.rs index 594eb89f5..96ed2d1cc 100644 --- a/src/gridfs/options.rs +++ b/src/gridfs/options.rs @@ -5,7 +5,7 @@ use typed_builder::TypedBuilder; use crate::{ bson::Document, - options::{FindOptions, ReadConcern, SelectionCriteria, WriteConcern}, + options::{FindOneOptions, FindOptions, ReadConcern, SelectionCriteria, WriteConcern}, }; /// Contains the options for creating a [`GridFsBucket`](crate::gridfs::GridFsBucket). @@ -103,3 +103,31 @@ impl From for FindOptions { } } } + +/// Contains the options for finding a single +/// [`FilesCollectionDocument`](crate::gridfs::FilesCollectionDocument) in a +/// [`GridFsBucket`](crate::gridfs::GridFsBucket). +#[derive(Clone, Debug, Default, Deserialize, TypedBuilder)] +#[builder(field_defaults(default, setter(into)))] +#[non_exhaustive] +pub struct GridFsFindOneOptions { + /// The maximum amount of time to allow the query to run. + pub max_time: Option, + + /// The number of documents to skip before returning. + pub skip: Option, + + /// The order by which to sort results. Defaults to not sorting. + pub sort: Option, +} + +impl From for FindOneOptions { + fn from(options: GridFsFindOneOptions) -> Self { + Self { + max_time: options.max_time, + skip: options.skip, + sort: options.sort, + ..Default::default() + } + } +} From f65fc2df23580a66586bee7b8eea98d4cd7c8e21 Mon Sep 17 00:00:00 2001 From: Kyle Kloberdanz Date: Wed, 24 Jan 2024 15:11:00 -0600 Subject: [PATCH 2/5] Test the new GridFsBucket::find_one() method --- src/test/spec/gridfs.rs | 50 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/test/spec/gridfs.rs b/src/test/spec/gridfs.rs index 0f393c602..f3645b159 100644 --- a/src/test/spec/gridfs.rs +++ b/src/test/spec/gridfs.rs @@ -5,8 +5,8 @@ use futures_util::io::{AsyncReadExt, AsyncWriteExt}; use crate::{ bson::{doc, Bson, Document}, error::{Error, ErrorKind, GridFsErrorKind}, - gridfs::{GridFsBucket, GridFsUploadStream}, - options::{GridFsBucketOptions, GridFsUploadOptions}, + gridfs::{GridFsBucket, GridFsFindOneOptions, GridFsUploadStream}, + options::{FindOneOptions, GridFsBucketOptions, GridFsUploadOptions}, runtime, test::{ get_client_options, @@ -332,3 +332,49 @@ async fn assert_no_chunks_written(bucket: &GridFsBucket, id: &Bson) { .unwrap() .is_none()); } + +#[cfg_attr(feature = "tokio-runtime", tokio::test)] +#[cfg_attr(feature = "async-std-runtime", async_std::test)] +async fn test_gridfs_bucket_find_one() { + let data = &[1, 2, 3, 4]; + let client = TestClient::new().await; + + let options = GridFsBucketOptions::default(); + let bucket = client.database("gridfs_find_one").gridfs_bucket(options); + + let filename = String::from("somefile"); + let mut upload_stream = bucket.open_upload_stream(&filename, None); + upload_stream.write_all(data).await.unwrap(); + upload_stream.close().await.unwrap(); + + let found = bucket + .find_one(doc! { "_id": upload_stream.id() }, None) + .await + .unwrap() + .unwrap(); + + assert_eq!(&found.id, upload_stream.id()); + assert_eq!(found.length, 4); + assert_eq!(found.filename, Some(filename)); +} + +#[cfg_attr(feature = "tokio-runtime", tokio::test)] +#[cfg_attr(feature = "async-std-runtime", async_std::test)] +async fn test_gridfs_find_one_options_from() { + let default_options = GridFsFindOneOptions::default(); + let find_one_options = FindOneOptions::from(default_options); + assert_eq!(find_one_options.max_time, None); + assert_eq!(find_one_options.skip, None); + assert_eq!(find_one_options.sort, None); + + let options = GridFsFindOneOptions::builder() + .sort(doc! { "foo": -1 }) + .skip(1) + .max_time(Duration::from_millis(42)) + .build(); + + let find_one_options = FindOneOptions::from(options); + assert_eq!(find_one_options.max_time, Some(Duration::from_millis(42))); + assert_eq!(find_one_options.skip, Some(1)); + assert_eq!(find_one_options.sort, Some(doc! {"foo": -1})); +} From df0575d8da66b69c7bdb07f27b169e34c3adbaa6 Mon Sep 17 00:00:00 2001 From: Kyle Kloberdanz Date: Wed, 24 Jan 2024 15:12:47 -0600 Subject: [PATCH 3/5] Use new GridFsBucket.find_one() method Convert calls to GridFsBucket.files().find_one() to the equivalent call to GridFsBucket.find_one() --- src/gridfs/download.rs | 2 +- src/test/spec/gridfs.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gridfs/download.rs b/src/gridfs/download.rs index c996394c0..ef23228f6 100644 --- a/src/gridfs/download.rs +++ b/src/gridfs/download.rs @@ -22,7 +22,7 @@ use crate::{ // Utility functions for finding files within the bucket. impl GridFsBucket { async fn find_file_by_id(&self, id: &Bson) -> Result { - match self.files().find_one(doc! { "_id": id }, None).await? { + match self.find_one(doc! { "_id": id }, None).await? { Some(file) => Ok(file), None => Err(ErrorKind::GridFs(GridFsErrorKind::FileNotFound { identifier: GridFsFileIdentifier::Id(id.clone()), diff --git a/src/test/spec/gridfs.rs b/src/test/spec/gridfs.rs index f3645b159..52c872e3a 100644 --- a/src/test/spec/gridfs.rs +++ b/src/test/spec/gridfs.rs @@ -120,7 +120,6 @@ async fn upload_test(bucket: &GridFsBucket, data: &[u8], options: Option Date: Thu, 25 Jan 2024 11:01:33 -0600 Subject: [PATCH 4/5] Update src/gridfs.rs Co-authored-by: Isabel Atkinson --- src/gridfs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gridfs.rs b/src/gridfs.rs index d42e96599..263f0f70c 100644 --- a/src/gridfs.rs +++ b/src/gridfs.rs @@ -228,7 +228,7 @@ impl GridFsBucket { Ok(()) } - /// Finds and returns the [`FilesCollectionDocument`]s within this bucket that matches the given + /// Finds and returns the [`FilesCollectionDocument`]s within this bucket that match the given /// filter. pub async fn find( &self, From edb26e275dfc7a4748b7fd76f09027d624605f5d Mon Sep 17 00:00:00 2001 From: Kyle Kloberdanz Date: Thu, 25 Jan 2024 11:03:43 -0600 Subject: [PATCH 5/5] Remove async from find one options test --- src/test/spec/gridfs.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/spec/gridfs.rs b/src/test/spec/gridfs.rs index 52c872e3a..bf2be6cae 100644 --- a/src/test/spec/gridfs.rs +++ b/src/test/spec/gridfs.rs @@ -357,9 +357,8 @@ async fn test_gridfs_bucket_find_one() { assert_eq!(found.filename, Some(filename)); } -#[cfg_attr(feature = "tokio-runtime", tokio::test)] -#[cfg_attr(feature = "async-std-runtime", async_std::test)] -async fn test_gridfs_find_one_options_from() { +#[test] +fn test_gridfs_find_one_options_from() { let default_options = GridFsFindOneOptions::default(); let find_one_options = FindOneOptions::from(default_options); assert_eq!(find_one_options.max_time, None);