Skip to content

Commit c4de516

Browse files
authored
RUST-1667 Add search index management helpers (#989)
1 parent e57da8a commit c4de516

22 files changed

+1889
-2
lines changed

src/coll.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ impl<T> Clone for Collection<T> {
135135
}
136136
}
137137

138-
#[derive(Debug)]
138+
#[derive(Debug, Clone)]
139139
struct CollectionInner {
140140
client: Client,
141141
db: Database,
@@ -183,6 +183,16 @@ impl<T> Collection<T> {
183183
}
184184
}
185185

186+
pub(crate) fn clone_unconcerned(&self) -> Self {
187+
let mut new_inner = CollectionInner::clone(&self.inner);
188+
new_inner.write_concern = None;
189+
new_inner.read_concern = None;
190+
Self {
191+
inner: Arc::new(new_inner),
192+
_phantom: Default::default(),
193+
}
194+
}
195+
186196
/// Get the `Client` that this collection descended from.
187197
pub fn client(&self) -> &Client {
188198
&self.inner.client

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ mod index;
337337
mod operation;
338338
pub mod results;
339339
pub(crate) mod runtime;
340+
mod search_index;
340341
mod sdam;
341342
mod selection_criteria;
342343
mod serde_util;
@@ -362,7 +363,7 @@ pub use crate::{
362363
gridfs::{GridFsBucket, GridFsDownloadStream, GridFsUploadStream},
363364
};
364365

365-
pub use {client::session::ClusterTime, coll::Namespace, index::IndexModel, sdam::public::*};
366+
pub use {client::session::ClusterTime, coll::Namespace, index::IndexModel, sdam::public::*, search_index::SearchIndexModel};
366367

367368
#[cfg(all(feature = "tokio-runtime", feature = "sync",))]
368369
compile_error!(

src/operation.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ mod list_indexes;
2020
mod raw_output;
2121
mod run_command;
2222
mod run_cursor_command;
23+
mod search_index;
2324
mod update;
2425

2526
#[cfg(test)]
@@ -73,6 +74,7 @@ pub(crate) use list_indexes::ListIndexes;
7374
pub(crate) use raw_output::RawOutput;
7475
pub(crate) use run_command::RunCommand;
7576
pub(crate) use run_cursor_command::RunCursorCommand;
77+
pub(crate) use search_index::{CreateSearchIndexes, DropSearchIndex, UpdateSearchIndex};
7678
pub(crate) use update::{Update, UpdateOrReplace};
7779

7880
const SERVER_4_2_0_WIRE_VERSION: i32 = 8;

src/operation/search_index.rs

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
use bson::{doc, Document};
2+
use serde::Deserialize;
3+
4+
use crate::{cmap::Command, error::Result, Namespace, SearchIndexModel};
5+
6+
use super::OperationWithDefaults;
7+
8+
#[derive(Debug)]
9+
pub(crate) struct CreateSearchIndexes {
10+
ns: Namespace,
11+
indexes: Vec<SearchIndexModel>,
12+
}
13+
14+
impl CreateSearchIndexes {
15+
pub(crate) fn new(ns: Namespace, indexes: Vec<SearchIndexModel>) -> Self {
16+
Self { ns, indexes }
17+
}
18+
}
19+
20+
impl OperationWithDefaults for CreateSearchIndexes {
21+
type O = Vec<String>;
22+
type Command = Document;
23+
const NAME: &'static str = "createSearchIndexes";
24+
25+
fn build(&mut self, _description: &crate::cmap::StreamDescription) -> Result<Command> {
26+
Ok(Command::new(
27+
Self::NAME.to_string(),
28+
self.ns.db.clone(),
29+
doc! {
30+
Self::NAME: self.ns.coll.clone(),
31+
"indexes": bson::to_bson(&self.indexes)?,
32+
},
33+
))
34+
}
35+
36+
fn handle_response(
37+
&self,
38+
response: crate::cmap::RawCommandResponse,
39+
_description: &crate::cmap::StreamDescription,
40+
) -> Result<Self::O> {
41+
#[derive(Debug, Deserialize)]
42+
#[serde(rename_all = "camelCase")]
43+
struct Response {
44+
indexes_created: Vec<CreatedIndex>,
45+
}
46+
47+
#[derive(Debug, Deserialize)]
48+
struct CreatedIndex {
49+
#[allow(unused)]
50+
id: String,
51+
name: String,
52+
}
53+
54+
let response: Response = response.body()?;
55+
Ok(response
56+
.indexes_created
57+
.into_iter()
58+
.map(|ci| ci.name)
59+
.collect())
60+
}
61+
62+
fn supports_sessions(&self) -> bool {
63+
false
64+
}
65+
66+
fn supports_read_concern(&self, _description: &crate::cmap::StreamDescription) -> bool {
67+
false
68+
}
69+
}
70+
71+
#[derive(Debug)]
72+
pub(crate) struct UpdateSearchIndex {
73+
ns: Namespace,
74+
name: String,
75+
definition: Document,
76+
}
77+
78+
impl UpdateSearchIndex {
79+
pub(crate) fn new(ns: Namespace, name: String, definition: Document) -> Self {
80+
Self {
81+
ns,
82+
name,
83+
definition,
84+
}
85+
}
86+
}
87+
88+
impl OperationWithDefaults for UpdateSearchIndex {
89+
type O = ();
90+
type Command = Document;
91+
const NAME: &'static str = "updateSearchIndex";
92+
93+
fn build(
94+
&mut self,
95+
_description: &crate::cmap::StreamDescription,
96+
) -> crate::error::Result<crate::cmap::Command<Self::Command>> {
97+
Ok(Command::new(
98+
Self::NAME.to_string(),
99+
self.ns.db.clone(),
100+
doc! {
101+
Self::NAME: self.ns.coll.clone(),
102+
"name": &self.name,
103+
"definition": &self.definition,
104+
},
105+
))
106+
}
107+
108+
fn handle_response(
109+
&self,
110+
response: crate::cmap::RawCommandResponse,
111+
_description: &crate::cmap::StreamDescription,
112+
) -> crate::error::Result<Self::O> {
113+
response.body()
114+
}
115+
116+
fn supports_sessions(&self) -> bool {
117+
false
118+
}
119+
120+
fn supports_read_concern(&self, _description: &crate::cmap::StreamDescription) -> bool {
121+
false
122+
}
123+
}
124+
125+
#[derive(Debug)]
126+
pub(crate) struct DropSearchIndex {
127+
ns: Namespace,
128+
name: String,
129+
}
130+
131+
impl DropSearchIndex {
132+
pub(crate) fn new(ns: Namespace, name: String) -> Self {
133+
Self { ns, name }
134+
}
135+
}
136+
137+
impl OperationWithDefaults for DropSearchIndex {
138+
type O = ();
139+
type Command = Document;
140+
const NAME: &'static str = "dropSearchIndex";
141+
142+
fn build(
143+
&mut self,
144+
_description: &crate::cmap::StreamDescription,
145+
) -> Result<Command<Self::Command>> {
146+
Ok(Command::new(
147+
Self::NAME.to_string(),
148+
self.ns.db.clone(),
149+
doc! {
150+
Self::NAME: self.ns.coll.clone(),
151+
"name": &self.name,
152+
},
153+
))
154+
}
155+
156+
fn handle_response(
157+
&self,
158+
response: crate::cmap::RawCommandResponse,
159+
_description: &crate::cmap::StreamDescription,
160+
) -> Result<Self::O> {
161+
response.body()
162+
}
163+
164+
fn handle_error(&self, error: crate::error::Error) -> Result<Self::O> {
165+
if error.is_ns_not_found() {
166+
Ok(())
167+
} else {
168+
Err(error)
169+
}
170+
}
171+
172+
fn supports_sessions(&self) -> bool {
173+
false
174+
}
175+
176+
fn supports_read_concern(&self, _description: &crate::cmap::StreamDescription) -> bool {
177+
false
178+
}
179+
}

src/options.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub use crate::{
2525
db::options::*,
2626
gridfs::options::*,
2727
index::options::*,
28+
search_index::options::*,
2829
selection_criteria::*,
2930
};
3031

src/search_index.rs

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
use self::options::*;
2+
use crate::{
3+
bson::Document,
4+
coll::options::AggregateOptions,
5+
error::{Error, Result},
6+
operation::{CreateSearchIndexes, DropSearchIndex, UpdateSearchIndex},
7+
Collection,
8+
Cursor,
9+
};
10+
11+
use bson::doc;
12+
use serde::{Deserialize, Serialize};
13+
use typed_builder::TypedBuilder;
14+
15+
impl<T> Collection<T> {
16+
/// Convenience method for creating a single search index.
17+
pub async fn create_search_index(
18+
&self,
19+
model: SearchIndexModel,
20+
options: impl Into<Option<CreateSearchIndexOptions>>,
21+
) -> Result<String> {
22+
let mut names = self.create_search_indexes(Some(model), options).await?;
23+
match names.len() {
24+
1 => Ok(names.pop().unwrap()),
25+
n => Err(Error::internal(format!("expected 1 index name, got {}", n))),
26+
}
27+
}
28+
29+
/// Creates multiple search indexes on the collection.
30+
pub async fn create_search_indexes(
31+
&self,
32+
models: impl IntoIterator<Item = SearchIndexModel>,
33+
_options: impl Into<Option<CreateSearchIndexOptions>>,
34+
) -> Result<Vec<String>> {
35+
let op = CreateSearchIndexes::new(self.namespace(), models.into_iter().collect());
36+
self.client().execute_operation(op, None).await
37+
}
38+
39+
/// Updates the search index with the given name to use the provided definition.
40+
pub async fn update_search_index(
41+
&self,
42+
name: impl AsRef<str>,
43+
definition: Document,
44+
_options: impl Into<Option<UpdateSearchIndexOptions>>,
45+
) -> Result<()> {
46+
let op = UpdateSearchIndex::new(
47+
self.namespace(),
48+
name.as_ref().to_string(),
49+
definition.clone(),
50+
);
51+
self.client().execute_operation(op, None).await
52+
}
53+
54+
/// Drops the search index with the given name.
55+
pub async fn drop_search_index(
56+
&self,
57+
name: impl AsRef<str>,
58+
_options: impl Into<Option<DropSearchIndexOptions>>,
59+
) -> Result<()> {
60+
let op = DropSearchIndex::new(self.namespace(), name.as_ref().to_string());
61+
self.client().execute_operation(op, None).await
62+
}
63+
64+
/// Gets index information for one or more search indexes in the collection.
65+
///
66+
/// If name is not specified, information for all indexes on the specified collection will be
67+
/// returned.
68+
pub async fn list_search_indexes(
69+
&self,
70+
name: impl Into<Option<&str>>,
71+
aggregation_options: impl Into<Option<AggregateOptions>>,
72+
_list_index_options: impl Into<Option<ListSearchIndexOptions>>,
73+
) -> Result<Cursor<Document>> {
74+
let mut inner = doc! {};
75+
if let Some(name) = name.into() {
76+
inner.insert("name", name.to_string());
77+
}
78+
self.clone_unconcerned()
79+
.aggregate(
80+
vec![doc! {
81+
"$listSearchIndexes": inner,
82+
}],
83+
aggregation_options,
84+
)
85+
.await
86+
}
87+
}
88+
89+
/// Specifies the options for a search index.
90+
#[derive(Debug, Clone, Default, TypedBuilder, Serialize, Deserialize)]
91+
#[builder(field_defaults(default, setter(into)))]
92+
#[non_exhaustive]
93+
pub struct SearchIndexModel {
94+
/// The definition for this index.
95+
pub definition: Document,
96+
97+
/// The name for this index, if present.
98+
#[serde(skip_serializing_if = "Option::is_none")]
99+
pub name: Option<String>,
100+
}
101+
102+
pub mod options {
103+
#[cfg(docsrs)]
104+
use crate::Collection;
105+
use serde::Deserialize;
106+
use typed_builder::TypedBuilder;
107+
108+
/// Options for [Collection::create_search_index]. Present to allow additional options to be
109+
/// added in the future as a non-breaking change.
110+
#[derive(Clone, Debug, Default, TypedBuilder, Deserialize)]
111+
#[builder(field_defaults(default, setter(into)))]
112+
#[non_exhaustive]
113+
pub struct CreateSearchIndexOptions {}
114+
115+
/// Options for [Collection::update_search_index]. Present to allow additional options to be
116+
/// added in the future as a non-breaking change.
117+
#[derive(Clone, Debug, Default, TypedBuilder, Deserialize)]
118+
#[builder(field_defaults(default, setter(into)))]
119+
#[non_exhaustive]
120+
pub struct UpdateSearchIndexOptions {}
121+
122+
/// Options for [Collection::list_search_indexes]. Present to allow additional options to be
123+
/// added in the future as a non-breaking change.
124+
#[derive(Clone, Debug, Default, TypedBuilder, Deserialize)]
125+
#[builder(field_defaults(default, setter(into)))]
126+
#[non_exhaustive]
127+
pub struct ListSearchIndexOptions {}
128+
129+
/// Options for [Collection::drop_search_index]. Present to allow additional options to be
130+
/// added in the future as a non-breaking change.
131+
#[derive(Clone, Debug, Default, TypedBuilder, Deserialize)]
132+
#[builder(field_defaults(default, setter(into)))]
133+
#[non_exhaustive]
134+
pub struct DropSearchIndexOptions {}
135+
}

src/test/spec.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod crud;
1010
mod crud_v1;
1111
mod faas;
1212
mod gridfs;
13+
mod index_management;
1314
#[cfg(all(not(feature = "sync"), not(feature = "tokio-sync")))]
1415
mod initial_dns_seedlist_discovery;
1516
mod load_balancers;

src/test/spec/index_management.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use crate::test::spec::unified_runner::run_unified_tests;
2+
3+
#[cfg_attr(feature = "tokio-runtime", tokio::test)]
4+
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
5+
async fn run() {
6+
run_unified_tests(&["index-management"]).await;
7+
}

0 commit comments

Comments
 (0)