Skip to content

Commit b46bdf2

Browse files
authored
CSHARP-4141: Clustered Indexes for all Collections. (#794)
1 parent 27a7c21 commit b46bdf2

File tree

15 files changed

+461
-27
lines changed

15 files changed

+461
-27
lines changed

Docs/reference/content/reference/driver/admin.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ using (var cursor = await client.ListDatabaseAsync())
6464

6565
## Collections
6666

67-
These operations exists on the [`IMongoDatabase`]({{< apiref "T_MongoDB_Driver_IMongoDatabase" >}}) interface.
67+
These operations exist on the [`IMongoDatabase`]({{< apiref "T_MongoDB_Driver_IMongoDatabase" >}}) interface.
6868

6969
### Getting a collection
7070

@@ -88,7 +88,7 @@ var options = new CreateCollectionOptions
8888
{
8989
Capped = true,
9090
MaxSize = 10000
91-
});
91+
};
9292
```
9393
```csharp
9494
// creates a capped collection named "foo" with a maximum size of 10,000 bytes
@@ -99,6 +99,19 @@ db.CreateCollection("foo", options);
9999
await db.CreateCollectionAsync("foo", options);
100100
```
101101

102+
### Creating a clustered collection
103+
104+
*New in MongoDB 5.3.* [Clustered collections](https://www.mongodb.com/docs/upcoming/core/clustered-collections/) are collections created
105+
with a clustered index. Documents in a clustered collection are ordered by the clustered index key value. To create a clustered collection:
106+
107+
```csharp
108+
var options = new CreateCollectionOptions<Product>
109+
{
110+
ClusteredIndex = new ClusteredIndexOptions<Product>()
111+
};
112+
db.CreateCollection("product", options);
113+
```
114+
102115
### Dropping a collection
103116

104117
Use the [`DropCollection`]({{< apiref "M_MongoDB_Driver_IMongoDatabase_DropCollection" >}}) or [`DropCollectionAsync`]({{< apiref "M_MongoDB_Driver_IMongoDatabase_DropCollectionAsync" >}}) methods.
@@ -272,4 +285,4 @@ using (var cursor = await collection.Indexes.ListAsync())
272285
var list = await cursor.ToListAsync();
273286
// do something with the list...
274287
}
275-
```
288+
```

src/MongoDB.Driver.Core/Core/Misc/Feature.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2016-present MongoDB Inc.
1+
/* Copyright 2016-present MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -50,6 +50,7 @@ public class Feature
5050
private static readonly Feature __changeStreamPostBatchResumeToken = new Feature("ChangeStreamPostBatchResumeToken", WireVersion.Server40);
5151
private static readonly Feature __changeStreamPrePostImages = new Feature("ChangeStreamPrePostImages", WireVersion.Server60);
5252
private static readonly Feature __clientSideEncryption = new Feature("ClientSideEncryption", WireVersion.Server42);
53+
private static readonly Feature __clusteredIndexes = new Feature("ClusteredIndexes", WireVersion.Server53);
5354
private static readonly Feature __collation = new Feature("Collation", WireVersion.Server34);
5455
private static readonly Feature __commandMessage = new Feature("CommandMessage", WireVersion.Server36);
5556
private static readonly Feature __commandsThatWriteAcceptWriteConcern = new Feature("CommandsThatWriteAcceptWriteConcern", WireVersion.Server34);
@@ -267,6 +268,12 @@ public class Feature
267268
/// </summary>
268269
public static Feature ClientSideEncryption => __clientSideEncryption;
269270

271+
272+
/// <summary>
273+
/// Gets the clustered indexes feature.
274+
/// </summary>
275+
public static Feature ClusteredIndexes => __clusteredIndexes;
276+
270277
/// <summary>
271278
/// Gets the collation feature.
272279
/// </summary>

src/MongoDB.Driver.Core/Core/Operations/CreateCollectionOperation.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ CreateCollectionOperation CreateInnerCollectionOperation(string collectionName)
6969
// fields
7070
private bool? _autoIndexId;
7171
private bool? _capped;
72+
private BsonDocument _clusteredIndex;
7273
private Collation _collation;
7374
private readonly CollectionNamespace _collectionNamespace;
7475
private BsonValue _comment;
@@ -324,6 +325,18 @@ public WriteConcern WriteConcern
324325
set { _writeConcern = value; }
325326
}
326327

328+
/// <summary>
329+
/// Gets or sets the clustered index definition.
330+
/// </summary>
331+
/// <value>
332+
/// The clustered index definition.
333+
/// </value>
334+
public BsonDocument ClusteredIndex
335+
{
336+
get => _clusteredIndex;
337+
set => _clusteredIndex = value;
338+
}
339+
327340
// methods
328341
internal BsonDocument CreateCommand(ICoreSessionHandle session)
329342
{
@@ -332,6 +345,7 @@ internal BsonDocument CreateCommand(ICoreSessionHandle session)
332345
return new BsonDocument
333346
{
334347
{ "create", _collectionNamespace.CollectionName },
348+
{ "clusteredIndex", _clusteredIndex, _clusteredIndex != null },
335349
{ "capped", () => _capped.Value, _capped.HasValue },
336350
{ "autoIndexId", () => _autoIndexId.Value, _autoIndexId.HasValue },
337351
{ "size", () => _maxSize.Value, _maxSize.HasValue },
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using MongoDB.Bson;
17+
using MongoDB.Bson.Serialization;
18+
19+
namespace MongoDB.Driver
20+
{
21+
/// <summary>
22+
/// Options for creating a clustered index.
23+
/// </summary>
24+
public class ClusteredIndexOptions<TDocument>
25+
{
26+
private IndexKeysDefinition<TDocument> _key;
27+
private string _name;
28+
private bool _unique;
29+
30+
/// <summary>
31+
/// Initializes a new instance of the <see cref="ClusteredIndexOptions{TDocument}"/> class.
32+
/// </summary>
33+
public ClusteredIndexOptions()
34+
{
35+
_key = new BsonDocument { { "_id", 1 } };
36+
_unique = true;
37+
}
38+
39+
/// <summary>
40+
/// Gets or sets the index key, which must currently be {_id: 1}.
41+
/// </summary>
42+
public IndexKeysDefinition<TDocument> Key
43+
{
44+
get => _key;
45+
set => _key = value;
46+
}
47+
48+
/// <summary>
49+
/// Gets or sets the index name.
50+
/// </summary>
51+
public string Name
52+
{
53+
get => _name;
54+
set => _name = value;
55+
}
56+
57+
/// <summary>
58+
/// Gets or sets whether the index entries must be unique, which currently must be true.
59+
/// </summary>
60+
public bool Unique
61+
{
62+
get => _unique;
63+
set => _unique = value;
64+
}
65+
66+
internal BsonDocument Render(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry)
67+
{
68+
return new BsonDocument {
69+
{ "key", _key.Render(documentSerializer, serializerRegistry) },
70+
{ "unique", _unique },
71+
{ "name", _name, _name != null }
72+
};
73+
}
74+
}
75+
}

src/MongoDB.Driver/CreateCollectionOptions.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,20 @@ internal static CreateCollectionOptions<TDocument> CoercedFrom(CreateCollectionO
237237
#endregion
238238

239239
// private fields
240+
private ClusteredIndexOptions<TDocument> _clusteredIndex;
240241
private IBsonSerializer<TDocument> _documentSerializer;
241242
private FilterDefinition<TDocument> _validator;
242243

243244
// public properties
245+
/// <summary>
246+
/// Gets or sets the <see cref="ClusteredIndexOptions{TDocument}"/>.
247+
/// </summary>
248+
public ClusteredIndexOptions<TDocument> ClusteredIndex
249+
{
250+
get { return _clusteredIndex; }
251+
set { _clusteredIndex = value; }
252+
}
253+
244254
/// <summary>
245255
/// Gets or sets the document serializer.
246256
/// </summary>

src/MongoDB.Driver/CreateIndexModel.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,7 @@
1313
* limitations under the License.
1414
*/
1515

16-
using System;
17-
using System.Collections.Generic;
18-
using System.Linq;
19-
using System.Text;
20-
using System.Threading.Tasks;
2116
using MongoDB.Driver.Core.Misc;
22-
using MongoDB.Driver.Core.Operations;
2317

2418
namespace MongoDB.Driver
2519
{

src/MongoDB.Driver/MongoDatabaseImpl.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -644,13 +644,11 @@ private Task CreateCollectionHelperAsync<TDocument>(IClientSessionHandle session
644644

645645
private IWriteOperation<BsonDocument> CreateCreateCollectionOperation<TDocument>(string name, CreateCollectionOptions<TDocument> options)
646646
{
647-
BsonDocument validator = null;
648-
if (options.Validator != null)
649-
{
650-
var serializerRegistry = options.SerializerRegistry ?? BsonSerializer.SerializerRegistry;
651-
var documentSerializer = options.DocumentSerializer ?? serializerRegistry.GetSerializer<TDocument>();
652-
validator = options.Validator.Render(documentSerializer, serializerRegistry, _linqProvider);
653-
}
647+
var serializerRegistry = options.SerializerRegistry ?? BsonSerializer.SerializerRegistry;
648+
var documentSerializer = options.DocumentSerializer ?? serializerRegistry.GetSerializer<TDocument>();
649+
650+
var clusteredIndex = options.ClusteredIndex?.Render(documentSerializer, serializerRegistry);
651+
var validator = options.Validator?.Render(documentSerializer, serializerRegistry, _linqProvider);
654652

655653
var collectionNamespace = new CollectionNamespace(_databaseNamespace, name);
656654

@@ -667,6 +665,7 @@ private IWriteOperation<BsonDocument> CreateCreateCollectionOperation<TDocument>
667665
cco.AutoIndexId = options.AutoIndexId;
668666
#pragma warning restore CS0618 // Type or member is obsolete
669667
cco.Capped = options.Capped;
668+
cco.ClusteredIndex = clusteredIndex;
670669
cco.Collation = options.Collation;
671670
cco.ExpireAfter = options.ExpireAfter;
672671
cco.IndexOptionDefaults = options.IndexOptionDefaults?.ToBsonDocument();

tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateCollectionOperationTests.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public void constructor_should_initialize_subject()
8787
subject.AutoIndexId.Should().NotHaveValue();
8888
#pragma warning restore
8989
subject.Capped.Should().NotHaveValue();
90+
subject.ClusteredIndex.Should().BeNull();
9091
subject.Collation.Should().BeNull();
9192
subject.EncryptedFields.Should().BeNull();
9293
subject.IndexOptionDefaults.Should().BeNull();
@@ -171,6 +172,28 @@ public void CreateCommand_should_return_expected_result_when_Capped_is_set(
171172
result.Should().Be(expectedResult);
172173
}
173174

175+
[Theory]
176+
[ParameterAttributeData]
177+
public void CreateCommand_should_return_expected_result_when_ClusteredIndex_is_set(
178+
[Values(null, "{ key : { _id : 1 }, unique : true }", "{ key : { _id : 1 }, unique : true, name: 'clustered index name' }")]
179+
string clusteredIndex)
180+
{
181+
var subject = new CreateCollectionOperation(_collectionNamespace, _messageEncoderSettings)
182+
{
183+
ClusteredIndex = clusteredIndex != null ? BsonDocument.Parse(clusteredIndex) : null
184+
};
185+
var session = OperationTestHelper.CreateSession();
186+
187+
var result = subject.CreateCommand(session);
188+
189+
var expectedResult = new BsonDocument
190+
{
191+
{ "create", _collectionNamespace.CollectionName },
192+
{ "clusteredIndex", () => BsonDocument.Parse(clusteredIndex), clusteredIndex != null }
193+
};
194+
result.Should().Be(expectedResult);
195+
}
196+
174197
[Theory]
175198
[ParameterAttributeData]
176199
public void CreateCommand_should_return_expected_result_when_Collation_is_set(

tests/MongoDB.Driver.TestConsoleApplication/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
using System;
1717
using System.IO;
18+
using MongoDB.Bson;
1819
using MongoDB.Driver.Core.Configuration;
1920
using MongoDB.Driver.Core.Events.Diagnostics;
2021

tests/MongoDB.Driver.Tests/MongoDatabaseImplTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
using MongoDB.Bson.TestHelpers.XunitExtensions;
2626
using MongoDB.Driver.Core.Bindings;
2727
using MongoDB.Driver.Core.Clusters;
28+
using MongoDB.Driver.Core.Misc;
2829
using MongoDB.Driver.Core.Operations;
2930
using MongoDB.Driver.Core.TestHelpers.XunitExtensions;
3031
using MongoDB.Driver.Tests;
@@ -381,8 +382,14 @@ public void AggregateToCollection_should_throw_when_last_stage_is_not_an_output_
381382
[ParameterAttributeData]
382383
public void CreateCollection_should_execute_a_CreateCollectionOperation_when_options_is_generic(
383384
[Values(false, true)] bool usingSession,
385+
[Values(false, true)] bool clustered,
384386
[Values(false, true)] bool async)
385387
{
388+
if (clustered)
389+
{
390+
RequireServer.Check().Supports(Feature.ClusteredIndexes);
391+
}
392+
386393
var writeConcern = new WriteConcern(1);
387394
var subject = _subject.WithWriteConcern(writeConcern);
388395
var session = CreateSession(usingSession);
@@ -395,6 +402,7 @@ public void CreateCollection_should_execute_a_CreateCollectionOperation_when_opt
395402
{
396403
AutoIndexId = false,
397404
Capped = true,
405+
ClusteredIndex = clustered ? new ClusteredIndexOptions<BsonDocument>() : null,
398406
Collation = new Collation("en_US"),
399407
IndexOptionDefaults = new IndexOptionDefaults { StorageEngine = new BsonDocument("x", 1) },
400408
MaxDocuments = 10,
@@ -441,6 +449,14 @@ public void CreateCollection_should_execute_a_CreateCollectionOperation_when_opt
441449
op.AutoIndexId.Should().Be(options.AutoIndexId);
442450
#pragma warning restore
443451
op.Capped.Should().Be(options.Capped);
452+
if (clustered)
453+
{
454+
op.ClusteredIndex.Should().NotBeNull();
455+
}
456+
else
457+
{
458+
op.ClusteredIndex.Should().BeNull();
459+
}
444460
op.Collation.Should().BeSameAs(options.Collation);
445461
op.IndexOptionDefaults.ToBsonDocument().Should().Be(options.IndexOptionDefaults.ToBsonDocument());
446462
op.MaxDocuments.Should().Be(options.MaxDocuments);
@@ -515,6 +531,7 @@ public void CreateCollection_should_execute_a_CreateCollectionOperation_when_opt
515531
op.AutoIndexId.Should().Be(options.AutoIndexId);
516532
#pragma warning restore
517533
op.Capped.Should().Be(options.Capped);
534+
op.ClusteredIndex.Should().BeNull();
518535
op.Collation.Should().BeSameAs(options.Collation);
519536
op.IndexOptionDefaults.ToBsonDocument().Should().Be(options.IndexOptionDefaults.ToBsonDocument());
520537
op.MaxDocuments.Should().Be(options.MaxDocuments);
@@ -571,6 +588,7 @@ public void CreateCollection_should_execute_a_CreateCollectionOperation_when_opt
571588
op.AutoIndexId.Should().NotHaveValue();
572589
#pragma warning restore
573590
op.Capped.Should().NotHaveValue();
591+
op.ClusteredIndex.Should().BeNull();
574592
op.IndexOptionDefaults.Should().BeNull();
575593
op.MaxDocuments.Should().NotHaveValue();
576594
op.MaxSize.Should().NotHaveValue();

0 commit comments

Comments
 (0)