From 34b73a1e80b9e2cd038827fc4032b275d2fc99b2 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Thu, 2 Nov 2023 11:59:21 -0400 Subject: [PATCH 01/21] API outline --- src/lib.rs | 3 +- src/options.rs | 1 + src/search_index.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 src/search_index.rs diff --git a/src/lib.rs b/src/lib.rs index bb59688ac..c30b8a591 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -337,6 +337,7 @@ mod index; mod operation; pub mod results; pub(crate) mod runtime; +mod search_index; mod sdam; mod selection_criteria; mod serde_util; @@ -362,7 +363,7 @@ pub use crate::{ gridfs::{GridFsBucket, GridFsDownloadStream, GridFsUploadStream}, }; -pub use {client::session::ClusterTime, coll::Namespace, index::IndexModel, sdam::public::*}; +pub use {client::session::ClusterTime, coll::Namespace, index::IndexModel, sdam::public::*, search_index::{SearchIndexModel, SearchIndexView}}; #[cfg(all(feature = "tokio-runtime", feature = "sync",))] compile_error!( diff --git a/src/options.rs b/src/options.rs index 2851fb009..a96ec7967 100644 --- a/src/options.rs +++ b/src/options.rs @@ -25,6 +25,7 @@ pub use crate::{ db::options::*, gridfs::options::*, index::options::*, + search_index::options::*, selection_criteria::*, }; diff --git a/src/search_index.rs b/src/search_index.rs new file mode 100644 index 000000000..beddbe8f9 --- /dev/null +++ b/src/search_index.rs @@ -0,0 +1,67 @@ +use crate::{bson::Document, Collection, error::Result, coll::options::AggregateOptions, Cursor}; +use self::options::*; + +use typed_builder::TypedBuilder; + +impl Collection { + /// Convenience method for creating a single search index. + pub async fn create_search_index(model: &SearchIndexModel, options: impl Into>) -> Result { + todo!() + } + + /// Creates multiple search indexes on the collection. + pub async fn create_search_indexes(models: impl IntoIterator, options: impl Into>) -> Result> { + todo!() + } + + /// Updates the search index with the given name to use the provided definition. + pub async fn update_search_index(name: &str, definition: &Document, options: impl Into>) -> Result<()> { + todo!() + } + + /// Drops the search index with the given name. + pub async fn drop_search_index(name: &str, options: impl Into>) -> Result<()> { + todo!() + } + + /// Gets index information for one or more search indexes in the collection. + /// + /// If name is not specified, information for all indexes on the specified collection will be returned. + pub async fn list_search_indexes(name: impl Into>, aggregation_options: impl Into>, list_index_options: impl Into>) -> Result> { + todo!() + } +} + +#[derive(Debug, Clone, Default, TypedBuilder)] +#[non_exhaustive] +pub struct SearchIndexModel { + /// The definition for this index. + pub definition: Document, + + /// The name for this index, if present. + pub name: Option, +} + +pub mod options { + use typed_builder::TypedBuilder; + + /// Options for [Collection::create_search_index]. Present to allow additional options to be added in the future as a non-breaking change. + #[derive(Clone, Debug, Default, TypedBuilder)] + #[non_exhaustive] + pub struct CreateSearchIndexOptions { } + + /// Options for [Collection::update_search_index]. Present to allow additional options to be added in the future as a non-breaking change. + #[derive(Clone, Debug, Default, TypedBuilder)] + #[non_exhaustive] + pub struct UpdateSearchIndexOptions { } + + /// Options for [Collection::list_search_indexes]. Present to allow additional options to be added in the future as a non-breaking change. + #[derive(Clone, Debug, Default, TypedBuilder)] + #[non_exhaustive] + pub struct ListSearchIndexOptions { } + + /// Options for [Collection::drop_search_index]. Present to allow additional options to be added in the future as a non-breaking change. + #[derive(Clone, Debug, Default, TypedBuilder)] + #[non_exhaustive] + pub struct DropSearchIndexOptions { } +} \ No newline at end of file From ee9f500040b5b1f90354900f1e4188dccdbc1726 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Mon, 6 Nov 2023 10:04:24 -0500 Subject: [PATCH 02/21] create_search_index impl --- src/search_index.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/search_index.rs b/src/search_index.rs index beddbe8f9..5c9499579 100644 --- a/src/search_index.rs +++ b/src/search_index.rs @@ -1,33 +1,37 @@ -use crate::{bson::Document, Collection, error::Result, coll::options::AggregateOptions, Cursor}; +use crate::{bson::Document, Collection, error::{Error, Result}, coll::options::AggregateOptions, Cursor}; use self::options::*; use typed_builder::TypedBuilder; impl Collection { /// Convenience method for creating a single search index. - pub async fn create_search_index(model: &SearchIndexModel, options: impl Into>) -> Result { - todo!() + pub async fn create_search_index(&self, model: &SearchIndexModel, options: impl Into>) -> Result { + let mut names = self.create_search_indexes(Some(model), options).await?; + match names.len() { + 1 => Ok(names.pop().unwrap()), + n => Err(Error::internal(format!("expected 1 index name, got {}", names.len()))), + } } /// Creates multiple search indexes on the collection. - pub async fn create_search_indexes(models: impl IntoIterator, options: impl Into>) -> Result> { + pub async fn create_search_indexes(&self, models: impl IntoIterator, options: impl Into>) -> Result> { todo!() } /// Updates the search index with the given name to use the provided definition. - pub async fn update_search_index(name: &str, definition: &Document, options: impl Into>) -> Result<()> { + pub async fn update_search_index(&self, name: &str, definition: &Document, options: impl Into>) -> Result<()> { todo!() } /// Drops the search index with the given name. - pub async fn drop_search_index(name: &str, options: impl Into>) -> Result<()> { + pub async fn drop_search_index(&self, name: &str, options: impl Into>) -> Result<()> { todo!() } /// Gets index information for one or more search indexes in the collection. /// /// If name is not specified, information for all indexes on the specified collection will be returned. - pub async fn list_search_indexes(name: impl Into>, aggregation_options: impl Into>, list_index_options: impl Into>) -> Result> { + pub async fn list_search_indexes(&self, name: impl Into>, aggregation_options: impl Into>, list_index_options: impl Into>) -> Result> { todo!() } } From 7bd3649554c8ae50b513546f5d453b3f489a556c Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Mon, 6 Nov 2023 10:51:38 -0500 Subject: [PATCH 03/21] create_search_indexes impl --- src/operation.rs | 2 + src/operation/create_search_indexes.rs | 56 ++++++++++++++++++++++++++ src/search_index.rs | 10 +++-- 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 src/operation/create_search_indexes.rs diff --git a/src/operation.rs b/src/operation.rs index 67358171c..30c0c8849 100644 --- a/src/operation.rs +++ b/src/operation.rs @@ -5,6 +5,7 @@ mod count; mod count_documents; mod create; mod create_indexes; +mod create_search_indexes; mod delete; mod distinct; mod drop_collection; @@ -57,6 +58,7 @@ pub(crate) use count::Count; pub(crate) use count_documents::CountDocuments; pub(crate) use create::Create; pub(crate) use create_indexes::CreateIndexes; +pub(crate) use create_search_indexes::CreateSearchIndexes; pub(crate) use delete::Delete; pub(crate) use distinct::Distinct; pub(crate) use drop_collection::DropCollection; diff --git a/src/operation/create_search_indexes.rs b/src/operation/create_search_indexes.rs new file mode 100644 index 000000000..f3547bcb8 --- /dev/null +++ b/src/operation/create_search_indexes.rs @@ -0,0 +1,56 @@ +use bson::{Document, doc}; +use serde::Deserialize; + +use crate::{SearchIndexModel, Namespace, error::Result, cmap::Command}; + +use super::OperationWithDefaults; + +#[derive(Debug)] +pub(crate) struct CreateSearchIndexes { + ns: Namespace, + indexes: Vec, +} + +impl CreateSearchIndexes { + pub(crate) fn new(ns: Namespace, indexes: Vec) -> Self { + Self { ns, indexes } + } +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct CreateSearchIndexesResponse { + indexes_created: Vec, +} + +#[derive(Debug, Deserialize)] +struct CreatedSearchIndex { + id: String, + name: String, +} + +impl OperationWithDefaults for CreateSearchIndexes { + type O = Vec; + type Command = Document; + const NAME: &'static str = "createSearchIndexes"; + + fn build(&mut self, _description: &crate::cmap::StreamDescription) -> Result { + Ok(Command::new( + Self::NAME.to_string(), + self.ns.db.clone(), + doc! { + Self::NAME: self.ns.coll.clone(), + "indexes": bson::to_bson(&self.indexes)?, + } + )) + } + + fn handle_response( + &self, + response: crate::cmap::RawCommandResponse, + _description: &crate::cmap::StreamDescription, + ) -> Result { + let response: CreateSearchIndexesResponse = response.body()?; + Ok(response.indexes_created.into_iter().map(|ci| ci.name).collect()) + } +} \ No newline at end of file diff --git a/src/search_index.rs b/src/search_index.rs index 5c9499579..86f4390ae 100644 --- a/src/search_index.rs +++ b/src/search_index.rs @@ -1,6 +1,7 @@ -use crate::{bson::Document, Collection, error::{Error, Result}, coll::options::AggregateOptions, Cursor}; +use crate::{bson::Document, Collection, error::{Error, Result}, coll::options::AggregateOptions, Cursor, operation::CreateSearchIndexes}; use self::options::*; +use serde::Serialize; use typed_builder::TypedBuilder; impl Collection { @@ -14,8 +15,9 @@ impl Collection { } /// Creates multiple search indexes on the collection. - pub async fn create_search_indexes(&self, models: impl IntoIterator, options: impl Into>) -> Result> { - todo!() + pub async fn create_search_indexes(&self, models: impl IntoIterator, _options: impl Into>) -> Result> { + let op = CreateSearchIndexes::new(self.namespace(), models.into_iter().cloned().collect()); + self.client().execute_operation(op, None).await } /// Updates the search index with the given name to use the provided definition. @@ -36,7 +38,7 @@ impl Collection { } } -#[derive(Debug, Clone, Default, TypedBuilder)] +#[derive(Debug, Clone, Default, TypedBuilder, Serialize)] #[non_exhaustive] pub struct SearchIndexModel { /// The definition for this index. From 18f0a80b58641b5ef0add3914675635b9f680256 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Mon, 6 Nov 2023 11:05:55 -0500 Subject: [PATCH 04/21] update_search_index impl --- src/lib.rs | 2 +- src/operation.rs | 2 ++ src/operation/create_search_indexes.rs | 1 + src/operation/update_search_index.rs | 49 ++++++++++++++++++++++++++ src/search_index.rs | 7 ++-- 5 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 src/operation/update_search_index.rs diff --git a/src/lib.rs b/src/lib.rs index c30b8a591..c09e91f14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -363,7 +363,7 @@ pub use crate::{ gridfs::{GridFsBucket, GridFsDownloadStream, GridFsUploadStream}, }; -pub use {client::session::ClusterTime, coll::Namespace, index::IndexModel, sdam::public::*, search_index::{SearchIndexModel, SearchIndexView}}; +pub use {client::session::ClusterTime, coll::Namespace, index::IndexModel, sdam::public::*, search_index::SearchIndexModel}; #[cfg(all(feature = "tokio-runtime", feature = "sync",))] compile_error!( diff --git a/src/operation.rs b/src/operation.rs index 30c0c8849..246209c8d 100644 --- a/src/operation.rs +++ b/src/operation.rs @@ -22,6 +22,7 @@ mod raw_output; mod run_command; mod run_cursor_command; mod update; +mod update_search_index; #[cfg(test)] mod test; @@ -76,6 +77,7 @@ pub(crate) use raw_output::RawOutput; pub(crate) use run_command::RunCommand; pub(crate) use run_cursor_command::RunCursorCommand; pub(crate) use update::{Update, UpdateOrReplace}; +pub(crate) use update_search_index::UpdateSearchIndex; const SERVER_4_2_0_WIRE_VERSION: i32 = 8; const SERVER_4_4_0_WIRE_VERSION: i32 = 9; diff --git a/src/operation/create_search_indexes.rs b/src/operation/create_search_indexes.rs index f3547bcb8..55e896a06 100644 --- a/src/operation/create_search_indexes.rs +++ b/src/operation/create_search_indexes.rs @@ -25,6 +25,7 @@ struct CreateSearchIndexesResponse { #[derive(Debug, Deserialize)] struct CreatedSearchIndex { + #[allow(unused)] id: String, name: String, } diff --git a/src/operation/update_search_index.rs b/src/operation/update_search_index.rs new file mode 100644 index 000000000..8a57816ae --- /dev/null +++ b/src/operation/update_search_index.rs @@ -0,0 +1,49 @@ +use bson::{Document, doc}; + +use crate::{Namespace, cmap::Command}; + +use super::OperationWithDefaults; + + +#[derive(Debug)] +pub(crate) struct UpdateSearchIndex { + ns: Namespace, + name: String, + definition: Document, +} + +impl UpdateSearchIndex { + pub(crate) fn new( + ns: Namespace, + name: String, + definition: Document, + ) -> Self { + Self { ns, name, definition } + } +} + +impl OperationWithDefaults for UpdateSearchIndex { + type O = (); + type Command = Document; + const NAME: &'static str = "updateSearchIndex"; + + fn build(&mut self, _description: &crate::cmap::StreamDescription) -> crate::error::Result> { + Ok(Command::new( + Self::NAME.to_string(), + self.ns.db.clone(), + doc! { + Self::NAME: self.ns.coll.clone(), + "name": &self.name, + "definition": &self.definition, + }, + )) + } + + fn handle_response( + &self, + response: crate::cmap::RawCommandResponse, + _description: &crate::cmap::StreamDescription, + ) -> crate::error::Result { + response.body() + } +} \ No newline at end of file diff --git a/src/search_index.rs b/src/search_index.rs index 86f4390ae..d673bc42c 100644 --- a/src/search_index.rs +++ b/src/search_index.rs @@ -1,4 +1,4 @@ -use crate::{bson::Document, Collection, error::{Error, Result}, coll::options::AggregateOptions, Cursor, operation::CreateSearchIndexes}; +use crate::{bson::Document, Collection, error::{Error, Result}, coll::options::AggregateOptions, Cursor, operation::{CreateSearchIndexes, UpdateSearchIndex}}; use self::options::*; use serde::Serialize; @@ -21,8 +21,9 @@ impl Collection { } /// Updates the search index with the given name to use the provided definition. - pub async fn update_search_index(&self, name: &str, definition: &Document, options: impl Into>) -> Result<()> { - todo!() + pub async fn update_search_index(&self, name: &str, definition: &Document, _options: impl Into>) -> Result<()> { + let op = UpdateSearchIndex::new(self.namespace(), name.to_string(), definition.clone()); + self.client().execute_operation(op, None).await } /// Drops the search index with the given name. From 34c98f5c7c393da0b4a6acb2db9928c9da4db064 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Mon, 6 Nov 2023 11:12:09 -0500 Subject: [PATCH 05/21] refactor --- src/operation.rs | 6 +- src/operation/create_search_indexes.rs | 57 -------------- src/operation/search_index.rs | 100 +++++++++++++++++++++++++ src/operation/update_search_index.rs | 49 ------------ 4 files changed, 102 insertions(+), 110 deletions(-) delete mode 100644 src/operation/create_search_indexes.rs create mode 100644 src/operation/search_index.rs delete mode 100644 src/operation/update_search_index.rs diff --git a/src/operation.rs b/src/operation.rs index 246209c8d..956ea070a 100644 --- a/src/operation.rs +++ b/src/operation.rs @@ -5,7 +5,7 @@ mod count; mod count_documents; mod create; mod create_indexes; -mod create_search_indexes; +mod search_index; mod delete; mod distinct; mod drop_collection; @@ -22,7 +22,6 @@ mod raw_output; mod run_command; mod run_cursor_command; mod update; -mod update_search_index; #[cfg(test)] mod test; @@ -59,7 +58,7 @@ pub(crate) use count::Count; pub(crate) use count_documents::CountDocuments; pub(crate) use create::Create; pub(crate) use create_indexes::CreateIndexes; -pub(crate) use create_search_indexes::CreateSearchIndexes; +pub(crate) use search_index::{CreateSearchIndexes, UpdateSearchIndex}; pub(crate) use delete::Delete; pub(crate) use distinct::Distinct; pub(crate) use drop_collection::DropCollection; @@ -77,7 +76,6 @@ pub(crate) use raw_output::RawOutput; pub(crate) use run_command::RunCommand; pub(crate) use run_cursor_command::RunCursorCommand; pub(crate) use update::{Update, UpdateOrReplace}; -pub(crate) use update_search_index::UpdateSearchIndex; const SERVER_4_2_0_WIRE_VERSION: i32 = 8; const SERVER_4_4_0_WIRE_VERSION: i32 = 9; diff --git a/src/operation/create_search_indexes.rs b/src/operation/create_search_indexes.rs deleted file mode 100644 index 55e896a06..000000000 --- a/src/operation/create_search_indexes.rs +++ /dev/null @@ -1,57 +0,0 @@ -use bson::{Document, doc}; -use serde::Deserialize; - -use crate::{SearchIndexModel, Namespace, error::Result, cmap::Command}; - -use super::OperationWithDefaults; - -#[derive(Debug)] -pub(crate) struct CreateSearchIndexes { - ns: Namespace, - indexes: Vec, -} - -impl CreateSearchIndexes { - pub(crate) fn new(ns: Namespace, indexes: Vec) -> Self { - Self { ns, indexes } - } -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct CreateSearchIndexesResponse { - indexes_created: Vec, -} - -#[derive(Debug, Deserialize)] -struct CreatedSearchIndex { - #[allow(unused)] - id: String, - name: String, -} - -impl OperationWithDefaults for CreateSearchIndexes { - type O = Vec; - type Command = Document; - const NAME: &'static str = "createSearchIndexes"; - - fn build(&mut self, _description: &crate::cmap::StreamDescription) -> Result { - Ok(Command::new( - Self::NAME.to_string(), - self.ns.db.clone(), - doc! { - Self::NAME: self.ns.coll.clone(), - "indexes": bson::to_bson(&self.indexes)?, - } - )) - } - - fn handle_response( - &self, - response: crate::cmap::RawCommandResponse, - _description: &crate::cmap::StreamDescription, - ) -> Result { - let response: CreateSearchIndexesResponse = response.body()?; - Ok(response.indexes_created.into_iter().map(|ci| ci.name).collect()) - } -} \ No newline at end of file diff --git a/src/operation/search_index.rs b/src/operation/search_index.rs new file mode 100644 index 000000000..162418bec --- /dev/null +++ b/src/operation/search_index.rs @@ -0,0 +1,100 @@ +use bson::{Document, doc}; +use serde::Deserialize; + +use crate::{SearchIndexModel, Namespace, error::Result, cmap::Command}; + +use super::OperationWithDefaults; + +#[derive(Debug)] +pub(crate) struct CreateSearchIndexes { + ns: Namespace, + indexes: Vec, +} + +impl CreateSearchIndexes { + pub(crate) fn new(ns: Namespace, indexes: Vec) -> Self { + Self { ns, indexes } + } +} + +impl OperationWithDefaults for CreateSearchIndexes { + type O = Vec; + type Command = Document; + const NAME: &'static str = "createSearchIndexes"; + + fn build(&mut self, _description: &crate::cmap::StreamDescription) -> Result { + Ok(Command::new( + Self::NAME.to_string(), + self.ns.db.clone(), + doc! { + Self::NAME: self.ns.coll.clone(), + "indexes": bson::to_bson(&self.indexes)?, + } + )) + } + + fn handle_response( + &self, + response: crate::cmap::RawCommandResponse, + _description: &crate::cmap::StreamDescription, + ) -> Result { + #[derive(Debug, Deserialize)] + #[serde(rename_all = "camelCase")] + struct Response { + indexes_created: Vec, + } + + #[derive(Debug, Deserialize)] + struct CreatedIndex { + #[allow(unused)] + id: String, + name: String, + } + + let response: Response = response.body()?; + Ok(response.indexes_created.into_iter().map(|ci| ci.name).collect()) + } +} + +#[derive(Debug)] +pub(crate) struct UpdateSearchIndex { + ns: Namespace, + name: String, + definition: Document, +} + +impl UpdateSearchIndex { + pub(crate) fn new( + ns: Namespace, + name: String, + definition: Document, + ) -> Self { + Self { ns, name, definition } + } +} + +impl OperationWithDefaults for UpdateSearchIndex { + type O = (); + type Command = Document; + const NAME: &'static str = "updateSearchIndex"; + + fn build(&mut self, _description: &crate::cmap::StreamDescription) -> crate::error::Result> { + Ok(Command::new( + Self::NAME.to_string(), + self.ns.db.clone(), + doc! { + Self::NAME: self.ns.coll.clone(), + "name": &self.name, + "definition": &self.definition, + }, + )) + } + + fn handle_response( + &self, + response: crate::cmap::RawCommandResponse, + _description: &crate::cmap::StreamDescription, + ) -> crate::error::Result { + response.body() + } +} \ No newline at end of file diff --git a/src/operation/update_search_index.rs b/src/operation/update_search_index.rs deleted file mode 100644 index 8a57816ae..000000000 --- a/src/operation/update_search_index.rs +++ /dev/null @@ -1,49 +0,0 @@ -use bson::{Document, doc}; - -use crate::{Namespace, cmap::Command}; - -use super::OperationWithDefaults; - - -#[derive(Debug)] -pub(crate) struct UpdateSearchIndex { - ns: Namespace, - name: String, - definition: Document, -} - -impl UpdateSearchIndex { - pub(crate) fn new( - ns: Namespace, - name: String, - definition: Document, - ) -> Self { - Self { ns, name, definition } - } -} - -impl OperationWithDefaults for UpdateSearchIndex { - type O = (); - type Command = Document; - const NAME: &'static str = "updateSearchIndex"; - - fn build(&mut self, _description: &crate::cmap::StreamDescription) -> crate::error::Result> { - Ok(Command::new( - Self::NAME.to_string(), - self.ns.db.clone(), - doc! { - Self::NAME: self.ns.coll.clone(), - "name": &self.name, - "definition": &self.definition, - }, - )) - } - - fn handle_response( - &self, - response: crate::cmap::RawCommandResponse, - _description: &crate::cmap::StreamDescription, - ) -> crate::error::Result { - response.body() - } -} \ No newline at end of file From fdb8a413c65824767af706526845e365afcf15c7 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Mon, 6 Nov 2023 11:23:58 -0500 Subject: [PATCH 06/21] drop_search_index impl --- src/operation.rs | 2 +- src/operation/search_index.rs | 40 +++++++++++++++++++++++++++++++++++ src/search_index.rs | 7 +++--- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/operation.rs b/src/operation.rs index 956ea070a..c9e6348d3 100644 --- a/src/operation.rs +++ b/src/operation.rs @@ -58,7 +58,7 @@ pub(crate) use count::Count; pub(crate) use count_documents::CountDocuments; pub(crate) use create::Create; pub(crate) use create_indexes::CreateIndexes; -pub(crate) use search_index::{CreateSearchIndexes, UpdateSearchIndex}; +pub(crate) use search_index::{CreateSearchIndexes, UpdateSearchIndex, DropSearchIndex}; pub(crate) use delete::Delete; pub(crate) use distinct::Distinct; pub(crate) use drop_collection::DropCollection; diff --git a/src/operation/search_index.rs b/src/operation/search_index.rs index 162418bec..2289d3708 100644 --- a/src/operation/search_index.rs +++ b/src/operation/search_index.rs @@ -97,4 +97,44 @@ impl OperationWithDefaults for UpdateSearchIndex { ) -> crate::error::Result { response.body() } +} + +#[derive(Debug)] +pub(crate) struct DropSearchIndex { + ns: Namespace, + name: String, +} + +impl DropSearchIndex { + pub(crate) fn new( + ns: Namespace, + name: String, + ) -> Self { + Self { ns, name } + } +} + +impl OperationWithDefaults for DropSearchIndex { + type O = (); + type Command = Document; + const NAME: &'static str = "dropSearchIndex"; + + fn build(&mut self, _description: &crate::cmap::StreamDescription) -> Result> { + Ok(Command::new( + Self::NAME.to_string(), + self.ns.db.clone(), + doc! { + Self::NAME: self.ns.coll.clone(), + "name": &self.name, + }, + )) + } + + fn handle_response( + &self, + response: crate::cmap::RawCommandResponse, + _description: &crate::cmap::StreamDescription, + ) -> Result { + response.body() + } } \ No newline at end of file diff --git a/src/search_index.rs b/src/search_index.rs index d673bc42c..5a989cee3 100644 --- a/src/search_index.rs +++ b/src/search_index.rs @@ -1,4 +1,4 @@ -use crate::{bson::Document, Collection, error::{Error, Result}, coll::options::AggregateOptions, Cursor, operation::{CreateSearchIndexes, UpdateSearchIndex}}; +use crate::{bson::Document, Collection, error::{Error, Result}, coll::options::AggregateOptions, Cursor, operation::{CreateSearchIndexes, UpdateSearchIndex, DropSearchIndex}}; use self::options::*; use serde::Serialize; @@ -27,8 +27,9 @@ impl Collection { } /// Drops the search index with the given name. - pub async fn drop_search_index(&self, name: &str, options: impl Into>) -> Result<()> { - todo!() + pub async fn drop_search_index(&self, name: &str, _options: impl Into>) -> Result<()> { + let op = DropSearchIndex::new(self.namespace(), name.to_string()); + self.client().execute_operation(op, None).await } /// Gets index information for one or more search indexes in the collection. From 0587f6d73b93641ba2fbd1d88871957422ceb6bc Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Mon, 6 Nov 2023 11:31:51 -0500 Subject: [PATCH 07/21] list_search_indexes impl --- src/search_index.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/search_index.rs b/src/search_index.rs index 5a989cee3..a6de20899 100644 --- a/src/search_index.rs +++ b/src/search_index.rs @@ -1,6 +1,7 @@ use crate::{bson::Document, Collection, error::{Error, Result}, coll::options::AggregateOptions, Cursor, operation::{CreateSearchIndexes, UpdateSearchIndex, DropSearchIndex}}; use self::options::*; +use bson::doc; use serde::Serialize; use typed_builder::TypedBuilder; @@ -10,7 +11,7 @@ impl Collection { let mut names = self.create_search_indexes(Some(model), options).await?; match names.len() { 1 => Ok(names.pop().unwrap()), - n => Err(Error::internal(format!("expected 1 index name, got {}", names.len()))), + n => Err(Error::internal(format!("expected 1 index name, got {}", n))), } } @@ -35,11 +36,23 @@ impl Collection { /// Gets index information for one or more search indexes in the collection. /// /// If name is not specified, information for all indexes on the specified collection will be returned. - pub async fn list_search_indexes(&self, name: impl Into>, aggregation_options: impl Into>, list_index_options: impl Into>) -> Result> { - todo!() + pub async fn list_search_indexes(&self, name: impl Into>, aggregation_options: impl Into>, _list_index_options: impl Into>) -> Result> { + let mut inner = doc! { }; + if let Some(name) = name.into() { + inner.insert("name", name); + } + self.aggregate( + vec![ + doc! { + "$listSearchIndexes": inner, + } + ], + aggregation_options, + ).await } } +/// Specifies the options for a search index. #[derive(Debug, Clone, Default, TypedBuilder, Serialize)] #[non_exhaustive] pub struct SearchIndexModel { From 7f5ac5f11e6d71b0307ed4cc8663e051a38879b8 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Wed, 8 Nov 2023 10:12:51 -0500 Subject: [PATCH 08/21] ignore not found for drop --- src/operation/search_index.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/operation/search_index.rs b/src/operation/search_index.rs index 2289d3708..ea4ca724f 100644 --- a/src/operation/search_index.rs +++ b/src/operation/search_index.rs @@ -137,4 +137,12 @@ impl OperationWithDefaults for DropSearchIndex { ) -> Result { response.body() } + + fn handle_error(&self, error: crate::error::Error) -> Result { + if error.is_ns_not_found() { + Ok(()) + } else { + Err(error) + } + } } \ No newline at end of file From 75e132534301b620728e87e82869d0c9f46d611f Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Wed, 8 Nov 2023 10:39:09 -0500 Subject: [PATCH 09/21] createSearchIndex test impl --- src/search_index.rs | 26 ++++++++-------- src/test/spec/unified_runner/operation.rs | 3 ++ .../unified_runner/operation/search_index.rs | 30 +++++++++++++++++++ 3 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 src/test/spec/unified_runner/operation/search_index.rs diff --git a/src/search_index.rs b/src/search_index.rs index a6de20899..dceaa9876 100644 --- a/src/search_index.rs +++ b/src/search_index.rs @@ -2,12 +2,12 @@ use crate::{bson::Document, Collection, error::{Error, Result}, coll::options::A use self::options::*; use bson::doc; -use serde::Serialize; +use serde::{Serialize, Deserialize}; use typed_builder::TypedBuilder; impl Collection { /// Convenience method for creating a single search index. - pub async fn create_search_index(&self, model: &SearchIndexModel, options: impl Into>) -> Result { + pub async fn create_search_index(&self, model: SearchIndexModel, options: impl Into>) -> Result { let mut names = self.create_search_indexes(Some(model), options).await?; match names.len() { 1 => Ok(names.pop().unwrap()), @@ -16,30 +16,31 @@ impl Collection { } /// Creates multiple search indexes on the collection. - pub async fn create_search_indexes(&self, models: impl IntoIterator, _options: impl Into>) -> Result> { - let op = CreateSearchIndexes::new(self.namespace(), models.into_iter().cloned().collect()); + pub async fn create_search_indexes(&self, models: impl IntoIterator, _options: impl Into>) -> Result> { + let op = CreateSearchIndexes::new(self.namespace(), models.into_iter().collect()); self.client().execute_operation(op, None).await } /// Updates the search index with the given name to use the provided definition. - pub async fn update_search_index(&self, name: &str, definition: &Document, _options: impl Into>) -> Result<()> { - let op = UpdateSearchIndex::new(self.namespace(), name.to_string(), definition.clone()); + pub async fn update_search_index(&self, name: impl AsRef, definition: Document, _options: impl Into>) -> Result<()> { + let op = UpdateSearchIndex::new(self.namespace(), name.as_ref().to_string(), definition.clone()); self.client().execute_operation(op, None).await } /// Drops the search index with the given name. - pub async fn drop_search_index(&self, name: &str, _options: impl Into>) -> Result<()> { - let op = DropSearchIndex::new(self.namespace(), name.to_string()); + pub async fn drop_search_index(&self, name: impl AsRef, _options: impl Into>) -> Result<()> { + let op = DropSearchIndex::new(self.namespace(), name.as_ref().to_string()); self.client().execute_operation(op, None).await } /// Gets index information for one or more search indexes in the collection. /// /// If name is not specified, information for all indexes on the specified collection will be returned. - pub async fn list_search_indexes(&self, name: impl Into>, aggregation_options: impl Into>, _list_index_options: impl Into>) -> Result> { + pub async fn list_search_indexes(&self, name: impl Into>, aggregation_options: impl Into>, _list_index_options: impl Into>) -> Result> + { let mut inner = doc! { }; if let Some(name) = name.into() { - inner.insert("name", name); + inner.insert("name", name.to_string()); } self.aggregate( vec![ @@ -53,7 +54,7 @@ impl Collection { } /// Specifies the options for a search index. -#[derive(Debug, Clone, Default, TypedBuilder, Serialize)] +#[derive(Debug, Clone, Default, TypedBuilder, Serialize, Deserialize)] #[non_exhaustive] pub struct SearchIndexModel { /// The definition for this index. @@ -64,10 +65,11 @@ pub struct SearchIndexModel { } pub mod options { + use serde::Deserialize; use typed_builder::TypedBuilder; /// Options for [Collection::create_search_index]. Present to allow additional options to be added in the future as a non-breaking change. - #[derive(Clone, Debug, Default, TypedBuilder)] + #[derive(Clone, Debug, Default, TypedBuilder, Deserialize)] #[non_exhaustive] pub struct CreateSearchIndexOptions { } diff --git a/src/test/spec/unified_runner/operation.rs b/src/test/spec/unified_runner/operation.rs index 7269e1546..81d42d0d5 100644 --- a/src/test/spec/unified_runner/operation.rs +++ b/src/test/spec/unified_runner/operation.rs @@ -3,6 +3,8 @@ mod csfle; #[cfg(feature = "in-use-encryption-unstable")] use self::csfle::*; +mod search_index; + use std::{ collections::HashMap, convert::TryInto, @@ -290,6 +292,7 @@ impl<'de> Deserialize<'de> for Operation { "find" => deserialize_op::(definition.arguments), "createFindCursor" => deserialize_op::(definition.arguments), "createCommandCursor" => deserialize_op::(definition.arguments), + "createSearchIndex" => deserialize_op::(definition.arguments), "aggregate" => deserialize_op::(definition.arguments), "distinct" => deserialize_op::(definition.arguments), "countDocuments" => deserialize_op::(definition.arguments), diff --git a/src/test/spec/unified_runner/operation/search_index.rs b/src/test/spec/unified_runner/operation/search_index.rs new file mode 100644 index 000000000..ef4efb43a --- /dev/null +++ b/src/test/spec/unified_runner/operation/search_index.rs @@ -0,0 +1,30 @@ +use bson::Bson; +use futures_core::future::BoxFuture; +use futures_util::FutureExt; +use serde::Deserialize; + +use crate::{error::Result, search_index::options::CreateSearchIndexOptions, SearchIndexModel, test::spec::unified_runner::{TestRunner, Entity}}; + +use super::TestOperation; + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub(super) struct CreateSearchIndex { + model: SearchIndexModel, + #[serde(flatten)] + options: CreateSearchIndexOptions, +} + +impl TestOperation for CreateSearchIndex { + fn execute_entity_operation<'a>( + &'a self, + id: &'a str, + test_runner: &'a TestRunner, + ) -> BoxFuture<'a, Result>> { + async move { + let collection = test_runner.get_collection(id).await; + let name = collection.create_search_index(self.model.clone(), self.options.clone()).await?; + Ok(Some(Bson::String(name).into())) + }.boxed() + } +} \ No newline at end of file From 1b94f33984ee92f4c6d8600a9ba6f7ff3746ea91 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Wed, 8 Nov 2023 10:42:11 -0500 Subject: [PATCH 10/21] createSearchIndexes test impl --- src/test/spec/unified_runner/operation.rs | 1 + .../unified_runner/operation/search_index.rs | 24 ++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/test/spec/unified_runner/operation.rs b/src/test/spec/unified_runner/operation.rs index 81d42d0d5..cddb6ade6 100644 --- a/src/test/spec/unified_runner/operation.rs +++ b/src/test/spec/unified_runner/operation.rs @@ -293,6 +293,7 @@ impl<'de> Deserialize<'de> for Operation { "createFindCursor" => deserialize_op::(definition.arguments), "createCommandCursor" => deserialize_op::(definition.arguments), "createSearchIndex" => deserialize_op::(definition.arguments), + "createSearchIndexes" => deserialize_op::(definition.arguments), "aggregate" => deserialize_op::(definition.arguments), "distinct" => deserialize_op::(definition.arguments), "countDocuments" => deserialize_op::(definition.arguments), diff --git a/src/test/spec/unified_runner/operation/search_index.rs b/src/test/spec/unified_runner/operation/search_index.rs index ef4efb43a..cc66f43a3 100644 --- a/src/test/spec/unified_runner/operation/search_index.rs +++ b/src/test/spec/unified_runner/operation/search_index.rs @@ -1,4 +1,4 @@ -use bson::Bson; +use bson::{Bson, to_bson}; use futures_core::future::BoxFuture; use futures_util::FutureExt; use serde::Deserialize; @@ -27,4 +27,26 @@ impl TestOperation for CreateSearchIndex { Ok(Some(Bson::String(name).into())) }.boxed() } +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub(super) struct CreateSearchIndexes { + models: Vec, + #[serde(flatten)] + options: CreateSearchIndexOptions, +} + +impl TestOperation for CreateSearchIndexes { + fn execute_entity_operation<'a>( + &'a self, + id: &'a str, + test_runner: &'a TestRunner, + ) -> BoxFuture<'a, Result>> { + async move { + let collection = test_runner.get_collection(id).await; + let names = collection.create_search_indexes(self.models.clone(), self.options.clone()).await?; + Ok(Some(to_bson(&names)?.into())) + }.boxed() + } } \ No newline at end of file From 6daaffce86c78a223b291942d9f7132fab576a72 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Wed, 8 Nov 2023 10:45:01 -0500 Subject: [PATCH 11/21] dropSearchIndex test impl --- src/search_index.rs | 6 ++--- .../unified_runner/operation/search_index.rs | 24 ++++++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/search_index.rs b/src/search_index.rs index dceaa9876..6ac32269e 100644 --- a/src/search_index.rs +++ b/src/search_index.rs @@ -74,17 +74,17 @@ pub mod options { pub struct CreateSearchIndexOptions { } /// Options for [Collection::update_search_index]. Present to allow additional options to be added in the future as a non-breaking change. - #[derive(Clone, Debug, Default, TypedBuilder)] + #[derive(Clone, Debug, Default, TypedBuilder, Deserialize)] #[non_exhaustive] pub struct UpdateSearchIndexOptions { } /// Options for [Collection::list_search_indexes]. Present to allow additional options to be added in the future as a non-breaking change. - #[derive(Clone, Debug, Default, TypedBuilder)] + #[derive(Clone, Debug, Default, TypedBuilder, Deserialize)] #[non_exhaustive] pub struct ListSearchIndexOptions { } /// Options for [Collection::drop_search_index]. Present to allow additional options to be added in the future as a non-breaking change. - #[derive(Clone, Debug, Default, TypedBuilder)] + #[derive(Clone, Debug, Default, TypedBuilder, Deserialize)] #[non_exhaustive] pub struct DropSearchIndexOptions { } } \ No newline at end of file diff --git a/src/test/spec/unified_runner/operation/search_index.rs b/src/test/spec/unified_runner/operation/search_index.rs index cc66f43a3..d86453f70 100644 --- a/src/test/spec/unified_runner/operation/search_index.rs +++ b/src/test/spec/unified_runner/operation/search_index.rs @@ -3,7 +3,7 @@ use futures_core::future::BoxFuture; use futures_util::FutureExt; use serde::Deserialize; -use crate::{error::Result, search_index::options::CreateSearchIndexOptions, SearchIndexModel, test::spec::unified_runner::{TestRunner, Entity}}; +use crate::{error::Result, search_index::options::{CreateSearchIndexOptions, DropSearchIndexOptions}, SearchIndexModel, test::spec::unified_runner::{TestRunner, Entity}}; use super::TestOperation; @@ -49,4 +49,26 @@ impl TestOperation for CreateSearchIndexes { Ok(Some(to_bson(&names)?.into())) }.boxed() } +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub(super) struct DropSearchIndex { + name: String, + #[serde(flatten)] + options: DropSearchIndexOptions, +} + +impl TestOperation for DropSearchIndex { + fn execute_entity_operation<'a>( + &'a self, + id: &'a str, + test_runner: &'a TestRunner, + ) -> BoxFuture<'a, Result>> { + async move { + let collection = test_runner.get_collection(id).await; + collection.drop_search_index(&self.name, self.options.clone()).await?; + Ok(None) + }.boxed() + } } \ No newline at end of file From e35036e9fd009061d546878e4b0d7568088ffc9c Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Wed, 8 Nov 2023 10:46:01 -0500 Subject: [PATCH 12/21] follow-up --- src/test/spec/unified_runner/operation.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/spec/unified_runner/operation.rs b/src/test/spec/unified_runner/operation.rs index cddb6ade6..a199a0c25 100644 --- a/src/test/spec/unified_runner/operation.rs +++ b/src/test/spec/unified_runner/operation.rs @@ -292,8 +292,6 @@ impl<'de> Deserialize<'de> for Operation { "find" => deserialize_op::(definition.arguments), "createFindCursor" => deserialize_op::(definition.arguments), "createCommandCursor" => deserialize_op::(definition.arguments), - "createSearchIndex" => deserialize_op::(definition.arguments), - "createSearchIndexes" => deserialize_op::(definition.arguments), "aggregate" => deserialize_op::(definition.arguments), "distinct" => deserialize_op::(definition.arguments), "countDocuments" => deserialize_op::(definition.arguments), @@ -387,6 +385,9 @@ impl<'de> Deserialize<'de> for Operation { #[cfg(feature = "in-use-encryption-unstable")] "removeKeyAltName" => deserialize_op::(definition.arguments), "iterateOnce" => deserialize_op::(definition.arguments), + "createSearchIndex" => deserialize_op::(definition.arguments), + "createSearchIndexes" => deserialize_op::(definition.arguments), + "dropSearchIndex" => deserialize_op::(definition.arguments), s => Ok(Box::new(UnimplementedOperation { _name: s.to_string(), }) as Box), From c2766084fec668903bc8960f62facb90bca9ead1 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Wed, 8 Nov 2023 10:58:41 -0500 Subject: [PATCH 13/21] listSearchIndexes test impl --- src/test/spec/unified_runner/operation.rs | 1 + .../unified_runner/operation/search_index.rs | 29 +++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/test/spec/unified_runner/operation.rs b/src/test/spec/unified_runner/operation.rs index a199a0c25..b16bc7c06 100644 --- a/src/test/spec/unified_runner/operation.rs +++ b/src/test/spec/unified_runner/operation.rs @@ -388,6 +388,7 @@ impl<'de> Deserialize<'de> for Operation { "createSearchIndex" => deserialize_op::(definition.arguments), "createSearchIndexes" => deserialize_op::(definition.arguments), "dropSearchIndex" => deserialize_op::(definition.arguments), + "listSearchIndexes" => deserialize_op::(definition.arguments), s => Ok(Box::new(UnimplementedOperation { _name: s.to_string(), }) as Box), diff --git a/src/test/spec/unified_runner/operation/search_index.rs b/src/test/spec/unified_runner/operation/search_index.rs index d86453f70..2062b4601 100644 --- a/src/test/spec/unified_runner/operation/search_index.rs +++ b/src/test/spec/unified_runner/operation/search_index.rs @@ -1,9 +1,9 @@ use bson::{Bson, to_bson}; use futures_core::future::BoxFuture; -use futures_util::FutureExt; +use futures_util::{FutureExt, TryStreamExt}; use serde::Deserialize; -use crate::{error::Result, search_index::options::{CreateSearchIndexOptions, DropSearchIndexOptions}, SearchIndexModel, test::spec::unified_runner::{TestRunner, Entity}}; +use crate::{error::Result, search_index::options::{CreateSearchIndexOptions, DropSearchIndexOptions, ListSearchIndexOptions}, SearchIndexModel, test::spec::unified_runner::{TestRunner, Entity}, coll::options::AggregateOptions}; use super::TestOperation; @@ -71,4 +71,29 @@ impl TestOperation for DropSearchIndex { Ok(None) }.boxed() } +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub(super) struct ListSearchIndexes { + name: Option, + #[serde(flatten)] + aggregate_options: AggregateOptions, + #[serde(flatten)] + options: ListSearchIndexOptions, +} + +impl TestOperation for ListSearchIndexes { + fn execute_entity_operation<'a>( + &'a self, + id: &'a str, + test_runner: &'a TestRunner, + ) -> BoxFuture<'a, Result>> { + async move { + let collection = test_runner.get_collection(id).await; + let cursor = collection.list_search_indexes(self.name.as_ref().map(|s| s.as_str()), self.aggregate_options.clone(), self.options.clone()).await?; + let values: Vec<_> = cursor.try_collect().await?; + Ok(Some(to_bson(&values)?.into())) + }.boxed() + } } \ No newline at end of file From 3d6ee3e774e5d5d06cc5996b76a0597d625c660f Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Wed, 8 Nov 2023 11:01:52 -0500 Subject: [PATCH 14/21] updateSearchIndex test impl --- src/test/spec/unified_runner/operation.rs | 1 + .../unified_runner/operation/search_index.rs | 27 +++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/test/spec/unified_runner/operation.rs b/src/test/spec/unified_runner/operation.rs index b16bc7c06..d93b4a680 100644 --- a/src/test/spec/unified_runner/operation.rs +++ b/src/test/spec/unified_runner/operation.rs @@ -389,6 +389,7 @@ impl<'de> Deserialize<'de> for Operation { "createSearchIndexes" => deserialize_op::(definition.arguments), "dropSearchIndex" => deserialize_op::(definition.arguments), "listSearchIndexes" => deserialize_op::(definition.arguments), + "updateSearchIndex" => deserialize_op::(definition.arguments), s => Ok(Box::new(UnimplementedOperation { _name: s.to_string(), }) as Box), diff --git a/src/test/spec/unified_runner/operation/search_index.rs b/src/test/spec/unified_runner/operation/search_index.rs index 2062b4601..46463c210 100644 --- a/src/test/spec/unified_runner/operation/search_index.rs +++ b/src/test/spec/unified_runner/operation/search_index.rs @@ -1,9 +1,9 @@ -use bson::{Bson, to_bson}; +use bson::{Bson, to_bson, Document}; use futures_core::future::BoxFuture; use futures_util::{FutureExt, TryStreamExt}; use serde::Deserialize; -use crate::{error::Result, search_index::options::{CreateSearchIndexOptions, DropSearchIndexOptions, ListSearchIndexOptions}, SearchIndexModel, test::spec::unified_runner::{TestRunner, Entity}, coll::options::AggregateOptions}; +use crate::{error::Result, search_index::options::{CreateSearchIndexOptions, DropSearchIndexOptions, ListSearchIndexOptions, UpdateSearchIndexOptions}, SearchIndexModel, test::spec::unified_runner::{TestRunner, Entity}, coll::options::AggregateOptions}; use super::TestOperation; @@ -96,4 +96,27 @@ impl TestOperation for ListSearchIndexes { Ok(Some(to_bson(&values)?.into())) }.boxed() } +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub(super) struct UpdateSearchIndex { + name: String, + definition: Document, + #[serde(flatten)] + options: UpdateSearchIndexOptions, +} + +impl TestOperation for UpdateSearchIndex { + fn execute_entity_operation<'a>( + &'a self, + id: &'a str, + test_runner: &'a TestRunner, + ) -> BoxFuture<'a, Result>> { + async move { + let collection = test_runner.get_collection(id).await; + collection.update_search_index(&self.name, self.definition.clone(), self.options.clone()).await?; + Ok(None) + }.boxed() + } } \ No newline at end of file From 161ceae1d335bda382495c0b21c8585a9106d0b3 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Wed, 8 Nov 2023 13:42:49 -0500 Subject: [PATCH 15/21] unified tests --- src/operation/search_index.rs | 4 +- src/test/spec.rs | 1 + src/test/spec/index_management.rs | 7 + .../spec/json/index-management/README.rst | 200 ++++++++++++++++++ .../index-management/createSearchIndex.json | 136 ++++++++++++ .../index-management/createSearchIndex.yml | 62 ++++++ .../index-management/createSearchIndexes.json | 172 +++++++++++++++ .../index-management/createSearchIndexes.yml | 83 ++++++++ .../index-management/dropSearchIndex.json | 74 +++++++ .../json/index-management/dropSearchIndex.yml | 42 ++++ .../index-management/listSearchIndexes.json | 156 ++++++++++++++ .../index-management/listSearchIndexes.yml | 85 ++++++++ .../index-management/updateSearchIndex.json | 76 +++++++ .../index-management/updateSearchIndex.yml | 45 ++++ .../unified_runner/operation/search_index.rs | 5 +- 15 files changed, 1143 insertions(+), 5 deletions(-) create mode 100644 src/test/spec/index_management.rs create mode 100644 src/test/spec/json/index-management/README.rst create mode 100644 src/test/spec/json/index-management/createSearchIndex.json create mode 100644 src/test/spec/json/index-management/createSearchIndex.yml create mode 100644 src/test/spec/json/index-management/createSearchIndexes.json create mode 100644 src/test/spec/json/index-management/createSearchIndexes.yml create mode 100644 src/test/spec/json/index-management/dropSearchIndex.json create mode 100644 src/test/spec/json/index-management/dropSearchIndex.yml create mode 100644 src/test/spec/json/index-management/listSearchIndexes.json create mode 100644 src/test/spec/json/index-management/listSearchIndexes.yml create mode 100644 src/test/spec/json/index-management/updateSearchIndex.json create mode 100644 src/test/spec/json/index-management/updateSearchIndex.yml diff --git a/src/operation/search_index.rs b/src/operation/search_index.rs index ea4ca724f..c322acdcb 100644 --- a/src/operation/search_index.rs +++ b/src/operation/search_index.rs @@ -26,10 +26,10 @@ impl OperationWithDefaults for CreateSearchIndexes { Ok(Command::new( Self::NAME.to_string(), self.ns.db.clone(), - doc! { + dbg!(doc! { Self::NAME: self.ns.coll.clone(), "indexes": bson::to_bson(&self.indexes)?, - } + }) )) } diff --git a/src/test/spec.rs b/src/test/spec.rs index 4084aaabe..48515f47e 100644 --- a/src/test/spec.rs +++ b/src/test/spec.rs @@ -10,6 +10,7 @@ mod crud; mod crud_v1; mod faas; mod gridfs; +mod index_management; #[cfg(all(not(feature = "sync"), not(feature = "tokio-sync")))] mod initial_dns_seedlist_discovery; mod load_balancers; diff --git a/src/test/spec/index_management.rs b/src/test/spec/index_management.rs new file mode 100644 index 000000000..6113af4a0 --- /dev/null +++ b/src/test/spec/index_management.rs @@ -0,0 +1,7 @@ +use crate::test::spec::unified_runner::run_unified_tests; + +#[cfg_attr(feature = "tokio-runtime", tokio::test)] +#[cfg_attr(feature = "async-std-runtime", async_std::test)] +async fn run() { + run_unified_tests(&["index-management"]).await; +} diff --git a/src/test/spec/json/index-management/README.rst b/src/test/spec/json/index-management/README.rst new file mode 100644 index 000000000..090cda4be --- /dev/null +++ b/src/test/spec/json/index-management/README.rst @@ -0,0 +1,200 @@ +====================== +Index Management Tests +====================== + +.. contents:: + +---- + +Test Plan +========= + +These prose tests are ported from the legacy enumerate-indexes spec. + +Configurations +-------------- + +- standalone node +- replica set primary node +- replica set secondary node +- mongos node + +Preparation +----------- + +For each of the configurations: + +- Create a (new) database +- Create a collection +- Create a single column index, a compound index, and a unique index +- Insert at least one document containing all the fields that the above + indicated indexes act on + +Tests + +- Run the driver's method that returns a list of index names, and: + + - verify that *all* index names are represented in the result + - verify that there are no duplicate index names + - verify there are no returned indexes that do not exist + +- Run the driver's method that returns a list of index information records, and: + + - verify all the indexes are represented in the result + - verify the "unique" flags show up for the unique index + - verify there are no duplicates in the returned list + - if the result consists of statically defined index models that include an ``ns`` field, verify + that its value is accurate + +Search Index Management Helpers +------------------------------- + +These tests are intended to smoke test the search management helpers end-to-end against a live Atlas cluster. + +The search index management commands are asynchronous and mongod/mongos returns before the changes to a clusters' search indexes have completed. When +these prose tests specify "waiting for the changes", drivers should repeatedly poll the cluster with ``listSearchIndexes`` +until the changes are visible. Each test specifies the condition that is considered "ready". For example, when creating a +new search index, waiting until the inserted index has a status ``queryable: true`` indicates that the index was successfully +created. + +The commands tested in these prose tests take a while to successfully complete. Drivers should raise the timeout for each test to avoid timeout errors if +the test timeout is too low. 5 minutes is a sufficiently large timeout that any timeout that occurs indicates a real failure, but this value is not required and can be tweaked per-driver. + +There is a server-side limitation that prevents multiple search indexes from being created with the same name, definition and +collection name. This limitation does not take into account collection uuid. Because these commands are asynchronous, any cleanup +code that may run after a test (cleaning a database or dropping search indexes) may not have completed by the next iteration of the +test (or the next test run, if running locally). To address this issue, each test uses a randomly generated collection name. Drivers +may generate this collection name however they like, but a suggested implementation is a hex representation of an +ObjectId (``new ObjectId().toHexString()`` in Node). + +Setup +~~~~~ + +These tests must run against an Atlas cluster with a 7.0+ server. `Scripts are available `_ in drivers-evergreen-tools which can setup and teardown +Atlas clusters. To ensure that the Atlas cluster is cleaned up after each CI run, drivers should configure evergreen to run these tests +as a part of a task group. Be sure that the cluster gets torn down! + +When working locally on these tests, the same Atlas setup and teardown scripts can be used locally to provision a cluster for development. + +Case 1: Driver can successfully create and list search indexes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#. Create a collection with the "create" command using a randomly generated name (referred to as ``coll0``). +#. Create a new search index on ``coll0`` with the ``createSearchIndex`` helper. Use the following definition: + + .. code:: typescript + + { + name: 'test-search-index', + definition: { + mappings: { dynamic: false } + } + } + +#. Assert that the command returns the name of the index: ``"test-search-index"``. +#. Run ``coll0.listSearchIndexes()`` repeatedly every 5 seconds until the following condition is satisfied and store the value in a variable ``index``: + + - An index with the ``name`` of ``test-search-index`` is present and the index has a field ``queryable`` with a value of ``true``. + +#. Assert that ``index`` has a property ``latestDefinition`` whose value is ``{ 'mappings': { 'dynamic': false } }`` + +Case 2: Driver can successfully create multiple indexes in batch +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#. Create a collection with the "create" command using a randomly generated name (referred to as ``coll0``). +#. Create two new search indexes on ``coll0`` with the ``createSearchIndexes`` helper. Use the following + definitions when creating the indexes. These definitions are referred to as ``indexDefinitions``. + + .. code:: typescript + + { + name: 'test-search-index-1', + definition: { + mappings: { dynamic: false } + } + } + + { + name: 'test-search-index-2', + definition: { + mappings: { dynamic: false } + } + } + +#. Assert that the command returns an array containing the new indexes' names: ``["test-search-index-1", "test-search-index-2"]``. +#. Run ``coll0.listSearchIndexes()`` repeatedly every 5 seconds until the following conditions are satisfied. + + - An index with the ``name`` of ``test-search-index-1`` is present and index has a field ``queryable`` with the value of ``true``. Store result in ``index1``. + - An index with the ``name`` of ``test-search-index-2`` is present and index has a field ``queryable`` with the value of ``true``. Store result in ``index2``. + +#. Assert that ``index1`` and ``index2`` have the property ``latestDefinition`` whose value is ``{ "mappings" : { "dynamic" : false } }`` + +Case 3: Driver can successfully drop search indexes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#. Create a collection with the "create" command using a randomly generated name (referred to as ``coll0``). +#. Create a new search index on ``coll0`` with the following definition: + + .. code:: typescript + + { + name: 'test-search-index', + definition: { + mappings: { dynamic: false } + } + } + +#. Assert that the command returns the name of the index: ``"test-search-index"``. +#. Run ``coll0.listSearchIndexes()`` repeatedly every 5 seconds until the following condition is satisfied: + + - An index with the ``name`` of ``test-search-index`` is present and index has a field ``queryable`` with the value of ``true``. + +#. Run a ``dropSearchIndex`` on ``coll0``, using ``test-search-index`` for the name. +#. Run ``coll0.listSearchIndexes()`` repeatedly every 5 seconds until ``listSearchIndexes`` returns an empty array. + +This test fails if it times out waiting for the deletion to succeed. + +Case 4: Driver can update a search index +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#. Create a collection with the "create" command using a randomly generated name (referred to as ``coll0``). +#. Create a new search index on ``coll0`` with the following definition: + + .. code:: typescript + + { + name: 'test-search-index', + definition: { + mappings: { dynamic: false } + } + } + +#. Assert that the command returns the name of the index: ``"test-search-index"``. +#. Run ``coll0.listSearchIndexes()`` repeatedly every 5 seconds until the following condition is satisfied: + + - An index with the ``name`` of ``test-search-index`` is present and index has a field ``queryable`` with the value of ``true``. + +#. Run a ``updateSearchIndex`` on ``coll0``, using the following definition. + + .. code:: typescript + + { + name: 'test-search-index', + definition: { + mappings: { dynamic: true } + } + } + +#. Assert that the command does not error and the server responds with a success. +#. Run ``coll0.listSearchIndexes()`` repeatedly every 5 seconds until the following conditions are satisfied: + + - An index with the ``name`` of ``test-search-index`` is present. This index is referred to as ``index``. + - The index has a field ``queryable`` with a value of ``true`` and has a field ``status`` with the value of ``READY``. + +#. Assert that an index is present with the name ``test-search-index`` and the definition has a property ``latestDefinition`` whose value is ``{ 'mappings': { 'dynamic': true } }``. + +Case 5: ``dropSearchIndex`` suppresses namespace not found errors +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#. Create a driver-side collection object for a randomly generated collection name. Do not create this collection on the server. +#. Run a ``dropSearchIndex`` command and assert that no error is thrown. diff --git a/src/test/spec/json/index-management/createSearchIndex.json b/src/test/spec/json/index-management/createSearchIndex.json new file mode 100644 index 000000000..04cffbe9c --- /dev/null +++ b/src/test/spec/json/index-management/createSearchIndex.json @@ -0,0 +1,136 @@ +{ + "description": "createSearchIndex", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "tests": [ + { + "description": "no name provided for an index definition", + "operations": [ + { + "name": "createSearchIndex", + "object": "collection0", + "arguments": { + "model": { + "definition": { + "mappings": { + "dynamic": true + } + } + } + }, + "expectError": { + "isError": true, + "errorContains": "Search index commands are only supported with Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createSearchIndexes": "collection0", + "indexes": [ + { + "definition": { + "mappings": { + "dynamic": true + } + } + } + ], + "$db": "database0" + } + } + } + ] + } + ] + }, + { + "description": "name provided for an index definition", + "operations": [ + { + "name": "createSearchIndex", + "object": "collection0", + "arguments": { + "model": { + "definition": { + "mappings": { + "dynamic": true + } + }, + "name": "test index" + } + }, + "expectError": { + "isError": true, + "errorContains": "Search index commands are only supported with Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createSearchIndexes": "collection0", + "indexes": [ + { + "definition": { + "mappings": { + "dynamic": true + } + }, + "name": "test index" + } + ], + "$db": "database0" + } + } + } + ] + } + ] + } + ] +} diff --git a/src/test/spec/json/index-management/createSearchIndex.yml b/src/test/spec/json/index-management/createSearchIndex.yml new file mode 100644 index 000000000..6aa56f3bc --- /dev/null +++ b/src/test/spec/json/index-management/createSearchIndex.yml @@ -0,0 +1,62 @@ +description: "createSearchIndex" +schemaVersion: "1.4" +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + +runOnRequirements: + - minServerVersion: "7.0.0" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + +tests: + - description: "no name provided for an index definition" + operations: + - name: createSearchIndex + object: *collection0 + arguments: + model: { definition: &definition { mappings: { dynamic: true } } } + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + isError: true + errorContains: Search index commands are only supported with Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + createSearchIndexes: *collection0 + indexes: [ { definition: *definition } ] + $db: *database0 + + - description: "name provided for an index definition" + operations: + - name: createSearchIndex + object: *collection0 + arguments: + model: { definition: &definition { mappings: { dynamic: true } } , name: 'test index' } + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + isError: true + errorContains: Search index commands are only supported with Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + createSearchIndexes: *collection0 + indexes: [ { definition: *definition, name: 'test index' } ] + $db: *database0 \ No newline at end of file diff --git a/src/test/spec/json/index-management/createSearchIndexes.json b/src/test/spec/json/index-management/createSearchIndexes.json new file mode 100644 index 000000000..95dbedde7 --- /dev/null +++ b/src/test/spec/json/index-management/createSearchIndexes.json @@ -0,0 +1,172 @@ +{ + "description": "createSearchIndexes", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "tests": [ + { + "description": "empty index definition array", + "operations": [ + { + "name": "createSearchIndexes", + "object": "collection0", + "arguments": { + "models": [] + }, + "expectError": { + "isError": true, + "errorContains": "Search index commands are only supported with Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createSearchIndexes": "collection0", + "indexes": [], + "$db": "database0" + } + } + } + ] + } + ] + }, + { + "description": "no name provided for an index definition", + "operations": [ + { + "name": "createSearchIndexes", + "object": "collection0", + "arguments": { + "models": [ + { + "definition": { + "mappings": { + "dynamic": true + } + } + } + ] + }, + "expectError": { + "isError": true, + "errorContains": "Search index commands are only supported with Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createSearchIndexes": "collection0", + "indexes": [ + { + "definition": { + "mappings": { + "dynamic": true + } + } + } + ], + "$db": "database0" + } + } + } + ] + } + ] + }, + { + "description": "name provided for an index definition", + "operations": [ + { + "name": "createSearchIndexes", + "object": "collection0", + "arguments": { + "models": [ + { + "definition": { + "mappings": { + "dynamic": true + } + }, + "name": "test index" + } + ] + }, + "expectError": { + "isError": true, + "errorContains": "Search index commands are only supported with Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createSearchIndexes": "collection0", + "indexes": [ + { + "definition": { + "mappings": { + "dynamic": true + } + }, + "name": "test index" + } + ], + "$db": "database0" + } + } + } + ] + } + ] + } + ] +} diff --git a/src/test/spec/json/index-management/createSearchIndexes.yml b/src/test/spec/json/index-management/createSearchIndexes.yml new file mode 100644 index 000000000..54a6e84cc --- /dev/null +++ b/src/test/spec/json/index-management/createSearchIndexes.yml @@ -0,0 +1,83 @@ +description: "createSearchIndexes" +schemaVersion: "1.4" +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + +runOnRequirements: + - minServerVersion: "7.0.0" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + +tests: + - description: "empty index definition array" + operations: + - name: createSearchIndexes + object: *collection0 + arguments: + models: [] + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + isError: true + errorContains: Search index commands are only supported with Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + createSearchIndexes: *collection0 + indexes: [] + $db: *database0 + + + - description: "no name provided for an index definition" + operations: + - name: createSearchIndexes + object: *collection0 + arguments: + models: [ { definition: &definition { mappings: { dynamic: true } } } ] + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + isError: true + errorContains: Search index commands are only supported with Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + createSearchIndexes: *collection0 + indexes: [ { definition: *definition } ] + $db: *database0 + + - description: "name provided for an index definition" + operations: + - name: createSearchIndexes + object: *collection0 + arguments: + models: [ { definition: &definition { mappings: { dynamic: true } } , name: 'test index' } ] + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + isError: true + errorContains: Search index commands are only supported with Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + createSearchIndexes: *collection0 + indexes: [ { definition: *definition, name: 'test index' } ] + $db: *database0 \ No newline at end of file diff --git a/src/test/spec/json/index-management/dropSearchIndex.json b/src/test/spec/json/index-management/dropSearchIndex.json new file mode 100644 index 000000000..0f21a5b68 --- /dev/null +++ b/src/test/spec/json/index-management/dropSearchIndex.json @@ -0,0 +1,74 @@ +{ + "description": "dropSearchIndex", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "tests": [ + { + "description": "sends the correct command", + "operations": [ + { + "name": "dropSearchIndex", + "object": "collection0", + "arguments": { + "name": "test index" + }, + "expectError": { + "isError": true, + "errorContains": "Search index commands are only supported with Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "dropSearchIndex": "collection0", + "name": "test index", + "$db": "database0" + } + } + } + ] + } + ] + } + ] +} diff --git a/src/test/spec/json/index-management/dropSearchIndex.yml b/src/test/spec/json/index-management/dropSearchIndex.yml new file mode 100644 index 000000000..e384cf26c --- /dev/null +++ b/src/test/spec/json/index-management/dropSearchIndex.yml @@ -0,0 +1,42 @@ +description: "dropSearchIndex" +schemaVersion: "1.4" +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + +runOnRequirements: + - minServerVersion: "7.0.0" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + +tests: + - description: "sends the correct command" + operations: + - name: dropSearchIndex + object: *collection0 + arguments: + name: &indexName 'test index' + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + isError: true + errorContains: Search index commands are only supported with Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + dropSearchIndex: *collection0 + name: *indexName + $db: *database0 diff --git a/src/test/spec/json/index-management/listSearchIndexes.json b/src/test/spec/json/index-management/listSearchIndexes.json new file mode 100644 index 000000000..24c51ad88 --- /dev/null +++ b/src/test/spec/json/index-management/listSearchIndexes.json @@ -0,0 +1,156 @@ +{ + "description": "listSearchIndexes", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "tests": [ + { + "description": "when no name is provided, it does not populate the filter", + "operations": [ + { + "name": "listSearchIndexes", + "object": "collection0", + "expectError": { + "isError": true, + "errorContains": "Search index commands are only supported with Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "pipeline": [ + { + "$listSearchIndexes": {} + } + ] + } + } + } + ] + } + ] + }, + { + "description": "when a name is provided, it is present in the filter", + "operations": [ + { + "name": "listSearchIndexes", + "object": "collection0", + "arguments": { + "name": "test index" + }, + "expectError": { + "isError": true, + "errorContains": "Search index commands are only supported with Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "pipeline": [ + { + "$listSearchIndexes": { + "name": "test index" + } + } + ], + "$db": "database0" + } + } + } + ] + } + ] + }, + { + "description": "aggregation cursor options are supported", + "operations": [ + { + "name": "listSearchIndexes", + "object": "collection0", + "arguments": { + "name": "test index", + "aggregationOptions": { + "batchSize": 10 + } + }, + "expectError": { + "isError": true, + "errorContains": "Search index commands are only supported with Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": { + "batchSize": 10 + }, + "pipeline": [ + { + "$listSearchIndexes": { + "name": "test index" + } + } + ], + "$db": "database0" + } + } + } + ] + } + ] + } + ] +} diff --git a/src/test/spec/json/index-management/listSearchIndexes.yml b/src/test/spec/json/index-management/listSearchIndexes.yml new file mode 100644 index 000000000..a50becdf1 --- /dev/null +++ b/src/test/spec/json/index-management/listSearchIndexes.yml @@ -0,0 +1,85 @@ +description: "listSearchIndexes" +schemaVersion: "1.4" +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + +runOnRequirements: + - minServerVersion: "7.0.0" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + +tests: + - description: "when no name is provided, it does not populate the filter" + operations: + - name: listSearchIndexes + object: *collection0 + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + isError: true + errorContains: Search index commands are only supported with Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0 + pipeline: + - $listSearchIndexes: {} + + - description: "when a name is provided, it is present in the filter" + operations: + - name: listSearchIndexes + object: *collection0 + arguments: + name: &indexName "test index" + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + isError: true + errorContains: Search index commands are only supported with Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0 + pipeline: + - $listSearchIndexes: { name: *indexName } + $db: *database0 + + - description: aggregation cursor options are supported + operations: + - name: listSearchIndexes + object: *collection0 + arguments: + name: &indexName "test index" + aggregationOptions: + batchSize: 10 + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + isError: true + errorContains: Search index commands are only supported with Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0 + cursor: { batchSize: 10 } + pipeline: + - $listSearchIndexes: { name: *indexName } + $db: *database0 \ No newline at end of file diff --git a/src/test/spec/json/index-management/updateSearchIndex.json b/src/test/spec/json/index-management/updateSearchIndex.json new file mode 100644 index 000000000..88a46a306 --- /dev/null +++ b/src/test/spec/json/index-management/updateSearchIndex.json @@ -0,0 +1,76 @@ +{ + "description": "updateSearchIndex", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "tests": [ + { + "description": "sends the correct command", + "operations": [ + { + "name": "updateSearchIndex", + "object": "collection0", + "arguments": { + "name": "test index", + "definition": {} + }, + "expectError": { + "isError": true, + "errorContains": "Search index commands are only supported with Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "updateSearchIndex": "collection0", + "name": "test index", + "definition": {}, + "$db": "database0" + } + } + } + ] + } + ] + } + ] +} diff --git a/src/test/spec/json/index-management/updateSearchIndex.yml b/src/test/spec/json/index-management/updateSearchIndex.yml new file mode 100644 index 000000000..bb18ab512 --- /dev/null +++ b/src/test/spec/json/index-management/updateSearchIndex.yml @@ -0,0 +1,45 @@ +description: "updateSearchIndex" +schemaVersion: "1.4" +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + +runOnRequirements: + - minServerVersion: "7.0.0" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + +tests: + - description: "sends the correct command" + operations: + - name: updateSearchIndex + object: *collection0 + arguments: + name: &indexName 'test index' + definition: &definition {} + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + isError: true + errorContains: Search index commands are only supported with Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + updateSearchIndex: *collection0 + name: *indexName + definition: *definition + $db: *database0 + diff --git a/src/test/spec/unified_runner/operation/search_index.rs b/src/test/spec/unified_runner/operation/search_index.rs index 46463c210..96ebb2017 100644 --- a/src/test/spec/unified_runner/operation/search_index.rs +++ b/src/test/spec/unified_runner/operation/search_index.rs @@ -77,8 +77,7 @@ impl TestOperation for DropSearchIndex { #[serde(rename_all = "camelCase", deny_unknown_fields)] pub(super) struct ListSearchIndexes { name: Option, - #[serde(flatten)] - aggregate_options: AggregateOptions, + aggregation_options: Option, #[serde(flatten)] options: ListSearchIndexOptions, } @@ -91,7 +90,7 @@ impl TestOperation for ListSearchIndexes { ) -> BoxFuture<'a, Result>> { async move { let collection = test_runner.get_collection(id).await; - let cursor = collection.list_search_indexes(self.name.as_ref().map(|s| s.as_str()), self.aggregate_options.clone(), self.options.clone()).await?; + let cursor = collection.list_search_indexes(self.name.as_ref().map(|s| s.as_str()), self.aggregation_options.clone(), self.options.clone()).await?; let values: Vec<_> = cursor.try_collect().await?; Ok(Some(to_bson(&values)?.into())) }.boxed() From 3f9c6cbc2ae9213d8f216bfb8f5296e9e6dcfa5a Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Wed, 8 Nov 2023 15:06:07 -0500 Subject: [PATCH 16/21] skip name --- src/operation/search_index.rs | 4 ++-- src/search_index.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/operation/search_index.rs b/src/operation/search_index.rs index c322acdcb..9e6f27331 100644 --- a/src/operation/search_index.rs +++ b/src/operation/search_index.rs @@ -26,10 +26,10 @@ impl OperationWithDefaults for CreateSearchIndexes { Ok(Command::new( Self::NAME.to_string(), self.ns.db.clone(), - dbg!(doc! { + doc! { Self::NAME: self.ns.coll.clone(), "indexes": bson::to_bson(&self.indexes)?, - }) + }, )) } diff --git a/src/search_index.rs b/src/search_index.rs index 6ac32269e..266fefb04 100644 --- a/src/search_index.rs +++ b/src/search_index.rs @@ -61,6 +61,7 @@ pub struct SearchIndexModel { pub definition: Document, /// The name for this index, if present. + #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, } From fd6c080270d5fa0f6098c03c1e6e324cfff0533f Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Wed, 8 Nov 2023 15:07:45 -0500 Subject: [PATCH 17/21] format --- src/operation.rs | 4 +- src/operation/search_index.rs | 39 ++++---- src/search_index.rs | 88 +++++++++++++------ src/test/spec/unified_runner/operation.rs | 20 +++-- .../unified_runner/operation/search_index.rs | 56 +++++++++--- 5 files changed, 145 insertions(+), 62 deletions(-) diff --git a/src/operation.rs b/src/operation.rs index c9e6348d3..52f2e8392 100644 --- a/src/operation.rs +++ b/src/operation.rs @@ -5,7 +5,6 @@ mod count; mod count_documents; mod create; mod create_indexes; -mod search_index; mod delete; mod distinct; mod drop_collection; @@ -21,6 +20,7 @@ mod list_indexes; mod raw_output; mod run_command; mod run_cursor_command; +mod search_index; mod update; #[cfg(test)] @@ -58,7 +58,6 @@ pub(crate) use count::Count; pub(crate) use count_documents::CountDocuments; pub(crate) use create::Create; pub(crate) use create_indexes::CreateIndexes; -pub(crate) use search_index::{CreateSearchIndexes, UpdateSearchIndex, DropSearchIndex}; pub(crate) use delete::Delete; pub(crate) use distinct::Distinct; pub(crate) use drop_collection::DropCollection; @@ -75,6 +74,7 @@ pub(crate) use list_indexes::ListIndexes; pub(crate) use raw_output::RawOutput; pub(crate) use run_command::RunCommand; pub(crate) use run_cursor_command::RunCursorCommand; +pub(crate) use search_index::{CreateSearchIndexes, DropSearchIndex, UpdateSearchIndex}; pub(crate) use update::{Update, UpdateOrReplace}; const SERVER_4_2_0_WIRE_VERSION: i32 = 8; diff --git a/src/operation/search_index.rs b/src/operation/search_index.rs index 9e6f27331..9da08e1aa 100644 --- a/src/operation/search_index.rs +++ b/src/operation/search_index.rs @@ -1,7 +1,7 @@ -use bson::{Document, doc}; +use bson::{doc, Document}; use serde::Deserialize; -use crate::{SearchIndexModel, Namespace, error::Result, cmap::Command}; +use crate::{cmap::Command, error::Result, Namespace, SearchIndexModel}; use super::OperationWithDefaults; @@ -52,7 +52,11 @@ impl OperationWithDefaults for CreateSearchIndexes { } let response: Response = response.body()?; - Ok(response.indexes_created.into_iter().map(|ci| ci.name).collect()) + Ok(response + .indexes_created + .into_iter() + .map(|ci| ci.name) + .collect()) } } @@ -64,12 +68,12 @@ pub(crate) struct UpdateSearchIndex { } impl UpdateSearchIndex { - pub(crate) fn new( - ns: Namespace, - name: String, - definition: Document, - ) -> Self { - Self { ns, name, definition } + pub(crate) fn new(ns: Namespace, name: String, definition: Document) -> Self { + Self { + ns, + name, + definition, + } } } @@ -78,7 +82,10 @@ impl OperationWithDefaults for UpdateSearchIndex { type Command = Document; const NAME: &'static str = "updateSearchIndex"; - fn build(&mut self, _description: &crate::cmap::StreamDescription) -> crate::error::Result> { + fn build( + &mut self, + _description: &crate::cmap::StreamDescription, + ) -> crate::error::Result> { Ok(Command::new( Self::NAME.to_string(), self.ns.db.clone(), @@ -106,10 +113,7 @@ pub(crate) struct DropSearchIndex { } impl DropSearchIndex { - pub(crate) fn new( - ns: Namespace, - name: String, - ) -> Self { + pub(crate) fn new(ns: Namespace, name: String) -> Self { Self { ns, name } } } @@ -119,7 +123,10 @@ impl OperationWithDefaults for DropSearchIndex { type Command = Document; const NAME: &'static str = "dropSearchIndex"; - fn build(&mut self, _description: &crate::cmap::StreamDescription) -> Result> { + fn build( + &mut self, + _description: &crate::cmap::StreamDescription, + ) -> Result> { Ok(Command::new( Self::NAME.to_string(), self.ns.db.clone(), @@ -145,4 +152,4 @@ impl OperationWithDefaults for DropSearchIndex { Err(error) } } -} \ No newline at end of file +} diff --git a/src/search_index.rs b/src/search_index.rs index 266fefb04..05f696074 100644 --- a/src/search_index.rs +++ b/src/search_index.rs @@ -1,13 +1,24 @@ -use crate::{bson::Document, Collection, error::{Error, Result}, coll::options::AggregateOptions, Cursor, operation::{CreateSearchIndexes, UpdateSearchIndex, DropSearchIndex}}; use self::options::*; +use crate::{ + bson::Document, + coll::options::AggregateOptions, + error::{Error, Result}, + operation::{CreateSearchIndexes, DropSearchIndex, UpdateSearchIndex}, + Collection, + Cursor, +}; use bson::doc; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; use typed_builder::TypedBuilder; impl Collection { /// Convenience method for creating a single search index. - pub async fn create_search_index(&self, model: SearchIndexModel, options: impl Into>) -> Result { + pub async fn create_search_index( + &self, + model: SearchIndexModel, + options: impl Into>, + ) -> Result { let mut names = self.create_search_indexes(Some(model), options).await?; match names.len() { 1 => Ok(names.pop().unwrap()), @@ -16,40 +27,61 @@ impl Collection { } /// Creates multiple search indexes on the collection. - pub async fn create_search_indexes(&self, models: impl IntoIterator, _options: impl Into>) -> Result> { + pub async fn create_search_indexes( + &self, + models: impl IntoIterator, + _options: impl Into>, + ) -> Result> { let op = CreateSearchIndexes::new(self.namespace(), models.into_iter().collect()); self.client().execute_operation(op, None).await } /// Updates the search index with the given name to use the provided definition. - pub async fn update_search_index(&self, name: impl AsRef, definition: Document, _options: impl Into>) -> Result<()> { - let op = UpdateSearchIndex::new(self.namespace(), name.as_ref().to_string(), definition.clone()); + pub async fn update_search_index( + &self, + name: impl AsRef, + definition: Document, + _options: impl Into>, + ) -> Result<()> { + let op = UpdateSearchIndex::new( + self.namespace(), + name.as_ref().to_string(), + definition.clone(), + ); self.client().execute_operation(op, None).await } /// Drops the search index with the given name. - pub async fn drop_search_index(&self, name: impl AsRef, _options: impl Into>) -> Result<()> { + pub async fn drop_search_index( + &self, + name: impl AsRef, + _options: impl Into>, + ) -> Result<()> { let op = DropSearchIndex::new(self.namespace(), name.as_ref().to_string()); self.client().execute_operation(op, None).await } /// Gets index information for one or more search indexes in the collection. /// - /// If name is not specified, information for all indexes on the specified collection will be returned. - pub async fn list_search_indexes(&self, name: impl Into>, aggregation_options: impl Into>, _list_index_options: impl Into>) -> Result> - { - let mut inner = doc! { }; + /// If name is not specified, information for all indexes on the specified collection will be + /// returned. + pub async fn list_search_indexes( + &self, + name: impl Into>, + aggregation_options: impl Into>, + _list_index_options: impl Into>, + ) -> Result> { + let mut inner = doc! {}; if let Some(name) = name.into() { inner.insert("name", name.to_string()); } self.aggregate( - vec![ - doc! { - "$listSearchIndexes": inner, - } - ], + vec![doc! { + "$listSearchIndexes": inner, + }], aggregation_options, - ).await + ) + .await } } @@ -69,23 +101,27 @@ pub mod options { use serde::Deserialize; use typed_builder::TypedBuilder; - /// Options for [Collection::create_search_index]. Present to allow additional options to be added in the future as a non-breaking change. + /// Options for [Collection::create_search_index]. Present to allow additional options to be + /// added in the future as a non-breaking change. #[derive(Clone, Debug, Default, TypedBuilder, Deserialize)] #[non_exhaustive] - pub struct CreateSearchIndexOptions { } + pub struct CreateSearchIndexOptions {} - /// Options for [Collection::update_search_index]. Present to allow additional options to be added in the future as a non-breaking change. + /// Options for [Collection::update_search_index]. Present to allow additional options to be + /// added in the future as a non-breaking change. #[derive(Clone, Debug, Default, TypedBuilder, Deserialize)] #[non_exhaustive] - pub struct UpdateSearchIndexOptions { } + pub struct UpdateSearchIndexOptions {} - /// Options for [Collection::list_search_indexes]. Present to allow additional options to be added in the future as a non-breaking change. + /// Options for [Collection::list_search_indexes]. Present to allow additional options to be + /// added in the future as a non-breaking change. #[derive(Clone, Debug, Default, TypedBuilder, Deserialize)] #[non_exhaustive] - pub struct ListSearchIndexOptions { } + pub struct ListSearchIndexOptions {} - /// Options for [Collection::drop_search_index]. Present to allow additional options to be added in the future as a non-breaking change. + /// Options for [Collection::drop_search_index]. Present to allow additional options to be + /// added in the future as a non-breaking change. #[derive(Clone, Debug, Default, TypedBuilder, Deserialize)] #[non_exhaustive] - pub struct DropSearchIndexOptions { } -} \ No newline at end of file + pub struct DropSearchIndexOptions {} +} diff --git a/src/test/spec/unified_runner/operation.rs b/src/test/spec/unified_runner/operation.rs index d93b4a680..41d1910c4 100644 --- a/src/test/spec/unified_runner/operation.rs +++ b/src/test/spec/unified_runner/operation.rs @@ -385,11 +385,21 @@ impl<'de> Deserialize<'de> for Operation { #[cfg(feature = "in-use-encryption-unstable")] "removeKeyAltName" => deserialize_op::(definition.arguments), "iterateOnce" => deserialize_op::(definition.arguments), - "createSearchIndex" => deserialize_op::(definition.arguments), - "createSearchIndexes" => deserialize_op::(definition.arguments), - "dropSearchIndex" => deserialize_op::(definition.arguments), - "listSearchIndexes" => deserialize_op::(definition.arguments), - "updateSearchIndex" => deserialize_op::(definition.arguments), + "createSearchIndex" => { + deserialize_op::(definition.arguments) + } + "createSearchIndexes" => { + deserialize_op::(definition.arguments) + } + "dropSearchIndex" => { + deserialize_op::(definition.arguments) + } + "listSearchIndexes" => { + deserialize_op::(definition.arguments) + } + "updateSearchIndex" => { + deserialize_op::(definition.arguments) + } s => Ok(Box::new(UnimplementedOperation { _name: s.to_string(), }) as Box), diff --git a/src/test/spec/unified_runner/operation/search_index.rs b/src/test/spec/unified_runner/operation/search_index.rs index 96ebb2017..a5c2f4c70 100644 --- a/src/test/spec/unified_runner/operation/search_index.rs +++ b/src/test/spec/unified_runner/operation/search_index.rs @@ -1,9 +1,20 @@ -use bson::{Bson, to_bson, Document}; +use bson::{to_bson, Bson, Document}; use futures_core::future::BoxFuture; use futures_util::{FutureExt, TryStreamExt}; use serde::Deserialize; -use crate::{error::Result, search_index::options::{CreateSearchIndexOptions, DropSearchIndexOptions, ListSearchIndexOptions, UpdateSearchIndexOptions}, SearchIndexModel, test::spec::unified_runner::{TestRunner, Entity}, coll::options::AggregateOptions}; +use crate::{ + coll::options::AggregateOptions, + error::Result, + search_index::options::{ + CreateSearchIndexOptions, + DropSearchIndexOptions, + ListSearchIndexOptions, + UpdateSearchIndexOptions, + }, + test::spec::unified_runner::{Entity, TestRunner}, + SearchIndexModel, +}; use super::TestOperation; @@ -23,9 +34,12 @@ impl TestOperation for CreateSearchIndex { ) -> BoxFuture<'a, Result>> { async move { let collection = test_runner.get_collection(id).await; - let name = collection.create_search_index(self.model.clone(), self.options.clone()).await?; + let name = collection + .create_search_index(self.model.clone(), self.options.clone()) + .await?; Ok(Some(Bson::String(name).into())) - }.boxed() + } + .boxed() } } @@ -45,9 +59,12 @@ impl TestOperation for CreateSearchIndexes { ) -> BoxFuture<'a, Result>> { async move { let collection = test_runner.get_collection(id).await; - let names = collection.create_search_indexes(self.models.clone(), self.options.clone()).await?; + let names = collection + .create_search_indexes(self.models.clone(), self.options.clone()) + .await?; Ok(Some(to_bson(&names)?.into())) - }.boxed() + } + .boxed() } } @@ -67,9 +84,12 @@ impl TestOperation for DropSearchIndex { ) -> BoxFuture<'a, Result>> { async move { let collection = test_runner.get_collection(id).await; - collection.drop_search_index(&self.name, self.options.clone()).await?; + collection + .drop_search_index(&self.name, self.options.clone()) + .await?; Ok(None) - }.boxed() + } + .boxed() } } @@ -90,10 +110,17 @@ impl TestOperation for ListSearchIndexes { ) -> BoxFuture<'a, Result>> { async move { let collection = test_runner.get_collection(id).await; - let cursor = collection.list_search_indexes(self.name.as_ref().map(|s| s.as_str()), self.aggregation_options.clone(), self.options.clone()).await?; + let cursor = collection + .list_search_indexes( + self.name.as_ref().map(|s| s.as_str()), + self.aggregation_options.clone(), + self.options.clone(), + ) + .await?; let values: Vec<_> = cursor.try_collect().await?; Ok(Some(to_bson(&values)?.into())) - }.boxed() + } + .boxed() } } @@ -114,8 +141,11 @@ impl TestOperation for UpdateSearchIndex { ) -> BoxFuture<'a, Result>> { async move { let collection = test_runner.get_collection(id).await; - collection.update_search_index(&self.name, self.definition.clone(), self.options.clone()).await?; + collection + .update_search_index(&self.name, self.definition.clone(), self.options.clone()) + .await?; Ok(None) - }.boxed() + } + .boxed() } -} \ No newline at end of file +} From fe8e6a1193e13724fb7fdb1b17d383fc19fc840a Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Thu, 9 Nov 2023 10:29:28 -0500 Subject: [PATCH 18/21] clippy and doc fixes --- src/search_index.rs | 2 ++ src/test/spec/unified_runner/operation/search_index.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search_index.rs b/src/search_index.rs index 05f696074..35237ecba 100644 --- a/src/search_index.rs +++ b/src/search_index.rs @@ -100,6 +100,8 @@ pub struct SearchIndexModel { pub mod options { use serde::Deserialize; use typed_builder::TypedBuilder; + #[cfg(docsrs)] + use crate::Collection; /// Options for [Collection::create_search_index]. Present to allow additional options to be /// added in the future as a non-breaking change. diff --git a/src/test/spec/unified_runner/operation/search_index.rs b/src/test/spec/unified_runner/operation/search_index.rs index a5c2f4c70..461d53df5 100644 --- a/src/test/spec/unified_runner/operation/search_index.rs +++ b/src/test/spec/unified_runner/operation/search_index.rs @@ -112,7 +112,7 @@ impl TestOperation for ListSearchIndexes { let collection = test_runner.get_collection(id).await; let cursor = collection .list_search_indexes( - self.name.as_ref().map(|s| s.as_str()), + self.name.as_deref(), self.aggregation_options.clone(), self.options.clone(), ) From 9be55368428603c6fc68040f8867d71da96c00f8 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Thu, 9 Nov 2023 11:46:00 -0500 Subject: [PATCH 19/21] rustfmt --- src/search_index.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search_index.rs b/src/search_index.rs index 35237ecba..475e02980 100644 --- a/src/search_index.rs +++ b/src/search_index.rs @@ -98,10 +98,10 @@ pub struct SearchIndexModel { } pub mod options { - use serde::Deserialize; - use typed_builder::TypedBuilder; #[cfg(docsrs)] use crate::Collection; + use serde::Deserialize; + use typed_builder::TypedBuilder; /// Options for [Collection::create_search_index]. Present to allow additional options to be /// added in the future as a non-breaking change. From 00202d24705480b0ebdc15e9df7d7c6d64291a87 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Fri, 10 Nov 2023 14:16:42 -0500 Subject: [PATCH 20/21] ignore read/write concern --- src/coll.rs | 12 +- src/operation/search_index.rs | 24 ++ src/search_index.rs | 15 +- .../searchIndexIgnoresReadWriteConcern.json | 252 ++++++++++++++++++ 4 files changed, 295 insertions(+), 8 deletions(-) create mode 100644 src/test/spec/json/index-management/searchIndexIgnoresReadWriteConcern.json diff --git a/src/coll.rs b/src/coll.rs index 953906bca..b8064fd74 100644 --- a/src/coll.rs +++ b/src/coll.rs @@ -135,7 +135,7 @@ impl Clone for Collection { } } -#[derive(Debug)] +#[derive(Debug, Clone)] struct CollectionInner { client: Client, db: Database, @@ -183,6 +183,16 @@ impl Collection { } } + pub(crate) fn clone_unconcerned(&self) -> Self { + let mut new_inner = CollectionInner::clone(&self.inner); + new_inner.write_concern = None; + new_inner.read_concern = None; + Self { + inner: Arc::new(new_inner), + _phantom: Default::default(), + } + } + /// Get the `Client` that this collection descended from. pub fn client(&self) -> &Client { &self.inner.client diff --git a/src/operation/search_index.rs b/src/operation/search_index.rs index 9da08e1aa..57267f8d0 100644 --- a/src/operation/search_index.rs +++ b/src/operation/search_index.rs @@ -58,6 +58,14 @@ impl OperationWithDefaults for CreateSearchIndexes { .map(|ci| ci.name) .collect()) } + + fn supports_sessions(&self) -> bool { + false + } + + fn supports_read_concern(&self, _description: &crate::cmap::StreamDescription) -> bool { + false + } } #[derive(Debug)] @@ -104,6 +112,14 @@ impl OperationWithDefaults for UpdateSearchIndex { ) -> crate::error::Result { response.body() } + + fn supports_sessions(&self) -> bool { + false + } + + fn supports_read_concern(&self, _description: &crate::cmap::StreamDescription) -> bool { + false + } } #[derive(Debug)] @@ -152,4 +168,12 @@ impl OperationWithDefaults for DropSearchIndex { Err(error) } } + + fn supports_sessions(&self) -> bool { + false + } + + fn supports_read_concern(&self, _description: &crate::cmap::StreamDescription) -> bool { + false + } } diff --git a/src/search_index.rs b/src/search_index.rs index 475e02980..8e1500f61 100644 --- a/src/search_index.rs +++ b/src/search_index.rs @@ -75,13 +75,14 @@ impl Collection { if let Some(name) = name.into() { inner.insert("name", name.to_string()); } - self.aggregate( - vec![doc! { - "$listSearchIndexes": inner, - }], - aggregation_options, - ) - .await + self.clone_unconcerned() + .aggregate( + vec![doc! { + "$listSearchIndexes": inner, + }], + aggregation_options, + ) + .await } } diff --git a/src/test/spec/json/index-management/searchIndexIgnoresReadWriteConcern.json b/src/test/spec/json/index-management/searchIndexIgnoresReadWriteConcern.json new file mode 100644 index 000000000..102f5a767 --- /dev/null +++ b/src/test/spec/json/index-management/searchIndexIgnoresReadWriteConcern.json @@ -0,0 +1,252 @@ +{ + "description": "search index operations ignore read and write concern", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "uriOptions": { + "readConcernLevel": "local", + "w": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "tests": [ + { + "description": "createSearchIndex ignores read and write concern", + "operations": [ + { + "name": "createSearchIndex", + "object": "collection0", + "arguments": { + "model": { + "definition": { + "mappings": { + "dynamic": true + } + } + } + }, + "expectError": { + "isError": true, + "errorContains": "Search index commands are only supported with Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createSearchIndexes": "collection0", + "indexes": [ + { + "definition": { + "mappings": { + "dynamic": true + } + } + } + ], + "$db": "database0", + "writeConcern": { + "$$exists": false + }, + "readConcern": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "createSearchIndex ignores read and write concern", + "operations": [ + { + "name": "createSearchIndexes", + "object": "collection0", + "arguments": { + "models": [] + }, + "expectError": { + "isError": true, + "errorContains": "Search index commands are only supported with Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createSearchIndexes": "collection0", + "indexes": [], + "$db": "database0", + "writeConcern": { + "$$exists": false + }, + "readConcern": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "dropSearchIndex ignores read and write concern", + "operations": [ + { + "name": "dropSearchIndex", + "object": "collection0", + "arguments": { + "name": "test index" + }, + "expectError": { + "isError": true, + "errorContains": "Search index commands are only supported with Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "dropSearchIndex": "collection0", + "name": "test index", + "$db": "database0", + "writeConcern": { + "$$exists": false + }, + "readConcern": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "listSearchIndexes ignores read and write concern", + "operations": [ + { + "name": "listSearchIndexes", + "object": "collection0", + "expectError": { + "isError": true, + "errorContains": "Search index commands are only supported with Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "pipeline": [ + { + "$listSearchIndexes": {} + } + ], + "writeConcern": { + "$$exists": false + }, + "readConcern": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "updateSearchIndex ignores the read and write concern", + "operations": [ + { + "name": "updateSearchIndex", + "object": "collection0", + "arguments": { + "name": "test index", + "definition": {} + }, + "expectError": { + "isError": true, + "errorContains": "Search index commands are only supported with Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "updateSearchIndex": "collection0", + "name": "test index", + "definition": {}, + "$db": "database0", + "writeConcern": { + "$$exists": false + }, + "readConcern": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] + } \ No newline at end of file From 8f54a8acd75b3710b0e2ac9e4a704678c659557a Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Mon, 13 Nov 2023 10:50:43 -0500 Subject: [PATCH 21/21] add builder defaults --- src/search_index.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/search_index.rs b/src/search_index.rs index 8e1500f61..91c18cb5c 100644 --- a/src/search_index.rs +++ b/src/search_index.rs @@ -88,6 +88,7 @@ impl Collection { /// Specifies the options for a search index. #[derive(Debug, Clone, Default, TypedBuilder, Serialize, Deserialize)] +#[builder(field_defaults(default, setter(into)))] #[non_exhaustive] pub struct SearchIndexModel { /// The definition for this index. @@ -107,24 +108,28 @@ pub mod options { /// Options for [Collection::create_search_index]. Present to allow additional options to be /// added in the future as a non-breaking change. #[derive(Clone, Debug, Default, TypedBuilder, Deserialize)] + #[builder(field_defaults(default, setter(into)))] #[non_exhaustive] pub struct CreateSearchIndexOptions {} /// Options for [Collection::update_search_index]. Present to allow additional options to be /// added in the future as a non-breaking change. #[derive(Clone, Debug, Default, TypedBuilder, Deserialize)] + #[builder(field_defaults(default, setter(into)))] #[non_exhaustive] pub struct UpdateSearchIndexOptions {} /// Options for [Collection::list_search_indexes]. Present to allow additional options to be /// added in the future as a non-breaking change. #[derive(Clone, Debug, Default, TypedBuilder, Deserialize)] + #[builder(field_defaults(default, setter(into)))] #[non_exhaustive] pub struct ListSearchIndexOptions {} /// Options for [Collection::drop_search_index]. Present to allow additional options to be /// added in the future as a non-breaking change. #[derive(Clone, Debug, Default, TypedBuilder, Deserialize)] + #[builder(field_defaults(default, setter(into)))] #[non_exhaustive] pub struct DropSearchIndexOptions {} }