From 633dc509928352083a5636895fa38e06b8fc99c3 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Wed, 18 May 2022 16:04:17 -0400 Subject: [PATCH 1/2] RUST-1271 Support clustered indexes for all collections --- src/db/options.rs | 35 +++ src/index/options.rs | 14 + .../json/collection-management/README.rst | 3 +- .../clustered-indexes.json | 291 ++++++++++++++++++ .../clustered-indexes.yml | 135 ++++++++ 5 files changed, 476 insertions(+), 2 deletions(-) create mode 100644 src/test/spec/json/collection-management/clustered-indexes.json create mode 100644 src/test/spec/json/collection-management/clustered-indexes.yml diff --git a/src/db/options.rs b/src/db/options.rs index 6f86de738..9ffcc3d0d 100644 --- a/src/db/options.rs +++ b/src/db/options.rs @@ -1,5 +1,6 @@ use std::time::Duration; +use bson::doc; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; use typed_builder::TypedBuilder; @@ -106,6 +107,9 @@ pub struct CreateCollectionOptions { /// Options for supporting change stream pre- and post-images. pub change_stream_pre_and_post_images: Option, + + /// Options for clustered connections. + pub clustered_index: Option, } /// Specifies how strictly the database should apply validation rules to existing documents during @@ -135,6 +139,37 @@ pub enum ValidationAction { Warn, } +/// Specifies options for a clustered connection. Some fields have required values; the `Default` +/// impl uses those values. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct ClusteredIndex { + /// Key pattern; currently required to be `{_id: 1}`. + pub key: Document, + + /// Currently required to be `true`. + pub unique: bool, + + /// Optional; will be automatically generated if not provided. + pub name: Option, + + /// Optional; currently must be `2` if provided. + #[serde(skip_serializing_if = "Option::is_none")] + pub v: Option, +} + +impl Default for ClusteredIndex { + fn default() -> Self { + Self { + key: doc! { "_id": 1 }, + unique: true, + name: None, + v: None, + } + } +} + /// Specifies default configuration for indexes created on a collection, including the _id index. #[derive(Clone, Debug, TypedBuilder, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] diff --git a/src/index/options.rs b/src/index/options.rs index 3e5a81bd3..0aa9339df 100644 --- a/src/index/options.rs +++ b/src/index/options.rs @@ -111,6 +111,20 @@ pub struct IndexOptions { /// A flag that determines whether the index is hidden from the query planner. A /// hidden index is not evaluated as part of the query plan selection. pub hidden: Option, + + #[builder(default, setter(skip))] + clustered: Option, +} + +impl IndexOptions { + /// Optionally specifies that this index is clustered. This is not a valid option to provide to + /// 'create_indexes', but can appear in the options returned for an index via 'list_indexes'. + /// To create a clustered index, create a new collection using the 'clustered_index' option. + /// + /// This options is only supported by servers >= 6.0. + pub fn clustered(&self) -> Option { + self.clustered + } } /// The version of the index. Version 0 Indexes are disallowed as of MongoDB 3.2. diff --git a/src/test/spec/json/collection-management/README.rst b/src/test/spec/json/collection-management/README.rst index 940161b9a..2fd8825a5 100644 --- a/src/test/spec/json/collection-management/README.rst +++ b/src/test/spec/json/collection-management/README.rst @@ -3,5 +3,4 @@ Collection Management Tests =========================== This directory contains tests for collection management. They are implemented -in the `Unified Test Format <../../unified-test-format/unified-test-format.rst>`__ -and require schema version 1.0. +in the `Unified Test Format <../../unified-test-format/unified-test-format.rst>`__. diff --git a/src/test/spec/json/collection-management/clustered-indexes.json b/src/test/spec/json/collection-management/clustered-indexes.json new file mode 100644 index 000000000..9db5ff06d --- /dev/null +++ b/src/test/spec/json/collection-management/clustered-indexes.json @@ -0,0 +1,291 @@ +{ + "description": "clustered-indexes", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "5.3", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "ci-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "ci-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "createCollection with clusteredIndex", + "operations": [ + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "test" + } + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "collection": "test", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true, + "name": "test index" + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "ci-tests", + "collectionName": "test" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "test" + }, + "databaseName": "ci-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "test", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true, + "name": "test index" + } + }, + "databaseName": "ci-tests" + } + } + ] + } + ] + }, + { + "description": "listCollections includes clusteredIndex", + "operations": [ + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "test" + } + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "collection": "test", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true, + "name": "test index" + } + } + }, + { + "name": "listCollections", + "object": "database0", + "arguments": { + "filter": { + "name": { + "$eq": "test" + } + } + }, + "expectResult": [ + { + "name": "test", + "options": { + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true, + "name": "test index", + "v": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "test" + }, + "databaseName": "ci-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "test", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true, + "name": "test index" + } + }, + "databaseName": "ci-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": { + "$eq": "test" + } + } + }, + "databaseName": "ci-tests" + } + } + ] + } + ] + }, + { + "description": "listIndexes returns the index", + "operations": [ + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "test" + } + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "collection": "test", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true, + "name": "test index" + } + } + }, + { + "name": "listIndexes", + "object": "collection0", + "expectResult": [ + { + "key": { + "_id": 1 + }, + "name": "test index", + "clustered": true, + "unique": true, + "v": { + "$$type": [ + "int", + "long" + ] + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "test" + }, + "databaseName": "ci-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "test", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true, + "name": "test index" + } + }, + "databaseName": "ci-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "test" + }, + "databaseName": "ci-tests" + } + } + ] + } + ] + } + ] +} diff --git a/src/test/spec/json/collection-management/clustered-indexes.yml b/src/test/spec/json/collection-management/clustered-indexes.yml new file mode 100644 index 000000000..e485735f8 --- /dev/null +++ b/src/test/spec/json/collection-management/clustered-indexes.yml @@ -0,0 +1,135 @@ +description: "clustered-indexes" + +schemaVersion: "1.4" + +runOnRequirements: + - minServerVersion: "5.3" + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name ci-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [] + +tests: + - description: "createCollection with clusteredIndex" + operations: + - name: dropCollection + object: *database0 + arguments: + collection: *collection0Name + - name: createCollection + object: *database0 + arguments: + collection: *collection0Name + clusteredIndex: &clusteredIndex + key: { _id: 1 } + unique: true + name: &index0Name "test index" + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database0Name + collectionName: *collection0Name + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + drop: *collection0Name + databaseName: *database0Name + - commandStartedEvent: + command: + create: *collection0Name + clusteredIndex: *clusteredIndex + databaseName: *database0Name + + - description: "listCollections includes clusteredIndex" + operations: + - name: dropCollection + object: *database0 + arguments: + collection: *collection0Name + - name: createCollection + object: *database0 + arguments: + collection: *collection0Name + clusteredIndex: *clusteredIndex + - name: listCollections + object: *database0 + arguments: + filter: &filter { name: { $eq: *collection0Name } } + expectResult: + - name: *collection0Name + options: + clusteredIndex: + key: { _id: 1 } + unique: true + name: *index0Name + v: { $$type: [ int, long ] } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + drop: *collection0Name + databaseName: *database0Name + - commandStartedEvent: + command: + create: *collection0Name + clusteredIndex: *clusteredIndex + databaseName: *database0Name + - commandStartedEvent: + command: + listCollections: 1 + filter: *filter + databaseName: *database0Name + + - description: "listIndexes returns the index" + operations: + - name: dropCollection + object: *database0 + arguments: + collection: *collection0Name + - name: createCollection + object: *database0 + arguments: + collection: *collection0Name + clusteredIndex: *clusteredIndex + - name: listIndexes + object: *collection0 + expectResult: + - key: { _id: 1 } + name: *index0Name + clustered: true + unique: true + v: { $$type: [ int, long ] } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + drop: *collection0Name + databaseName: *database0Name + - commandStartedEvent: + command: + create: *collection0Name + clusteredIndex: *clusteredIndex + databaseName: *database0Name + - commandStartedEvent: + command: + listIndexes: *collection0Name + databaseName: *database0Name From 1dc537c6fe720dd75299c21d51f56356a81cb0c1 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Wed, 18 May 2022 16:05:36 -0400 Subject: [PATCH 2/2] typo --- src/db/options.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/options.rs b/src/db/options.rs index 9ffcc3d0d..b548df778 100644 --- a/src/db/options.rs +++ b/src/db/options.rs @@ -108,7 +108,7 @@ pub struct CreateCollectionOptions { /// Options for supporting change stream pre- and post-images. pub change_stream_pre_and_post_images: Option, - /// Options for clustered connections. + /// Options for clustered collections. pub clustered_index: Option, } @@ -139,7 +139,7 @@ pub enum ValidationAction { Warn, } -/// Specifies options for a clustered connection. Some fields have required values; the `Default` +/// Specifies options for a clustered collection. Some fields have required values; the `Default` /// impl uses those values. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")]