Skip to content

Commit 31d9a37

Browse files
RUST-1819 Fix list_collections on Atlas for clustered collections (#1077)
--------- Co-authored-by: Jesse Hallett <[email protected]>
1 parent eed6343 commit 31d9a37

File tree

2 files changed

+71
-1
lines changed

2 files changed

+71
-1
lines changed

src/db/options.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ pub struct CreateCollectionOptions {
104104
/// Options for supporting change stream pre- and post-images.
105105
pub change_stream_pre_and_post_images: Option<ChangeStreamPreAndPostImages>,
106106

107-
/// Options for clustered collections.
107+
/// Options for clustered collections. This option is only available on server versions 5.3+.
108+
#[serde(default, deserialize_with = "ClusteredIndex::deserialize_self_or_true")]
108109
pub clustered_index: Option<ClusteredIndex>,
109110

110111
/// Tags the query with an arbitrary [`Bson`] value to help trace the operation through the
@@ -176,6 +177,35 @@ impl Default for ClusteredIndex {
176177
}
177178
}
178179

180+
impl ClusteredIndex {
181+
/// When creating a time-series collection on MongoDB Atlas the `clusteredIndex` field of the
182+
/// collection options is given as `true` instead of as an object that deserializes to
183+
/// `ClusteredIndex`. This custom deserializer handles that case by using the default value for
184+
/// `ClusteredIndex`.
185+
fn deserialize_self_or_true<'de, D>(deserializer: D) -> Result<Option<ClusteredIndex>, D::Error>
186+
where
187+
D: serde::Deserializer<'de>,
188+
{
189+
#[derive(Debug, Deserialize)]
190+
#[serde(untagged)]
191+
enum ValueUnion {
192+
Bool(bool),
193+
ClusteredIndex(ClusteredIndex),
194+
}
195+
196+
let value_option: Option<ValueUnion> = Deserialize::deserialize(deserializer)?;
197+
value_option
198+
.map(|value| match value {
199+
ValueUnion::Bool(true) => Ok(ClusteredIndex::default()),
200+
ValueUnion::Bool(false) => Err(serde::de::Error::custom(
201+
"if clusteredIndex is a boolean it must be `true`",
202+
)),
203+
ValueUnion::ClusteredIndex(value) => Ok(value),
204+
})
205+
.transpose()
206+
}
207+
}
208+
179209
/// Specifies default configuration for indexes created on a collection, including the _id index.
180210
#[derive(Clone, Debug, TypedBuilder, PartialEq, Serialize, Deserialize)]
181211
#[builder(field_defaults(default, setter(into)))]

src/test/db.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::{
77
bson::{doc, Document},
88
error::Result,
99
options::{
10+
ClusteredIndex,
1011
Collation,
1112
CreateCollectionOptions,
1213
IndexOptionDefaults,
@@ -338,3 +339,42 @@ async fn index_option_defaults_test(defaults: Option<IndexOptionDefaults>, name:
338339
};
339340
assert_eq!(event_defaults, defaults);
340341
}
342+
343+
#[test]
344+
fn deserialize_clustered_index_option_from_bool() {
345+
let options_doc = doc! { "clusteredIndex": true };
346+
let options: CreateCollectionOptions = bson::from_document(options_doc).unwrap();
347+
let clustered_index = options
348+
.clustered_index
349+
.expect("deserialized options should include clustered_index");
350+
assert_eq!(clustered_index, ClusteredIndex::default());
351+
}
352+
353+
#[tokio::test]
354+
async fn clustered_index_list_collections() {
355+
let client = TestClient::new().await;
356+
let database = client.database("db");
357+
358+
if client.server_version_lt(5, 3) {
359+
return;
360+
}
361+
362+
database
363+
.create_collection("clustered_index_collection")
364+
.clustered_index(ClusteredIndex::default())
365+
.await
366+
.unwrap();
367+
368+
let collections = database
369+
.list_collections()
370+
.await
371+
.unwrap()
372+
.try_collect::<Vec<_>>()
373+
.await
374+
.unwrap();
375+
let clustered_index_collection = collections
376+
.iter()
377+
.find(|specification| specification.name == "clustered_index_collection")
378+
.unwrap();
379+
assert!(clustered_index_collection.options.clustered_index.is_some());
380+
}

0 commit comments

Comments
 (0)