From 7ca6748cab3fd86c6c406bc27a61cb0a69bde732 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Tue, 28 Jul 2026 18:14:19 -0400 Subject: [PATCH 01/13] CSHARP-5996: Remove unreferenced obsolete APIs Removes the obsolete public API that had no remaining references anywhere in src/ or tests/, so no call sites needed rework: - Feature: 30 obsolete server-version features plus the HintForFindAndModifyFeature property. Each was supported by every server version this driver supports, or covered a feature the server removed in 4.2. HintForFindAndModifyOperations is the live replacement for the latter. - HintForFindAndModifyFeature, SemaphoreSlimRequest, PriorityServerSelector: public types with no references. DeprioritizedServersServerSelector is the live replacement for PriorityServerSelector. - MongoConnectionException.ContainsSocketTimeoutException: superseded by ContainsTimeoutException. - CreateCollectionOptions.NoPadding and UsePowerOf2Sizes: both options were removed in server 4.2, and the properties only wrote backing fields that nothing read, so neither ever reached the wire. - ChangeStreamDocument.DisambiguatedPaths: already returned null; ChangeStreamUpdateDescription.DisambiguatedPaths carries the value. Csfle2QEv2TextPreviewAlgorithm is left in place: its StringPreview replacement only shipped in 3.10.0, so the deprecation window is still open. Removing public API is a breaking change and targets the 4.0 major release. --- src/MongoDB.Driver/Core/AGENTS.md | 5 +- .../Core/ChangeStreamDocument.cs | 32 --- .../Core/ChangeStreamUpdateDescription.cs | 2 +- .../ServerSelectors/PriorityServerSelector.cs | 58 ------ src/MongoDB.Driver/Core/Misc/Feature.cs | 187 ------------------ .../Core/Misc/HintForFindAndModifyFeature.cs | 48 ----- .../Core/Misc/SemaphoreSlimRequest.cs | 94 --------- .../Core/MongoConnectionException.cs | 20 -- src/MongoDB.Driver/CreateCollectionOptions.cs | 22 --- 9 files changed, 3 insertions(+), 465 deletions(-) delete mode 100644 src/MongoDB.Driver/Core/Clusters/ServerSelectors/PriorityServerSelector.cs delete mode 100644 src/MongoDB.Driver/Core/Misc/HintForFindAndModifyFeature.cs delete mode 100644 src/MongoDB.Driver/Core/Misc/SemaphoreSlimRequest.cs diff --git a/src/MongoDB.Driver/Core/AGENTS.md b/src/MongoDB.Driver/Core/AGENTS.md index 74e9d6ed1f4..26d5cc776d5 100644 --- a/src/MongoDB.Driver/Core/AGENTS.md +++ b/src/MongoDB.Driver/Core/AGENTS.md @@ -38,8 +38,7 @@ This file covers the **Server Discovery And Monitoring (SDAM)** topology layer, - `WritableServerSelector` — returns only writable servers (Primaries in RS, all in Standalone/Sharded). - `LatencyLimitingServerSelector` — filters servers within `LocalThreshold` of the fastest server, reducing tail latency. - `EndPointServerSelector` — filters a specific server by endpoint (used internally for pinned connections). - - `DeprioritizedServersServerSelector` — deprioritizes known bad servers. - - `PriorityServerSelector` — `[Obsolete]`. The public, legacy form of the deprioritization filter: a standalone `public sealed` selector that takes a `deprioritizedServers` collection and removes those endpoints from the candidate set on sharded clusters. The internal replacement is `DeprioritizedServersServerSelector` (an `internal sealed` *composing* selector that wraps another `IServerSelector` and filters its output). Neither is related to replica-set priorities; `PriorityServerSelector` is slated for removal in a future release. + - `DeprioritizedServersServerSelector` — deprioritizes known bad servers. An `internal sealed` *composing* selector: it wraps another `IServerSelector` and filters that selector's output, removing deprioritized endpoints on sharded clusters. Despite the name, it has nothing to do with replica-set priorities. - `OperationsCountServerSelector` — load-balances by operation count (for load-balanced deployments). - `RandomServerSelector` — random tie-breaker when multiple servers are equally good. - `DelegateServerSelector` — wraps an arbitrary `Func<…>` for ad-hoc selection logic (rarely used in production code; useful for tests and bespoke deployments). @@ -288,7 +287,7 @@ This file covers the **Server Discovery And Monitoring (SDAM)** topology layer, - **`Ensure`** — precondition checks (NotNull, IsGreaterThanZero, etc.). - **`DnsClientWrapper`** — async DNS resolver. The implementation uses the `DnsClient` NuGet package (not `System.Net.Dns`) so SRV/TXT queries work consistently across platforms. Both `IDnsResolver` and `DnsClientWrapper` are `internal`; there is no public seam for plugging in a custom resolver. The interface exists so in-assembly tests (via `InternalsVisibleTo`) can substitute a fake — production code always goes through `DnsClientWrapper` from `DnsMonitor`. Past versions of this file claimed users could substitute a custom `IDnsResolver`; do not re-introduce that claim. - **`ExceptionMapper`** — classifies wire exceptions (network error, timeout, server error) into semantic types. -- **`Feature`** — checks server version for feature availability (e.g., "does this server support transactions?"). +- **`Feature`** — checks server version for feature availability (e.g., "does this server support client bulk write?"). Only features whose support varies across the server versions this driver supports belong here; once the driver's minimum server version passes a feature's floor, the entry is removed rather than left as a tautology. - **`EnvironmentVariableProvider`** — abstraction over `Environment.GetEnvironmentVariable` for testing. --- diff --git a/src/MongoDB.Driver/Core/ChangeStreamDocument.cs b/src/MongoDB.Driver/Core/ChangeStreamDocument.cs index f160bdf9485..c8925b316b4 100644 --- a/src/MongoDB.Driver/Core/ChangeStreamDocument.cs +++ b/src/MongoDB.Driver/Core/ChangeStreamDocument.cs @@ -93,38 +93,6 @@ public ChangeStreamDocument( /// public DatabaseNamespace DatabaseNamespace => GetValue(nameof(DatabaseNamespace), null); - /// - /// Gets the disambiguated paths if present. - /// - /// - /// The disambiguated paths. - /// - /// - /// - /// A document containing a map that associates an update path to an array containing the path components used in the update document. This data - /// can be used in combination with the other fields in an to determine the - /// actual path in the document that was updated. This is necessary in cases where a key contains dot-separated strings (i.e. { "a.b": "c" }) or - /// a document contains a numeric literal string key (i.e. { "a": { "0": "a" } }). Note that in this scenario, the numeric key can't be the top - /// level key because { "0": "a" } is not ambiguous - update paths would simply be '0' which is unambiguous because BSON documents cannot have - /// arrays at the top level. Each entry in the document maps an update path to an array which contains the actual path used when the document - /// was updated. For example, given a document with the following shape { "a": { "0": 0 } } and an update of { $inc: { "a.0": 1 } }, - /// would look like the following: - /// - /// - /// { - /// "a.0": ["a", "0"] - /// } - /// - /// - /// In each array, all elements will be returned as strings with the exception of array indices, which will be returned as 32-bit integers. - /// - /// - /// Added in MongoDB version 6.1.0. - /// - /// - [Obsolete("DisambiguatedPaths is obsolete and will be removed in a future version. Use instead.")] - public BsonDocument DisambiguatedPaths => null; - /// /// Gets the document key. /// diff --git a/src/MongoDB.Driver/Core/ChangeStreamUpdateDescription.cs b/src/MongoDB.Driver/Core/ChangeStreamUpdateDescription.cs index f8b5535e807..52f53d91cc9 100644 --- a/src/MongoDB.Driver/Core/ChangeStreamUpdateDescription.cs +++ b/src/MongoDB.Driver/Core/ChangeStreamUpdateDescription.cs @@ -93,7 +93,7 @@ public ChangeStreamUpdateDescription( /// level key because { "0": "a" } is not ambiguous - update paths would simply be '0' which is unambiguous because BSON documents cannot have /// arrays at the top level. Each entry in the document maps an update path to an array which contains the actual path used when the document /// was updated. For example, given a document with the following shape { "a": { "0": 0 } } and an update of { $inc: { "a.0": 1 } }, - /// would look like the following: + /// the disambiguated paths would look like the following: /// /// /// { diff --git a/src/MongoDB.Driver/Core/Clusters/ServerSelectors/PriorityServerSelector.cs b/src/MongoDB.Driver/Core/Clusters/ServerSelectors/PriorityServerSelector.cs deleted file mode 100644 index abde5d146bc..00000000000 --- a/src/MongoDB.Driver/Core/Clusters/ServerSelectors/PriorityServerSelector.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright 2010-present MongoDB Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.Servers; - -namespace MongoDB.Driver.Core.Clusters.ServerSelectors -{ - /// - /// Represents a server selector that selects servers based on a collection of servers to deprioritize. - /// - [Obsolete("PriorityServerSelector is obsolete and will be removed in a future release.")] - public sealed class PriorityServerSelector : IServerSelector - { - private readonly IReadOnlyCollection _deprioritizedServers; - - /// - /// Initializes a new instance of the class. - /// - /// The collection of servers to deprioritize. - public PriorityServerSelector(IReadOnlyCollection deprioritizedServers) - { - _deprioritizedServers = Ensure.IsNotNullOrEmpty(deprioritizedServers, nameof(deprioritizedServers)) as IReadOnlyCollection; - } - - /// - public IEnumerable SelectServers(ClusterDescription cluster, IEnumerable servers) - { - // according to spec, we only do deprioritization in a sharded cluster. - if (cluster.Type != ClusterType.Sharded) - { - return servers; - } - - var filteredServers = servers.Where(description => _deprioritizedServers.All(d => d.EndPoint != description.EndPoint)).ToList(); - - return filteredServers.Any() ? filteredServers : servers; - } - - /// - public override string ToString() => $"PriorityServerSelector{{{{ Deprioritized servers: {string.Join(", ", _deprioritizedServers.Select(s => s.EndPoint))} }}}}"; - } -} diff --git a/src/MongoDB.Driver/Core/Misc/Feature.cs b/src/MongoDB.Driver/Core/Misc/Feature.cs index 9e00e0b83ce..c29550940ff 100644 --- a/src/MongoDB.Driver/Core/Misc/Feature.cs +++ b/src/MongoDB.Driver/Core/Misc/Feature.cs @@ -42,12 +42,6 @@ public class Feature /// public static Feature AggregateOptionsLet { get; } = new("AggregateOptionsLet", WireVersion.Server50); - /// - /// Gets the aggregate merge feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature AggregateMerge { get; } = new("AggregateMerge", WireVersion.Server42); - /// /// Gets the aggregate out on secondary feature. /// @@ -63,12 +57,6 @@ public class Feature /// public static Feature AggregateOutToDifferentDatabase { get; } = new("AggregateOutToDifferentDatabase", WireVersion.Server44); - /// - /// Gets the aggregate toString feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature AggregateToString { get; } = new("AggregateToString", WireVersion.Server40); - /// /// Gets the aggregate unionWith feature. /// @@ -84,24 +72,6 @@ public class Feature /// public static Feature BitwiseOperators { get; } = new("BitwiseOperators", WireVersion.Server63); - /// - /// Gets the change stream all changes for cluster feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature ChangeStreamAllChangesForCluster { get; } = new("ChangeStreamAllChangesForCluster", WireVersion.Server40); - - /// - /// Gets the change stream for database feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature ChangeStreamForDatabase { get; } = new("ChangeStreamForDatabase", WireVersion.Server40); - - /// - /// Gets the change stream post batch resume token feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature ChangeStreamPostBatchResumeToken { get; } = new("ChangeStreamPostBatchResumeToken", WireVersion.Server40); - /// /// Gets the change stream pre post images feature. /// @@ -117,12 +87,6 @@ public class Feature /// public static Feature ClientBulkWrite { get; } = new("ClientBulkWrite", WireVersion.Server80); - /// - /// Gets the client side encryption feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature ClientSideEncryption { get; } = new("ClientSideEncryption", WireVersion.Server42); - /// /// Gets the clustered indexes feature. /// @@ -163,12 +127,6 @@ public class Feature /// public static Feature CreateIndexCommitQuorum { get; } = new("CreateIndexCommitQuorum", WireVersion.Server44); - /// - /// Gets the create indexes using insert operations feature. - /// - [Obsolete("This feature was removed in server version 4.2. This property will be removed in the next major release.")] - public static Feature CreateIndexesUsingInsertOperations { get; } = new("CreateIndexesUsingInsertOperations", WireVersion.Zero, WireVersion.Server42); - /// /// Represents support for the $createObjectId operator feature. /// @@ -220,12 +178,6 @@ public class Feature [Obsolete("Use Csfle2QEv2StringPreviewAlgorithm instead.")] public static Feature Csfle2QEv2TextPreviewAlgorithm { get; } = new("csfle2Qev2TextPreviewAlgorithm", WireVersion.Server82); - /// - /// Gets the $dateFromString format argument feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature DateFromStringFormatArgument { get; } = new("DateFromStringFormatArgument", WireVersion.Server40); - /// /// Gets the date operators added in 5.0 feature. /// @@ -256,30 +208,6 @@ public class Feature /// public static Feature ElectionIdPriorityInSDAM { get; } = new("ElectionIdPriorityInSDAM ", WireVersion.Server60); - /// - /// Gets the eval feature. - /// - [Obsolete("This feature was removed in server version 4.2. This property will be removed in the next major release.")] - public static Feature Eval { get; } = new("Eval", WireVersion.Zero, WireVersion.Server42); - - /// - /// Gets the fail points block connection feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature FailPointsBlockConnection { get; } = new("FailPointsBlockConnection", WireVersion.Server42); - - /// - /// Gets the fail points fail command feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature FailPointsFailCommand { get; } = new("FailPointsFailCommand", WireVersion.Server40); - - /// - /// Gets the fail points fail command for sharded feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature FailPointsFailCommandForSharded { get; } = new("FailPointsFailCommandForSharded", WireVersion.Server42); - /// /// Gets filter limit feature. /// @@ -295,13 +223,6 @@ public class Feature /// public static Feature FindProjectionExpressions { get; } = new("FindProjectionExpressions", WireVersion.Server44); - /// - /// Gets the geoNear command feature. - /// - /// - [Obsolete("This feature was removed in server version 4.2. This property will be removed in the next major release.")] - public static Feature GeoNearCommand { get; } = new("GeoNearCommand", WireVersion.Zero, WireVersion.Server42); - /// /// Gets the getField feature. /// @@ -312,12 +233,6 @@ public class Feature /// public static Feature GetMoreComment { get; } = new("GetMoreComment", WireVersion.Server44); - /// - /// Gets the group command feature. - /// - [Obsolete("This feature was removed in server version 4.2. This property will be removed in the next major release.")] - public static Feature GroupCommand { get; } = new("GroupCommand", WireVersion.Zero, WireVersion.Server42); - /// /// Gets the $hash operator feature. /// @@ -338,46 +253,16 @@ public class Feature /// public static Feature HintForDeleteOperations { get; } = new("HintForDeleteOperations", WireVersion.Server44); - /// - /// Gets the hint for find and modify operations feature. - /// - [Obsolete("HintForFindAndModifyFeature is Obsolete and will be removed in the next major release. Use HintForFindAndModifyOperations instead")] - public static HintForFindAndModifyFeature HintForFindAndModifyFeature { get; } = new("HintForFindAndModify", WireVersion.Server44); - /// /// Gets the hint for find and modify operations feature. /// public static Feature HintForFindAndModifyOperations { get; } = new("HintForFindAndModifyOperations", WireVersion.Server44); - /// - /// Gets the hint for update and replace operations feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature HintForUpdateAndReplaceOperations { get; } = new("HintForUpdateAndReplaceOperations", WireVersion.Server42); - - /// - /// Gets the keep connection pool when NotPrimary connection exception feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature KeepConnectionPoolWhenNotPrimaryConnectionException { get; } = new("KeepConnectionPoolWhenNotWritablePrimaryConnectionException", WireVersion.Server42); - - /// - /// Gets the keep connection pool when replSetStepDown feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature KeepConnectionPoolWhenReplSetStepDown { get; } = new("KeepConnectionPoolWhenReplSetStepDown", WireVersion.Server42); - /// /// Gets the legacy wire protocol feature. /// public static Feature LegacyWireProtocol { get; } = new("LegacyWireProtocol", WireVersion.Zero, WireVersion.Server51); - /// - /// Get the list databases authorizedDatabases feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature ListDatabasesAuthorizedDatabases { get; } = new("ListDatabasesAuthorizedDatabases", WireVersion.Server40); - /// /// Gets the load balanced mode feature. /// @@ -393,12 +278,6 @@ public class Feature /// public static Feature LookupDocuments { get; } = new("LookupDocuments", WireVersion.Server60); - /// - /// Gets the mmapv1 storage engine feature. - /// - [Obsolete("This feature was removed in server version 4.2. This property will be removed in the next major release.")] - public static Feature MmapV1StorageEngine { get; } = new("MmapV1StorageEngine", WireVersion.Zero, WireVersion.Server42); - /// /// Gets the $median operator added in 7.0 /// @@ -424,12 +303,6 @@ public class Feature /// public static Feature RankFusionStage { get; } = new("RankFusionStage", WireVersion.Server81); - /// - /// Gets the regex match feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature RegexMatch { get; } = new("RegexMatch", WireVersion.Server42); - /// /// Gets the $replaceAll feature. /// @@ -440,23 +313,11 @@ public class Feature /// public static Feature ReplaceAllWithRegex { get; } = new("ReplaceAllWithRegex", WireVersion.Server82); - /// - /// Gets the $round feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature Round { get; } = new("Round", WireVersion.Server42); - /// /// Gets the $scoreFusion feature. /// public static Feature ScoreFusionStage { get; } = new("ScoreFusionStage", WireVersion.Server82); - /// - /// Gets the scram sha256 authentication feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature ScramSha256Authentication { get; } = new("ScramSha256Authentication", WireVersion.Server40); - /// /// Gets the server returns resumableChangeStream label feature. /// @@ -472,12 +333,6 @@ public class Feature /// public static Feature SerializeEJsonOperator { get; } = new("SerializeEJsonOperator", WireVersion.Server83); - /// - /// Gets the $set stage feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature SetStage { get; } = new("SetStage", WireVersion.Server42); - /// /// Gets the set window fields feature. /// @@ -488,12 +343,6 @@ public class Feature /// public static Feature SetWindowFieldsLocf { get; } = new("SetWindowFieldsLocf", WireVersion.Server52); - /// - /// Gets the sharded transactions feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature ShardedTransactions { get; } = new("ShardedTransactions", WireVersion.Server42); - /// /// Gets the $sigmoid operator feature. /// @@ -540,42 +389,6 @@ public class Feature /// public static Feature SubtypeOperator { get; } = new("SubtypeOperator", WireVersion.Server83); - /// - /// Gets the $toXyz conversion operators feature ($toDouble etc.). - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature ToConversionOperators { get; } = new("ToConversionOperators", WireVersion.Server40); - - /// - /// Gets the transactions feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature Transactions { get; } = new("Transactions", WireVersion.Server40); - - /// - /// Gets the trig operators feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature TrigOperators { get; } = new("TrigOperators", WireVersion.Server42); - - /// - /// Gets the trim operator feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature TrimOperator { get; } = new("TrimOperator", WireVersion.Server40); - - /// - /// Gets the update with aggregation pipeline feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature UpdateWithAggregationPipeline { get; } = new("UpdateWithAggregationPipeline", WireVersion.Server42); - - /// - /// Gets the wildcard indexes feature. - /// - [Obsolete("This feature is supported by all server versions supported by this driver. This property will be removed in the next major release.")] - public static Feature WildcardIndexes { get; } = new("WildcardIndexes", WireVersion.Server42); - #endregion private readonly int? _supportRemovedWireVersion; diff --git a/src/MongoDB.Driver/Core/Misc/HintForFindAndModifyFeature.cs b/src/MongoDB.Driver/Core/Misc/HintForFindAndModifyFeature.cs deleted file mode 100644 index 0e2d511899a..00000000000 --- a/src/MongoDB.Driver/Core/Misc/HintForFindAndModifyFeature.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright 2020-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; - -namespace MongoDB.Driver.Core.Misc -{ - /// - /// Represents the hint for find and modify feature. - /// - [Obsolete("HintForFindAndModifyFeature is obsolete and will be removed in the next major release.")] - public class HintForFindAndModifyFeature : Feature - { - private readonly int _firstWireVersionWhereWeRelyOnServerToReturnError = WireVersion.Server42; - - /// - /// Initializes a new instance of the class. - /// - /// The name of the feature. - /// The first wire version that supports the feature. - public HintForFindAndModifyFeature(string name, int firstSupportedWireVersion) - : base(name, firstSupportedWireVersion) - { - } - - /// - /// Determines whether the driver must throw an exception if the feature is not supported by the server. - /// - /// The wire version. - /// Whether the driver must throw if feature is not supported. - public bool DriverMustThrowIfNotSupported(int wireVersion) - { - return wireVersion < _firstWireVersionWhereWeRelyOnServerToReturnError; - } - } -} diff --git a/src/MongoDB.Driver/Core/Misc/SemaphoreSlimRequest.cs b/src/MongoDB.Driver/Core/Misc/SemaphoreSlimRequest.cs deleted file mode 100644 index 8c583fa72d8..00000000000 --- a/src/MongoDB.Driver/Core/Misc/SemaphoreSlimRequest.cs +++ /dev/null @@ -1,94 +0,0 @@ -/* Copyright 2015-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace MongoDB.Driver.Core.Misc -{ - /// - /// Represents a tentative request to acquire a SemaphoreSlim. - /// - [Obsolete("SemaphoreSlimRequest is deprecated and will be removed in future release")] - public sealed class SemaphoreSlimRequest : IDisposable - { - // private fields - private readonly CancellationTokenSource _disposeCancellationTokenSource; - private readonly CancellationTokenSource _linkedCancellationTokenSource; -#pragma warning disable CA2213 // Disposable fields should be disposed - private readonly SemaphoreSlim _semaphore; -#pragma warning restore CA2213 // Disposable fields should be disposed - private readonly Task _task; - - // constructors - /// - /// Initializes a new instance of the class. - /// - /// The semaphore. - /// The cancellation token. - public SemaphoreSlimRequest(SemaphoreSlim semaphore, CancellationToken cancellationToken) - : this(semaphore, Timeout.InfiniteTimeSpan, cancellationToken) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The semaphore. - /// The timeout. - /// The cancellation token. - public SemaphoreSlimRequest(SemaphoreSlim semaphore, TimeSpan timeout, CancellationToken cancellationToken) - { - _semaphore = Ensure.IsNotNull(semaphore, nameof(semaphore)); - - _disposeCancellationTokenSource = new CancellationTokenSource(); - _linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _disposeCancellationTokenSource.Token); - _task = semaphore.WaitAsync(timeout, _linkedCancellationTokenSource.Token); - } - - // public properties - /// - /// Gets the semaphore wait task. - /// - /// - /// The semaphore wait task. - /// - public Task Task => _task; - - // public methods - /// - public void Dispose() - { - _disposeCancellationTokenSource.Cancel(); // does nothing if we have the lock, otherwise cancels the request - SpinWait.SpinUntil(() => _task.IsCompleted); - - if (_task.Status == TaskStatus.RanToCompletion) - { - try - { - _semaphore.Release(); - } - catch - { - // ignore... - } - } - - _disposeCancellationTokenSource.Dispose(); - _linkedCancellationTokenSource.Dispose(); - } - } -} diff --git a/src/MongoDB.Driver/Core/MongoConnectionException.cs b/src/MongoDB.Driver/Core/MongoConnectionException.cs index ac33e7b20c5..516a1fae64e 100644 --- a/src/MongoDB.Driver/Core/MongoConnectionException.cs +++ b/src/MongoDB.Driver/Core/MongoConnectionException.cs @@ -63,26 +63,6 @@ public ConnectionId ConnectionId get { return _connectionId; } } - /// - /// Whether or not this exception contains a socket timeout exception. - /// - [Obsolete("Use ContainsTimeoutException instead.")] - public bool ContainsSocketTimeoutException - { - get - { - for (var exception = InnerException; exception != null; exception = exception.InnerException) - { - if (exception is SocketException socketException && - socketException.SocketErrorCode == SocketError.TimedOut) - { - return true; - } - } - return false; - } - } - /// /// Whether or not this exception contains a timeout exception. /// diff --git a/src/MongoDB.Driver/CreateCollectionOptions.cs b/src/MongoDB.Driver/CreateCollectionOptions.cs index a91a13cf1fa..b4f0d33d484 100644 --- a/src/MongoDB.Driver/CreateCollectionOptions.cs +++ b/src/MongoDB.Driver/CreateCollectionOptions.cs @@ -34,11 +34,9 @@ public class CreateCollectionOptions private IndexOptionDefaults _indexOptionDefaults; private long? _maxDocuments; private long? _maxSize; - private bool? _noPadding; private BsonDocument _storageEngine; private TimeSpan? _timeout; private TimeSeriesOptions _timeSeriesOptions; - private bool? _usePowerOf2Sizes; private IBsonSerializerRegistry _serializerRegistry; private DocumentValidationAction? _validationAction; private DocumentValidationLevel? _validationLevel; @@ -119,16 +117,6 @@ public long? MaxSize set { _maxSize = value; } } - /// - /// Gets or sets whether padding should not be used. - /// - [Obsolete("This option was removed in server version 4.2. As such, this property will be removed in a later release.")] - public bool? NoPadding - { - get { return _noPadding; } - set { _noPadding = value; } - } - /// /// Gets or sets the serializer registry. /// @@ -166,16 +154,6 @@ public TimeSeriesOptions TimeSeriesOptions set { _timeSeriesOptions = value; } } - /// - /// Gets or sets a value indicating whether to use power of 2 sizes. - /// - [Obsolete("This option was removed in server version 4.2. As such, this property will be removed in a later release.")] - public bool? UsePowerOf2Sizes - { - get { return _usePowerOf2Sizes; } - set { _usePowerOf2Sizes = value; } - } - /// /// Gets or sets the validation action. /// From d2ae9885a8cb06ba97ec2e47c4fab3900aa4f6fe Mon Sep 17 00:00:00 2001 From: adelinowona Date: Tue, 28 Jul 2026 19:59:08 -0400 Subject: [PATCH 02/13] CSHARP-5996: Remove obsolete GeoHaystack, CreateOne and multi-message event APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GeoHaystack index support: the GeoHaystack builders on IndexKeysDefinitionBuilder and its extensions, the internal GeoHaystackIndexKeyDefinition, and the BucketSize option on both CreateIndexOptions and CreateIndexRequest (including the "bucketSize" field it rendered into the createIndexes command). Note this one does drop working functionality rather than dead code. The driver's minimum server version is 4.4, geoHaystack was deprecated in 4.4, and the server removed it in 5.0 — so it still works against 4.4, the oldest server this driver supports, and fails on everything newer. IMongoIndexManager.CreateOne / CreateOneAsync overloads taking an IndexKeysDefinition plus CreateIndexOptions, and their MongoIndexManagerBase implementations. The CreateIndexModel overloads cover the same ground; nothing in the driver or its tests called the removed overloads. The multi-message constructors and RequestIds properties on ConnectionSendingMessagesEvent, ConnectionSendingMessagesFailedEvent and ConnectionSentMessagesEvent. Sending multiple messages per event was already gone, so these only ever wrapped a single request id. Drops the now-unused System.Collections.Generic and System.Linq imports from all three. Removing public API is a breaking change and targets the 4.0 major release. --- .../Events/ConnectionSendingMessagesEvent.cs | 26 ------ .../ConnectionSendingMessagesFailedEvent.cs | 28 ------ .../Events/ConnectionSentMessagesEvent.cs | 32 ------- .../Core/Operations/CreateIndexRequest.cs | 5 -- src/MongoDB.Driver/CreateIndexOptions.cs | 14 --- src/MongoDB.Driver/IMongoIndexManager.cs | 50 ----------- .../IndexKeysDefinitionBuilder.cs | 90 ------------------- src/MongoDB.Driver/MongoCollectionImpl.cs | 3 - src/MongoDB.Driver/MongoIndexManagerBase.cs | 36 -------- .../Operations/CreateIndexRequestTests.cs | 44 --------- .../IndexKeysDefinitionBuilderTests.cs | 23 ----- .../MongoCollectionImplTests.cs | 15 ---- 12 files changed, 366 deletions(-) diff --git a/src/MongoDB.Driver/Core/Events/ConnectionSendingMessagesEvent.cs b/src/MongoDB.Driver/Core/Events/ConnectionSendingMessagesEvent.cs index f61acad279d..9527ebc4290 100644 --- a/src/MongoDB.Driver/Core/Events/ConnectionSendingMessagesEvent.cs +++ b/src/MongoDB.Driver/Core/Events/ConnectionSendingMessagesEvent.cs @@ -14,8 +14,6 @@ */ using System; -using System.Collections.Generic; -using System.Linq; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Servers; @@ -32,21 +30,6 @@ public struct ConnectionSendingMessagesEvent : IEvent private readonly int _requestId; private readonly DateTime _timestamp; - /// - /// Initializes a new instance of the struct. - /// - /// The connection identifier. - /// The request ids. - /// The operation identifier. - [Obsolete("Support for sending multiple messages has been removed, use the constructor with single requestId instead.")] - public ConnectionSendingMessagesEvent(ConnectionId connectionId, IReadOnlyList requestIds, long? operationId) - { - _connectionId = connectionId; - _requestId = requestIds.Single(); - _operationId = operationId; - _timestamp = DateTime.UtcNow; - } - /// /// Initializes a new instance of the struct. /// @@ -85,15 +68,6 @@ public int RequestId get { return _requestId; } } - /// - /// Gets the request ids. - /// - [Obsolete($"Support for sending multiple messages has been removed, use {nameof(RequestId)} instead.")] - public IReadOnlyList RequestIds - { - get { return [_requestId]; } - } - /// /// Gets the operation identifier. /// diff --git a/src/MongoDB.Driver/Core/Events/ConnectionSendingMessagesFailedEvent.cs b/src/MongoDB.Driver/Core/Events/ConnectionSendingMessagesFailedEvent.cs index fd9f9624d01..af701ec2e0c 100644 --- a/src/MongoDB.Driver/Core/Events/ConnectionSendingMessagesFailedEvent.cs +++ b/src/MongoDB.Driver/Core/Events/ConnectionSendingMessagesFailedEvent.cs @@ -14,8 +14,6 @@ */ using System; -using System.Collections.Generic; -using System.Linq; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Servers; @@ -33,23 +31,6 @@ public struct ConnectionSendingMessagesFailedEvent : IEvent private readonly int _requestId; private readonly DateTime _timestamp; - /// - /// Initializes a new instance of the struct. - /// - /// The connection identifier. - /// The request ids. - /// The exception. - /// The operation identifier. - [Obsolete("Support for sending multiple messages has been removed, use the constructor with single requestId instead.")] - public ConnectionSendingMessagesFailedEvent(ConnectionId connectionId, IReadOnlyList requestIds, Exception exception, long? operationId) - { - _connectionId = connectionId; - _requestId = requestIds.Single(); - _exception = exception; - _operationId = operationId; - _timestamp = DateTime.UtcNow; - } - /// /// Initializes a new instance of the struct. /// @@ -106,15 +87,6 @@ public int RequestId get { return _requestId; } } - /// - /// Gets the request ids. - /// - [Obsolete($"Support for sending multiple messages has been removed, use {nameof(RequestId)} instead.")] - public IReadOnlyList RequestIds - { - get { return [_requestId]; } - } - /// /// Gets the server identifier. /// diff --git a/src/MongoDB.Driver/Core/Events/ConnectionSentMessagesEvent.cs b/src/MongoDB.Driver/Core/Events/ConnectionSentMessagesEvent.cs index 6e66e122339..a8312add5a8 100644 --- a/src/MongoDB.Driver/Core/Events/ConnectionSentMessagesEvent.cs +++ b/src/MongoDB.Driver/Core/Events/ConnectionSentMessagesEvent.cs @@ -14,8 +14,6 @@ */ using System; -using System.Collections.Generic; -using System.Linq; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Servers; @@ -35,27 +33,6 @@ public struct ConnectionSentMessagesEvent : IEvent private readonly int _requestId; private readonly DateTime _timestamp; - /// - /// Initializes a new instance of the struct. - /// - /// The connection identifier. - /// The request ids. - /// The length. - /// The duration of time spent on the network. - /// The duration of time spent serializing the messages. - /// The operation identifier. - [Obsolete("Support for sending multiple messages has been removed, use the constructor with single requestId instead.")] - public ConnectionSentMessagesEvent(ConnectionId connectionId, IReadOnlyList requestIds, int length, TimeSpan networkDuration, TimeSpan serializationDuration, long? operationId) - { - _connectionId = connectionId; - _requestId = requestIds.Single(); - _length = length; - _networkDuration = networkDuration; - _serializationDuration = serializationDuration; - _operationId = operationId; - _timestamp = DateTime.UtcNow; - } - /// /// Initializes a new instance of the struct. /// @@ -140,15 +117,6 @@ public int RequestId get { return _requestId; } } - /// - /// Gets the request ids. - /// - [Obsolete($"Support for sending multiple messages has been removed, use {nameof(RequestId)} instead.")] - public IReadOnlyList RequestIds - { - get { return [_requestId]; } - } - /// /// Gets the server identifier. /// diff --git a/src/MongoDB.Driver/Core/Operations/CreateIndexRequest.cs b/src/MongoDB.Driver/Core/Operations/CreateIndexRequest.cs index dc77c279767..b7274cffda4 100644 --- a/src/MongoDB.Driver/Core/Operations/CreateIndexRequest.cs +++ b/src/MongoDB.Driver/Core/Operations/CreateIndexRequest.cs @@ -31,8 +31,6 @@ public CreateIndexRequest(BsonDocument keys) public BsonDocument AdditionalOptions { get; set; } public bool? Background { get; set; } public int? Bits { get; set; } - [Obsolete("GeoHaystack indexes were deprecated in server version 4.4.")] - public double? BucketSize { get; set; } public Collation Collation { get; set; } public string DefaultLanguage { get; set; } public TimeSpan? ExpireAfter { get; set; } @@ -81,9 +79,6 @@ public BsonDocument CreateIndexDocument() { "name", GetIndexName() }, { "background", () => Background.Value, Background.HasValue }, { "bits", () => Bits.Value, Bits.HasValue }, -#pragma warning disable CS0618 // Type or member is obsolete - { "bucketSize", () => BucketSize.Value, BucketSize.HasValue }, -#pragma warning restore CS0618 // Type or member is obsolete { "collation", () => Collation.ToBsonDocument(), Collation != null }, { "default_language", () => DefaultLanguage, DefaultLanguage != null }, { "expireAfterSeconds", () => ExpireAfter.Value.TotalSeconds, ExpireAfter.HasValue }, diff --git a/src/MongoDB.Driver/CreateIndexOptions.cs b/src/MongoDB.Driver/CreateIndexOptions.cs index 246be0fe719..c7b72f8fd3f 100644 --- a/src/MongoDB.Driver/CreateIndexOptions.cs +++ b/src/MongoDB.Driver/CreateIndexOptions.cs @@ -26,7 +26,6 @@ public class CreateIndexOptions // fields private bool? _background; private int? _bits; - private double? _bucketSize; private Collation _collation; private string _defaultLanguage; private TimeSpan? _expireAfter; @@ -62,16 +61,6 @@ public int? Bits set { _bits = value; } } - /// - /// Gets or sets the size of a geohash bucket. - /// - [Obsolete("GeoHaystack indexes were deprecated in server version 4.4.")] - public double? BucketSize - { - get { return _bucketSize; } - set { _bucketSize = value; } - } - /// /// Gets or sets the collation. /// @@ -229,9 +218,6 @@ internal static CreateIndexOptions CoercedFrom(CreateIndexOptions opt { Background = options.Background, Bits = options.Bits, -#pragma warning disable 618 - BucketSize = options.BucketSize, -#pragma warning restore 618 Collation = options.Collation, DefaultLanguage = options.DefaultLanguage, ExpireAfter = options.ExpireAfter, diff --git a/src/MongoDB.Driver/IMongoIndexManager.cs b/src/MongoDB.Driver/IMongoIndexManager.cs index 3321723f226..7aa63eb7a0f 100644 --- a/src/MongoDB.Driver/IMongoIndexManager.cs +++ b/src/MongoDB.Driver/IMongoIndexManager.cs @@ -163,31 +163,6 @@ string CreateOne( CreateOneIndexOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - /// - /// Creates an index. - /// - /// The keys. - /// The create index request options. - /// The cancellation token. - /// - /// The name of the index that was created. - /// - [Obsolete("Use CreateOne with a CreateIndexModel instead.")] - string CreateOne(IndexKeysDefinition keys, CreateIndexOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Creates an index. - /// - /// The session. - /// The keys. - /// The create index request options. - /// The cancellation token. - /// - /// The name of the index that was created. - /// - [Obsolete("Use CreateOne with a CreateIndexModel instead.")] - string CreateOne(IClientSessionHandle session, IndexKeysDefinition keys, CreateIndexOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - /// /// Creates an index. /// @@ -219,31 +194,6 @@ Task CreateOneAsync( CreateOneIndexOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - /// - /// Creates an index. - /// - /// The keys. - /// The create index request options. - /// The cancellation token. - /// - /// A task whose result is the name of the index that was created. - /// - [Obsolete("Use CreateOneAsync with a CreateIndexModel instead.")] - Task CreateOneAsync(IndexKeysDefinition keys, CreateIndexOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Creates an index. - /// - /// The session. - /// The keys. - /// The create index request options. - /// The cancellation token. - /// - /// A task whose result is the name of the index that was created. - /// - [Obsolete("Use CreateOneAsyc with a CreateIndexModel instead.")] - Task CreateOneAsync(IClientSessionHandle session, IndexKeysDefinition keys, CreateIndexOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - /// /// Creates an index. /// diff --git a/src/MongoDB.Driver/IndexKeysDefinitionBuilder.cs b/src/MongoDB.Driver/IndexKeysDefinitionBuilder.cs index 1b42c938411..2d1b50d375a 100644 --- a/src/MongoDB.Driver/IndexKeysDefinitionBuilder.cs +++ b/src/MongoDB.Driver/IndexKeysDefinitionBuilder.cs @@ -117,40 +117,6 @@ public static IndexKeysDefinition Geo2D(this IndexKeysDefi return builder.Combine(keys, builder.Geo2D(field)); } - /// - /// Combines an existing index keys definition with a geo haystack index key definition. - /// - /// The type of the document. - /// The keys. - /// The field. - /// Name of the additional field. - /// - /// A combined index keys definition. - /// - [Obsolete("GeoHaystack indexes were deprecated in server version 4.4.")] - public static IndexKeysDefinition GeoHaystack(this IndexKeysDefinition keys, FieldDefinition field, FieldDefinition additionalFieldName = null) - { - var builder = Builders.IndexKeys; - return builder.Combine(keys, builder.GeoHaystack(field, additionalFieldName)); - } - - /// - /// Combines an existing index keys definition with a geo haystack index key definition. - /// - /// The type of the document. - /// The keys. - /// The field. - /// Name of the additional field. - /// - /// A combined index keys definition. - /// - [Obsolete("GeoHaystack indexes were deprecated in server version 4.4.")] - public static IndexKeysDefinition GeoHaystack(this IndexKeysDefinition keys, Expression> field, Expression> additionalFieldName = null) - { - var builder = Builders.IndexKeys; - return builder.Combine(keys, builder.GeoHaystack(field, additionalFieldName)); - } - /// /// Combines an existing index keys definition with a 2dsphere index key definition. /// @@ -328,35 +294,6 @@ public IndexKeysDefinition Geo2D(Expression> return Geo2D(new ExpressionFieldDefinition(field)); } - /// - /// Creates a geo haystack index key definition. - /// - /// The field. - /// Name of the additional field. - /// - /// A geo haystack index key definition. - /// - [Obsolete("GeoHaystack indexes were deprecated in server version 4.4.")] - public IndexKeysDefinition GeoHaystack(FieldDefinition field, FieldDefinition additionalFieldName = null) - { - return new GeoHaystackIndexKeyDefinition(field, additionalFieldName); - } - - /// - /// Creates a geo haystack index key definition. - /// - /// The field. - /// Name of the additional field. - /// - /// A geo haystack index key definition. - /// - [Obsolete("GeoHaystack indexes were deprecated in server version 4.4.")] - public IndexKeysDefinition GeoHaystack(Expression> field, Expression> additionalFieldName = null) - { - FieldDefinition additional = additionalFieldName == null ? null : new ExpressionFieldDefinition(additionalFieldName); - return GeoHaystack(new ExpressionFieldDefinition(field), additional); - } - /// /// Creates a 2dsphere index key definition. /// @@ -491,33 +428,6 @@ public override BsonDocument Render(RenderArgs args) } } - [Obsolete("GeoHaystack indexes were deprecated in server version 4.4.")] - internal sealed class GeoHaystackIndexKeyDefinition : IndexKeysDefinition - { - private readonly FieldDefinition _field; - private readonly FieldDefinition _additionalFieldName; - - public GeoHaystackIndexKeyDefinition(FieldDefinition field, FieldDefinition additionalFieldName = null) - { - _field = Ensure.IsNotNull(field, nameof(field)); - _additionalFieldName = additionalFieldName; - } - - public override BsonDocument Render(RenderArgs args) - { - var renderedField = _field.Render(args); - - var document = new BsonDocument(renderedField.FieldName, "geoHaystack"); - if (_additionalFieldName != null) - { - var additionalRenderedField = _additionalFieldName.Render(args); - document.Add(additionalRenderedField.FieldName, 1); - } - - return document; - } - } - internal sealed class SimpleIndexKeyDefinition : IndexKeysDefinition { private readonly FieldDefinition _field; diff --git a/src/MongoDB.Driver/MongoCollectionImpl.cs b/src/MongoDB.Driver/MongoCollectionImpl.cs index efa238ae965..8cd5ceee87d 100644 --- a/src/MongoDB.Driver/MongoCollectionImpl.cs +++ b/src/MongoDB.Driver/MongoCollectionImpl.cs @@ -1672,9 +1672,6 @@ private IEnumerable CreateCreateIndexRequests(IEnumerable : IMongoIndexManager - [Obsolete("Use CreateOne with a CreateIndexModel instead.")] - public virtual string CreateOne(IndexKeysDefinition keys, CreateIndexOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - var model = new CreateIndexModel(keys, options); - var result = CreateMany(new[] { model }, cancellationToken); - return result.Single(); - } - /// public virtual string CreateOne( CreateIndexModel model, @@ -60,15 +51,6 @@ public virtual string CreateOne( return result.Single(); } - /// - [Obsolete("Use CreateOne with a CreateIndexModel instead.")] - public virtual string CreateOne(IClientSessionHandle session, IndexKeysDefinition keys, CreateIndexOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - var model = new CreateIndexModel(keys, options); - var result = CreateMany(session, new[] { model }, cancellationToken); - return result.Single(); - } - /// public virtual string CreateOne( IClientSessionHandle session, @@ -81,15 +63,6 @@ public virtual string CreateOne( return result.Single(); } - /// - [Obsolete("Use CreateOneAsync with a CreateIndexModel instead.")] - public virtual async Task CreateOneAsync(IndexKeysDefinition keys, CreateIndexOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - var model = new CreateIndexModel(keys, options); - var result = await CreateManyAsync(new[] { model }, cancellationToken).ConfigureAwait(false); - return result.Single(); - } - /// public virtual async Task CreateOneAsync( CreateIndexModel model, @@ -101,15 +74,6 @@ public virtual async Task CreateOneAsync( return result.Single(); } - /// - [Obsolete("Use CreateOneAsync with a CreateIndexModel instead.")] - public virtual async Task CreateOneAsync(IClientSessionHandle session, IndexKeysDefinition keys, CreateIndexOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - var model = new CreateIndexModel(keys, options); - var result = await CreateManyAsync(session, new[] { model }, cancellationToken).ConfigureAwait(false); - return result.Single(); - } - /// public virtual async Task CreateOneAsync( IClientSessionHandle session, diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/CreateIndexRequestTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/CreateIndexRequestTests.cs index b320220608a..826f0d90a84 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/CreateIndexRequestTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/CreateIndexRequestTests.cs @@ -67,22 +67,6 @@ public void Bits_get_and_set_should_work( result.Should().Be(value); } - [Theory] - [ParameterAttributeData] - public void BucketSize_get_and_set_should_work( - [Values(null, 1.0, 2.0)] - double? value) - { - var subject = new CreateIndexRequest(new BsonDocument("x", 1)); - -#pragma warning disable 618 - subject.BucketSize = value; - var result = subject.BucketSize; -#pragma warning restore 618 - - result.Should().Be(value); - } - [Theory] [ParameterAttributeData] public void Collation_get_and_set_should_work( @@ -110,9 +94,6 @@ public void constructor_should_initialize_subject() subject.AdditionalOptions.Should().BeNull(); subject.Background.Should().NotHaveValue(); subject.Bits.Should().NotHaveValue(); -#pragma warning disable 618 - subject.BucketSize.Should().NotHaveValue(); -#pragma warning restore 618 subject.Collation.Should().BeNull(); subject.DefaultLanguage.Should().BeNull(); subject.ExpireAfter.Should().NotHaveValue(); @@ -228,31 +209,6 @@ public void CreateIndexDocument_should_return_expected_result_when_Bits_is_set( result.Should().Be(expectedResult); } - [Theory] - [ParameterAttributeData] - public void CreateIndexDocument_should_return_expected_result_when_BucketSize_is_set( - [Values(null, 1.0, 2.0)] - double? bucketSize) - { - var keys = new BsonDocument("x", 1); - var subject = new CreateIndexRequest(keys) - { -#pragma warning disable 618 - BucketSize = bucketSize -#pragma warning restore 618 - }; - - var result = subject.CreateIndexDocument(); - - var expectedResult = new BsonDocument - { - { "key", keys }, - { "name", "x_1" }, - { "bucketSize", () => bucketSize.Value, bucketSize.HasValue } - }; - result.Should().Be(expectedResult); - } - [Theory] [ParameterAttributeData] public void CreateIndexDocument_should_return_expected_result_when_Collation_is_set( diff --git a/tests/MongoDB.Driver.Tests/IndexKeysDefinitionBuilderTests.cs b/tests/MongoDB.Driver.Tests/IndexKeysDefinitionBuilderTests.cs index f63b22592d1..2be6a9940f2 100644 --- a/tests/MongoDB.Driver.Tests/IndexKeysDefinitionBuilderTests.cs +++ b/tests/MongoDB.Driver.Tests/IndexKeysDefinitionBuilderTests.cs @@ -132,29 +132,6 @@ public void Geo2D_Typed() Assert(subject.Geo2D("FirstName"), "{fn: '2d'}"); } - [Fact] - public void GeoHaystack() - { - var subject = CreateSubject(); - -#pragma warning disable 618 - Assert(subject.GeoHaystack("a"), "{a: 'geoHaystack'}"); - Assert(subject.GeoHaystack("a", "b"), "{a: 'geoHaystack', b: 1 }"); -#pragma warning restore 618 - } - - [Fact] - public void GeoHaystack_Typed() - { - var subject = CreateSubject(); - -#pragma warning disable 618 - Assert(subject.GeoHaystack(x => x.FirstName), "{fn: 'geoHaystack'}"); - Assert(subject.GeoHaystack(x => x.FirstName, x => x.LastName), "{fn: 'geoHaystack', ln: 1}"); - Assert(subject.GeoHaystack("FirstName", "LastName"), "{fn: 'geoHaystack', ln: 1}"); -#pragma warning restore 618 - } - [Fact] public void Geo2DSphere() { diff --git a/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs b/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs index 24ee6fb8487..2cf34897e4d 100644 --- a/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs @@ -2100,9 +2100,6 @@ public void Indexes_CreateOne_should_execute_a_CreateIndexesOperation( { Background = true, Bits = 10, -#pragma warning disable 618 - BucketSize = 20, -#pragma warning restore 618 Collation = new Collation("en_US"), DefaultLanguage = "en", ExpireAfter = TimeSpan.FromSeconds(20), @@ -2166,9 +2163,6 @@ public void Indexes_CreateOne_should_execute_a_CreateIndexesOperation( request.AdditionalOptions.Should().BeNull(); request.Background.Should().Be(options.Background); request.Bits.Should().Be(options.Bits); -#pragma warning disable 618 - request.BucketSize.Should().Be(options.BucketSize); -#pragma warning restore 618 request.Collation.Should().BeSameAs(options.Collation); request.DefaultLanguage.Should().Be(options.DefaultLanguage); request.ExpireAfter.Should().Be(options.ExpireAfter); @@ -2236,9 +2230,6 @@ public void Indexes_CreateMany_should_execute_a_CreateIndexesOperation( { Background = true, Bits = 10, -#pragma warning disable 618 - BucketSize = 20, -#pragma warning restore 618 Collation = new Collation("en_US"), DefaultLanguage = "en", ExpireAfter = TimeSpan.FromSeconds(20), @@ -2303,9 +2294,6 @@ public void Indexes_CreateMany_should_execute_a_CreateIndexesOperation( request1.AdditionalOptions.Should().BeNull(); request1.Background.Should().Be(options.Background); request1.Bits.Should().Be(options.Bits); -#pragma warning disable 618 - request1.BucketSize.Should().Be(options.BucketSize); -#pragma warning restore 618 request1.Collation.Should().BeSameAs(options.Collation); request1.DefaultLanguage.Should().Be(options.DefaultLanguage); request1.ExpireAfter.Should().Be(options.ExpireAfter); @@ -2343,9 +2331,6 @@ public void Indexes_CreateMany_should_execute_a_CreateIndexesOperation( request2.AdditionalOptions.Should().BeNull(); request2.Background.Should().NotHaveValue(); request2.Bits.Should().NotHaveValue(); -#pragma warning disable 618 - request2.BucketSize.Should().NotHaveValue(); -#pragma warning restore 618 request2.Collation.Should().BeNull(); request2.DefaultLanguage.Should().BeNull(); request2.ExpireAfter.Should().NotHaveValue(); From 37c74dbbb8da8558161864dbfbc4ff8c95e2786e Mon Sep 17 00:00:00 2001 From: adelinowona Date: Wed, 29 Jul 2026 16:05:46 -0400 Subject: [PATCH 03/13] CSHARP-5996: Remove obsolete MapReduce support Removes the map-reduce surface in full. Aggregation pipelines are the replacement, as the obsolete messages have said since the API was deprecated. Public API: - IMongoCollection.MapReduce / MapReduceAsync (4 overloads) and their implementations in MongoCollectionBase, MongoCollectionImpl and FilteredMongoCollectionBase. - MapReduceOptions and MapReduceOutputOptions, including the Merge / Reduce / Replace factory methods and the NonAtomic, Sharded and JavaScriptMode options the server had already stopped honouring. Internals: MapReduceOperation, MapReduceOperationBase, MapReduceOutputToCollectionOperation, MapReduceOutputMode, and the three private helpers in MongoCollectionImpl that built them. Tests: the three operation test classes, JsonDrivenMapReduceTest, UnifiedMapReduceOperation, their two factory registrations, and the map-reduce cases in MongoCollectionImplTests and OfTypeMongoCollectionTests. No spec JSON referenced mapReduce, so no fixtures needed changing. Also drops the "out" special case in CommandStartedEventAsserter, which existed only to accept map-reduce's short-form output field. The switch it lived in has no default label, so a mismatch on "out" already fell through to the same assertion failure that now handles it. Removing public API is a breaking change and targets the 4.0 major release. --- .../Core/Operations/MapReduceOperation.cs | 149 ---- .../Core/Operations/MapReduceOperationBase.cs | 249 ------- .../Core/Operations/MapReduceOutputMode.cs | 27 - .../MapReduceOutputToCollectionOperation.cs | 287 -------- .../FilteredMongoCollectionBase.cs | 32 - src/MongoDB.Driver/IMongoCollection.cs | 54 -- src/MongoDB.Driver/MapReduceOptions.cs | 317 --------- src/MongoDB.Driver/MongoCollectionBase.cs | 21 - src/MongoDB.Driver/MongoCollectionImpl.cs | 185 ----- .../CommandStartedEventAsserter.cs | 15 - .../Operations/MapReduceOperationBaseTests.cs | 598 ---------------- .../Operations/MapReduceOperationTests.cs | 593 ---------------- ...pReduceOutputToCollectionOperationTests.cs | 669 ------------------ .../JsonDrivenMapReduceTest.cs | 123 ---- .../JsonDrivenTests/JsonDrivenTestFactory.cs | 1 - .../MongoCollectionImplTests.cs | 194 ----- .../OfTypeMongoCollectionTests.cs | 74 -- .../UnifiedMapReduceOperation.cs | 120 ---- .../UnifiedTestOperationFactory.cs | 1 - 19 files changed, 3709 deletions(-) delete mode 100644 src/MongoDB.Driver/Core/Operations/MapReduceOperation.cs delete mode 100644 src/MongoDB.Driver/Core/Operations/MapReduceOperationBase.cs delete mode 100644 src/MongoDB.Driver/Core/Operations/MapReduceOutputMode.cs delete mode 100644 src/MongoDB.Driver/Core/Operations/MapReduceOutputToCollectionOperation.cs delete mode 100644 src/MongoDB.Driver/MapReduceOptions.cs delete mode 100644 tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOperationBaseTests.cs delete mode 100644 tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOperationTests.cs delete mode 100644 tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOutputToCollectionOperationTests.cs delete mode 100644 tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenMapReduceTest.cs delete mode 100644 tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedMapReduceOperation.cs diff --git a/src/MongoDB.Driver/Core/Operations/MapReduceOperation.cs b/src/MongoDB.Driver/Core/Operations/MapReduceOperation.cs deleted file mode 100644 index a6b013f8924..00000000000 --- a/src/MongoDB.Driver/Core/Operations/MapReduceOperation.cs +++ /dev/null @@ -1,149 +0,0 @@ -/* Copyright 2010-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Threading.Tasks; -using MongoDB.Bson; -using MongoDB.Bson.Serialization; -using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Driver.Core.Bindings; -using MongoDB.Driver.Core.Connections; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; - -namespace MongoDB.Driver.Core.Operations -{ - /// - /// Represents a map-reduce operation. - /// - /// The type of the result. - [Obsolete("Use Aggregation pipeline instead.")] - internal sealed class MapReduceOperation : MapReduceOperationBase, IReadOperation> - { - // fields - private ReadConcern _readConcern = ReadConcern.Default; - private readonly IBsonSerializer _resultSerializer; - - // constructors - /// - /// Initializes a new instance of the class. - /// - /// The collection namespace. - /// The map function. - /// The reduce function. - /// The result serializer. - /// The message encoder settings. - public MapReduceOperation(CollectionNamespace collectionNamespace, BsonJavaScript mapFunction, BsonJavaScript reduceFunction, IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings) - : base( - collectionNamespace, - mapFunction, - reduceFunction, - messageEncoderSettings) - { - _resultSerializer = Ensure.IsNotNull(resultSerializer, nameof(resultSerializer)); - } - - // properties - /// - /// Gets or sets the read concern. - /// - /// - /// The read concern. - /// - public ReadConcern ReadConcern - { - get { return _readConcern; } - set { _readConcern = Ensure.IsNotNull(value, nameof(value)); } - } - - /// - /// Gets the result serializer. - /// - /// - /// The result serializer. - /// - public IBsonSerializer ResultSerializer - { - get { return _resultSerializer; } - } - - /// - /// Gets the name of the operation. - /// - public string OperationName => "mapReduce"; - - // methods - /// - protected override BsonDocument CreateOutputOptions() - { - return new BsonDocument("inline", 1); - } - - /// - public IAsyncCursor Execute(OperationContext operationContext, IReadBinding binding) - { - Ensure.IsNotNull(binding, nameof(binding)); - - using (var channelSource = binding.GetReadChannelSource(operationContext)) - using (var channel = channelSource.GetChannel(operationContext)) - using (var channelBinding = new ChannelReadBinding(channelSource.Server, channel, binding.ReadPreference)) - { - var operation = CreateOperation(operationContext, channel.ConnectionDescription); - var result = operation.Execute(operationContext, channelBinding); - return new SingleBatchAsyncCursor(result); - } - } - - /// - public async Task> ExecuteAsync(OperationContext operationContext, IReadBinding binding) - { - Ensure.IsNotNull(binding, nameof(binding)); - - using (var channelSource = await binding.GetReadChannelSourceAsync(operationContext).ConfigureAwait(false)) - using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) - using (var channelBinding = new ChannelReadBinding(channelSource.Server, channel, binding.ReadPreference)) - { - var operation = CreateOperation(operationContext, channel.ConnectionDescription); - var result = await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); - return new SingleBatchAsyncCursor(result); - } - } - - /// - protected internal override BsonDocument CreateCommand(OperationContext operationContext, ConnectionDescription connectionDescription, long? transactionNumber = null) - { - var command = base.CreateCommand(operationContext, connectionDescription); - - var readConcern = ReadConcernHelper.GetReadConcernForCommand(operationContext.Session, connectionDescription, _readConcern); - if (readConcern != null) - { - command.Add("readConcern", readConcern); - } - - return command; - } - - private ReadCommandOperation CreateOperation(OperationContext operationContext, ConnectionDescription connectionDescription) - { - var command = CreateCommand(operationContext, connectionDescription); - var resultArraySerializer = new ArraySerializer(_resultSerializer); - var resultSerializer = new ElementDeserializer("results", resultArraySerializer); - return new ReadCommandOperation(CollectionNamespace.DatabaseNamespace, command, resultSerializer, MessageEncoderSettings, OperationName) - { - RetryRequested = false, - }; - } - } -} diff --git a/src/MongoDB.Driver/Core/Operations/MapReduceOperationBase.cs b/src/MongoDB.Driver/Core/Operations/MapReduceOperationBase.cs deleted file mode 100644 index 5fcdb9d9826..00000000000 --- a/src/MongoDB.Driver/Core/Operations/MapReduceOperationBase.cs +++ /dev/null @@ -1,249 +0,0 @@ -/* Copyright 2010-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using MongoDB.Bson; -using MongoDB.Driver.Core.Connections; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; - -namespace MongoDB.Driver.Core.Operations -{ - /// - /// Represents a base class for map-reduce operations. - /// - [Obsolete("Use Aggregation pipeline instead.")] - internal abstract class MapReduceOperationBase - { - // fields - private Collation _collation; - private readonly CollectionNamespace _collectionNamespace; - private BsonDocument _filter; - private BsonJavaScript _finalizeFunction; - private bool? _javaScriptMode; - private long? _limit; - private readonly BsonJavaScript _mapFunction; - private TimeSpan? _maxTime; - private readonly MessageEncoderSettings _messageEncoderSettings; - private readonly BsonJavaScript _reduceFunction; - private BsonDocument _scope; - private BsonDocument _sort; - private bool? _verbose; - - // constructors - /// - /// Initializes a new instance of the class. - /// - /// The collection namespace. - /// The map function. - /// The reduce function. - /// The message encoder settings. - protected MapReduceOperationBase(CollectionNamespace collectionNamespace, BsonJavaScript mapFunction, BsonJavaScript reduceFunction, MessageEncoderSettings messageEncoderSettings) - { - _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); - _mapFunction = Ensure.IsNotNull(mapFunction, nameof(mapFunction)); - _reduceFunction = Ensure.IsNotNull(reduceFunction, nameof(reduceFunction)); - _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); - } - - // properties - /// - /// Gets or sets the collation. - /// - /// - /// The collation. - /// - public Collation Collation - { - get { return _collation; } - set { _collation = value; } - } - - /// - /// Gets the collection namespace. - /// - /// - /// The collection namespace. - /// - public CollectionNamespace CollectionNamespace - { - get { return _collectionNamespace; } - } - - /// - /// Gets or sets the filter. - /// - /// - /// The filter. - /// - public BsonDocument Filter - { - get { return _filter; } - set { _filter = value; } - } - - /// - /// Gets or sets the finalize function. - /// - /// - /// The finalize function. - /// - public BsonJavaScript FinalizeFunction - { - get { return _finalizeFunction; } - set { _finalizeFunction = value; } - } - - /// - /// Gets or sets a value indicating whether objects emitted by the map function remain as JavaScript objects. - /// - /// - /// - /// Setting this value to true can result in faster execution, but requires more memory on the server, and if - /// there are too many emitted objects the map-reduce operation may fail. - /// - /// true if objects emitted by the map function remain as JavaScript objects; otherwise, false. - /// - [Obsolete("JavaScriptMode is ignored by server versions 4.4.0 and newer.")] - public bool? JavaScriptMode - { - get { return _javaScriptMode; } - set { _javaScriptMode = value; } - } - - /// - /// Gets or sets the maximum number of documents to pass to the map function. - /// - /// - /// The maximum number of documents to pass to the map function. - /// - public long? Limit - { - get { return _limit; } - set { _limit = value; } - } - - /// - /// Gets the map function. - /// - /// - /// The map function. - /// - public BsonJavaScript MapFunction - { - get { return _mapFunction; } - } - - /// - /// Gets or sets the maximum time the server should spend on this operation. - /// - /// - /// The maximum time the server should spend on this operation. - /// - public TimeSpan? MaxTime - { - get { return _maxTime; } - set { _maxTime = Ensure.IsNullOrInfiniteOrGreaterThanOrEqualToZero(value, nameof(value)); } - } - - /// - /// Gets the message encoder settings. - /// - /// - /// The message encoder settings. - /// - public MessageEncoderSettings MessageEncoderSettings - { - get { return _messageEncoderSettings; } - } - - /// - /// Gets the reduce function. - /// - /// - /// The reduce function. - /// - public BsonJavaScript ReduceFunction - { - get { return _reduceFunction; } - } - - /// - /// Gets or sets the scope document. - /// - /// - /// The scode document defines global variables that are accessible from the map, reduce and finalize functions. - /// - /// - /// The scope document. - /// - public BsonDocument Scope - { - get { return _scope; } - set { _scope = value; } - } - - /// - /// Gets or sets the sort specification. - /// - /// - /// The sort specification. - /// - public BsonDocument Sort - { - get { return _sort; } - set { _sort = value; } - } - - /// - /// Gets or sets a value indicating whether to include extra information, such as timing, in the result. - /// - /// - /// true if extra information, such as timing, should be included in the result; otherwise, false. - /// - public bool? Verbose - { - get { return _verbose; } - set { _verbose = value; } - } - - // methods - protected internal virtual BsonDocument CreateCommand(OperationContext operationContext, ConnectionDescription connectionDescription, long? transactionNumber = null) - { - return new BsonDocument - { - { "mapReduce", _collectionNamespace.CollectionName }, - { "map", _mapFunction }, - { "reduce", _reduceFunction }, - { "out" , CreateOutputOptions() }, - { "query", _filter, _filter != null }, - { "sort", _sort, _sort != null }, - { "limit", () => _limit.Value, _limit.HasValue }, - { "finalize", _finalizeFunction, _finalizeFunction != null }, - { "scope", _scope, _scope != null }, - { "jsMode", () => _javaScriptMode.Value, _javaScriptMode.HasValue }, - { "verbose", () => _verbose.Value, _verbose.HasValue }, - { "maxTimeMS", () => MaxTimeHelper.ToMaxTimeMS(_maxTime.Value), _maxTime.HasValue && !operationContext.IsRootContextTimeoutConfigured() }, - { "collation", () => _collation.ToBsonDocument(), _collation != null } - }; - } - - /// - /// Creates the output options. - /// - /// The output options. - protected abstract BsonDocument CreateOutputOptions(); - } -} diff --git a/src/MongoDB.Driver/Core/Operations/MapReduceOutputMode.cs b/src/MongoDB.Driver/Core/Operations/MapReduceOutputMode.cs deleted file mode 100644 index d41781aeeb5..00000000000 --- a/src/MongoDB.Driver/Core/Operations/MapReduceOutputMode.cs +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright 2010-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; - -namespace MongoDB.Driver.Core.Operations -{ - [Obsolete("Use Aggregation pipeline instead.")] - internal enum MapReduceOutputMode - { - Replace = 0, - Merge, - Reduce - } -} diff --git a/src/MongoDB.Driver/Core/Operations/MapReduceOutputToCollectionOperation.cs b/src/MongoDB.Driver/Core/Operations/MapReduceOutputToCollectionOperation.cs deleted file mode 100644 index 92654b46412..00000000000 --- a/src/MongoDB.Driver/Core/Operations/MapReduceOutputToCollectionOperation.cs +++ /dev/null @@ -1,287 +0,0 @@ -/* Copyright 2010-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Threading.Tasks; -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Driver.Core.Bindings; -using MongoDB.Driver.Core.Connections; -using MongoDB.Driver.Core.Events; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; - -namespace MongoDB.Driver.Core.Operations -{ - /// - /// Represents a map-reduce operation that outputs its results to a collection. - /// - [Obsolete("Use Aggregation pipeline instead.")] - internal sealed class MapReduceOutputToCollectionOperation : MapReduceOperationBase, IWriteOperation, IRetryableWriteOperation - { - // fields - private bool? _bypassDocumentValidation; - private bool _enableOverloadRetargeting; - private int _maxAdaptiveRetries; - private bool _retryRequested; - private bool? _nonAtomicOutput; - private readonly CollectionNamespace _outputCollectionNamespace; - private MapReduceOutputMode _outputMode; - private bool? _shardedOutput; - private WriteConcern _writeConcern; - - // constructors - /// - /// Initializes a new instance of the class. - /// - /// The collection namespace. - /// The output collection namespace. - /// The map function. - /// The reduce function. - /// The message encoder settings. - public MapReduceOutputToCollectionOperation( - CollectionNamespace collectionNamespace, - CollectionNamespace outputCollectionNamespace, - BsonJavaScript mapFunction, - BsonJavaScript reduceFunction, - MessageEncoderSettings messageEncoderSettings) - : base( - collectionNamespace, - mapFunction, - reduceFunction, - messageEncoderSettings) - { - _outputCollectionNamespace = Ensure.IsNotNull(outputCollectionNamespace, nameof(outputCollectionNamespace)); - _outputMode = MapReduceOutputMode.Replace; - } - - // properties - /// - /// Gets or sets a value indicating whether to bypass document validation. - /// - /// - /// A value indicating whether to bypass document validation. - /// - public bool? BypassDocumentValidation - { - get { return _bypassDocumentValidation; } - set { _bypassDocumentValidation = value; } - } - - /// - /// Gets or sets a value indicating whether overload retargeting is enabled. - /// - public bool EnableOverloadRetargeting - { - get { return _enableOverloadRetargeting; } - set { _enableOverloadRetargeting = value; } - } - - /// - /// Gets a value indicating whether the operation is retryable. - /// - public bool IsOperationRetryable => false; - - /// - /// Gets or sets the maximum number of adaptive retries. - /// - public int MaxAdaptiveRetries - { - get { return _maxAdaptiveRetries; } - set { _maxAdaptiveRetries = value; } - } - - /// - /// Gets or sets a value indicating whether a retry was requested. - /// - /// - /// A value indicating whether a retry was requested. - /// - public bool RetryRequested - { - get { return _retryRequested; } - set { _retryRequested = value; } - } - - /// - /// Gets or sets a value indicating whether the server should not lock the database for merge and reduce output modes. - /// - /// - /// true if the server should not lock the database for merge and reduce output modes; otherwise, false. - /// - [Obsolete("NonAtomicOutput is rejected by server versions 4.4.0 and newer.")] - public bool? NonAtomicOutput - { - get { return _nonAtomicOutput; } - set { _nonAtomicOutput = value; } - } - - /// - /// Gets the name of the operation. - /// - public string OperationName => "mapReduce"; - - /// - /// Gets the output collection namespace. - /// - /// - /// The output collection namespace. - /// - public CollectionNamespace OutputCollectionNamespace - { - get { return _outputCollectionNamespace; } - } - - /// - /// Gets or sets the output mode. - /// - /// - /// The output mode. - /// - public MapReduceOutputMode OutputMode - { - get { return _outputMode; } - set { _outputMode = value; } - } - - /// - /// Gets or sets a value indicating whether the output collection should be sharded. - /// - /// - /// true if the output collection should be sharded; otherwise, false. - /// - [Obsolete("ShardedOutput is rejected by server versions 4.4.0 and newer.")] - public bool? ShardedOutput - { - get { return _shardedOutput; } - set { _shardedOutput = value; } - } - - /// - /// Gets or sets the write concern. - /// - /// - /// The write concern. - /// - public WriteConcern WriteConcern - { - get { return _writeConcern; } - set { _writeConcern = value; } - } - - // methods - /// - protected internal override BsonDocument CreateCommand(OperationContext operationContext, ConnectionDescription connectionDescription, long? transactionNumber = null) - { - var command = base.CreateCommand(operationContext, connectionDescription, transactionNumber); - - if (_bypassDocumentValidation.HasValue) - { - command.Add("bypassDocumentValidation", _bypassDocumentValidation.Value); - } - var writeConcern = WriteConcernHelper.GetEffectiveWriteConcern(operationContext, _writeConcern); - if (writeConcern != null) - { - command.Add("writeConcern", writeConcern.ToBsonDocument()); - } - return command; - } - - /// - protected override BsonDocument CreateOutputOptions() - { - var action = _outputMode.ToString().ToLowerInvariant(); - return new BsonDocument - { - { action, _outputCollectionNamespace.CollectionName }, - { "db", _outputCollectionNamespace.DatabaseNamespace.DatabaseName }, - { "sharded", () => _shardedOutput.Value, _shardedOutput.HasValue }, - { "nonAtomic", () => _nonAtomicOutput.Value, _nonAtomicOutput.HasValue } - }; - } - - /// - public BsonDocument Execute(OperationContext operationContext, IWriteBinding binding) - { - using (BeginOperation()) - { - return RetryableWriteOperationExecutor.Execute(operationContext, this, binding, retryRequested: RetryRequested, _maxAdaptiveRetries, _enableOverloadRetargeting); - } - } - - /// - public Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) - { - using (BeginOperation()) - { - return RetryableWriteOperationExecutor.ExecuteAsync(operationContext, this, binding, retryRequested: RetryRequested, _maxAdaptiveRetries, _enableOverloadRetargeting); - } - } - - /// - public BsonDocument Execute(OperationContext operationContext, RetryableWriteContext context) - { - using (BeginOperation()) - { - return RetryableWriteOperationExecutor.Execute(operationContext, this, context); - } - } - - /// - public Task ExecuteAsync(OperationContext operationContext, RetryableWriteContext context) - { - using (BeginOperation()) - { - return RetryableWriteOperationExecutor.ExecuteAsync(operationContext, this, context); - } - } - - /// - public BsonDocument ExecuteAttempt(OperationContext operationContext, RetryableWriteContext context, int attempt, long? transactionNumber) - { - var binding = context.Binding; - var channelSource = context.ChannelSource; - var channel = context.Channel; - - using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel)) - { - var operation = CreateOperation(operationContext, channel.ConnectionDescription, transactionNumber); - return operation.Execute(operationContext, channelBinding); - } - } - - /// - public async Task ExecuteAttemptAsync(OperationContext operationContext, RetryableWriteContext context, int attempt, long? transactionNumber) - { - var binding = context.Binding; - var channelSource = context.ChannelSource; - var channel = context.Channel; - - using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel)) - { - var operation = CreateOperation(operationContext, channel.ConnectionDescription, transactionNumber); - return await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); - } - } - - private IDisposable BeginOperation() => EventContext.BeginOperation("mapReduce"); - - private WriteCommandOperation CreateOperation(OperationContext operationContext, ConnectionDescription connectionDescription, long? transactionNumber) - { - var command = CreateCommand(operationContext, connectionDescription, transactionNumber); - return new WriteCommandOperation(CollectionNamespace.DatabaseNamespace, command, BsonDocumentSerializer.Instance, MessageEncoderSettings, OperationName); - } - } -} diff --git a/src/MongoDB.Driver/FilteredMongoCollectionBase.cs b/src/MongoDB.Driver/FilteredMongoCollectionBase.cs index 0fb4fede975..7a27fd8e40d 100644 --- a/src/MongoDB.Driver/FilteredMongoCollectionBase.cs +++ b/src/MongoDB.Driver/FilteredMongoCollectionBase.cs @@ -322,38 +322,6 @@ public override Task> DistinctManyAsync(IClientSessio return _wrappedCollection.FindOneAndUpdateAsync(session, CombineFilters(filter), AdjustUpdateDefinition(update, options?.IsUpsert ?? false), options, cancellationToken); } - [Obsolete("Use Aggregation pipeline instead.")] - public override IAsyncCursor MapReduce(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - options = options ?? new MapReduceOptions(); - options.Filter = CombineFilters(options.Filter); - return _wrappedCollection.MapReduce(map, reduce, options, cancellationToken); - } - - [Obsolete("Use Aggregation pipeline instead.")] - public override IAsyncCursor MapReduce(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - options = options ?? new MapReduceOptions(); - options.Filter = CombineFilters(options.Filter); - return _wrappedCollection.MapReduce(session, map, reduce, options, cancellationToken); - } - - [Obsolete("Use Aggregation pipeline instead.")] - public override Task> MapReduceAsync(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - options = options ?? new MapReduceOptions(); - options.Filter = CombineFilters(options.Filter); - return _wrappedCollection.MapReduceAsync(map, reduce, options, cancellationToken); - } - - [Obsolete("Use Aggregation pipeline instead.")] - public override Task> MapReduceAsync(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - options = options ?? new MapReduceOptions(); - options.Filter = CombineFilters(options.Filter); - return _wrappedCollection.MapReduceAsync(session, map, reduce, options, cancellationToken); - } - // private methods private FilterDefinition CombineFilters(FilterDefinition filter) { diff --git a/src/MongoDB.Driver/IMongoCollection.cs b/src/MongoDB.Driver/IMongoCollection.cs index 5d44271f46d..91ea55bd760 100644 --- a/src/MongoDB.Driver/IMongoCollection.cs +++ b/src/MongoDB.Driver/IMongoCollection.cs @@ -908,60 +908,6 @@ public interface IMongoCollection // TODO: derive from IMongoCollecti /// Task InsertManyAsync(IClientSessionHandle session, IEnumerable documents, InsertManyOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - /// - /// Executes a map-reduce command. - /// - /// The type of the result. - /// The map function. - /// The reduce function. - /// The options. - /// The cancellation token. - /// A cursor. - [Obsolete("Use Aggregation pipeline instead.")] - IAsyncCursor MapReduce(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Executes a map-reduce command. - /// - /// The type of the result. - /// The session. - /// The map function. - /// The reduce function. - /// The options. - /// The cancellation token. - /// - /// A cursor. - /// - [Obsolete("Use Aggregation pipeline instead.")] - IAsyncCursor MapReduce(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Executes a map-reduce command. - /// - /// The type of the result. - /// The map function. - /// The reduce function. - /// The options. - /// The cancellation token. - /// A Task whose result is a cursor. - [Obsolete("Use Aggregation pipeline instead.")] - Task> MapReduceAsync(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Executes a map-reduce command. - /// - /// The type of the result. - /// The session. - /// The map function. - /// The reduce function. - /// The options. - /// The cancellation token. - /// - /// A Task whose result is a cursor. - /// - [Obsolete("Use Aggregation pipeline instead.")] - Task> MapReduceAsync(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - /// /// Returns a filtered collection that appears to contain only documents of the derived type. /// All operations using this filtered collection will automatically use discriminators as necessary. diff --git a/src/MongoDB.Driver/MapReduceOptions.cs b/src/MongoDB.Driver/MapReduceOptions.cs deleted file mode 100644 index 5b268609eea..00000000000 --- a/src/MongoDB.Driver/MapReduceOptions.cs +++ /dev/null @@ -1,317 +0,0 @@ -/* Copyright 2010-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using MongoDB.Bson; -using MongoDB.Bson.Serialization; -using MongoDB.Driver.Core.Misc; - -namespace MongoDB.Driver -{ - /// - /// Represents the options for a map-reduce operation. - /// - /// The type of the document. - /// The type of the result. - [Obsolete("Use Aggregation pipeline instead.")] - public sealed class MapReduceOptions - { - // fields - private bool? _bypassDocumentValidation; - private Collation _collation; - private FilterDefinition _filter; - private BsonJavaScript _finalize; - private bool? _javaScriptMode; - private long? _limit; - private TimeSpan? _maxTime; - private MapReduceOutputOptions _outputOptions; - private IBsonSerializer _resultSerializer; - private BsonDocument _scope; - private SortDefinition _sort; - private TimeSpan? _timeout; - private bool? _verbose; - - // properties - /// - /// Gets or sets a value indicating whether to bypass document validation. - /// - public bool? BypassDocumentValidation - { - get { return _bypassDocumentValidation; } - set { _bypassDocumentValidation = value; } - } - - /// - /// Gets or sets the collation. - /// - public Collation Collation - { - get { return _collation; } - set { _collation = value; } - } - - /// - /// Gets or sets the filter. - /// - public FilterDefinition Filter - { - get { return _filter; } - set { _filter = value; } - } - - /// - /// Gets or sets the finalize function. - /// - public BsonJavaScript Finalize - { - get { return _finalize; } - set { _finalize = value; } - } - - /// - /// Gets or sets the java script mode. - /// - [Obsolete("JavaScriptMode is ignored by server versions 4.4.0 and newer.")] - public bool? JavaScriptMode - { - get { return _javaScriptMode; } - set { _javaScriptMode = value; } - } - - /// - /// Gets or sets the limit. - /// - public long? Limit - { - get { return _limit; } - set { _limit = value; } - } - - /// - /// Gets or sets the maximum time. - /// - public TimeSpan? MaxTime - { - get { return _maxTime; } - set { _maxTime = Ensure.IsNullOrInfiniteOrGreaterThanOrEqualToZero(value, nameof(value)); } - } - - /// - /// Gets or sets the output options. - /// - public MapReduceOutputOptions OutputOptions - { - get { return _outputOptions; } - set { _outputOptions = value; } - } - - /// - /// Gets or sets the result serializer. - /// - public IBsonSerializer ResultSerializer - { - get { return _resultSerializer; } - set { _resultSerializer = value; } - } - - /// - /// Gets or sets the scope. - /// - public BsonDocument Scope - { - get { return _scope; } - set { _scope = value; } - } - - /// - /// Gets or sets the sort. - /// - public SortDefinition Sort - { - get { return _sort; } - set { _sort = value; } - } - - /// - /// Gets or sets the operation timeout. - /// - // TODO: CSOT: Make it public when CSOT will be ready for GA - internal TimeSpan? Timeout - { - get => _timeout; - set => _timeout = Ensure.IsNullOrValidTimeout(value, nameof(Timeout)); - } - - /// - /// Gets or sets whether to include timing information. - /// - public bool? Verbose - { - get { return _verbose; } - set { _verbose = value; } - } - } - - /// - /// Represents the output options for a map-reduce operation. - /// - [Obsolete("Use Aggregation pipeline instead.")] - public abstract class MapReduceOutputOptions - { - private static MapReduceOutputOptions __inline = new InlineOutput(); - - private MapReduceOutputOptions() - { } - - /// - /// An inline map-reduce output options. - /// - public static MapReduceOutputOptions Inline - { - get { return __inline; } - } - - /// - /// A merge map-reduce output options. - /// - /// The name of the collection. - /// The name of the database. - /// Whether the output collection should be sharded. - /// Whether the server should not lock the database for the duration of the merge. - /// A merge map-reduce output options. - [Obsolete("Use an overload of Merge that does not have sharded and nonAtomic parameters instead.")] - public static MapReduceOutputOptions Merge(string collectionName, string databaseName = null, bool? sharded = null, bool? nonAtomic = null) - { - Ensure.IsNotNull(collectionName, nameof(collectionName)); - return new CollectionOutput(collectionName, Core.Operations.MapReduceOutputMode.Merge, databaseName, sharded, nonAtomic); - } - - /// - /// A merge map-reduce output options. - /// - /// The name of the collection. - /// The name of the database. - /// A merge map-reduce output options. - public static MapReduceOutputOptions Merge(string collectionName, string databaseName) - { - Ensure.IsNotNull(collectionName, nameof(collectionName)); - return new CollectionOutput(collectionName, Core.Operations.MapReduceOutputMode.Merge, databaseName); - } - - /// - /// A reduce map-reduce output options. - /// - /// The name of the collection. - /// The name of the database. - /// Whether the output collection should be sharded. - /// Whether the server should not lock the database for the duration of the reduce. - /// A reduce map-reduce output options. - [Obsolete("Use an overload of Reduce that does not have sharded and nonAtomic parameters instead.")] - public static MapReduceOutputOptions Reduce(string collectionName, string databaseName = null, bool? sharded = null, bool? nonAtomic = null) - { - Ensure.IsNotNull(collectionName, nameof(collectionName)); - return new CollectionOutput(collectionName, Core.Operations.MapReduceOutputMode.Reduce, databaseName, sharded, nonAtomic); - } - - /// - /// A reduce map-reduce output options. - /// - /// The name of the collection. - /// The name of the database. - /// A reduce map-reduce output options. - public static MapReduceOutputOptions Reduce(string collectionName, string databaseName) - { - Ensure.IsNotNull(collectionName, nameof(collectionName)); - return new CollectionOutput(collectionName, Core.Operations.MapReduceOutputMode.Reduce, databaseName); - } - - /// - /// A replace map-reduce output options. - /// - /// The name of the collection. - /// Name of the database. - /// Whether the output collection should be sharded. - /// A replace map-reduce output options. - [Obsolete("Use an overload of Replace that does not have a sharded parameter instead.")] - public static MapReduceOutputOptions Replace(string collectionName, string databaseName = null, bool? sharded = null) - { - Ensure.IsNotNull(collectionName, nameof(collectionName)); - return new CollectionOutput(collectionName, Core.Operations.MapReduceOutputMode.Replace, databaseName, sharded, null); - } - - /// - /// A replace map-reduce output options. - /// - /// The name of the collection. - /// Name of the database. - /// A replace map-reduce output options. - public static MapReduceOutputOptions Replace(string collectionName, string databaseName) - { - Ensure.IsNotNull(collectionName, nameof(collectionName)); - return new CollectionOutput(collectionName, Core.Operations.MapReduceOutputMode.Replace, databaseName); - } - - internal sealed class InlineOutput : MapReduceOutputOptions - { - internal InlineOutput() - { } - } - - internal sealed class CollectionOutput : MapReduceOutputOptions - { - private readonly string _collectionName; - private readonly string _databaseName; - private readonly bool? _nonAtomic; - private readonly Core.Operations.MapReduceOutputMode _outputMode; - private readonly bool? _sharded; - - internal CollectionOutput(string collectionName, Core.Operations.MapReduceOutputMode outputMode, string databaseName = null, bool? sharded = null, bool? nonAtomic = null) - { - _collectionName = collectionName; - _outputMode = outputMode; - _databaseName = databaseName; - _sharded = sharded; - _nonAtomic = nonAtomic; - } - - public string CollectionName - { - get { return _collectionName; } - } - - public string DatabaseName - { - get { return _databaseName; } - } - - [Obsolete("NonAtomic is rejected by server versions 4.4.0 and newer.")] - public bool? NonAtomic - { - get { return _nonAtomic; } - } - - public Core.Operations.MapReduceOutputMode OutputMode - { - get { return _outputMode; } - } - - [Obsolete("Sharded is rejected by server versions 4.4.0 and newer.")] - public bool? Sharded - { - get { return _sharded; } - } - } - } -} diff --git a/src/MongoDB.Driver/MongoCollectionBase.cs b/src/MongoDB.Driver/MongoCollectionBase.cs index 8e3fa3e5c59..36e595ebad9 100644 --- a/src/MongoDB.Driver/MongoCollectionBase.cs +++ b/src/MongoDB.Driver/MongoCollectionBase.cs @@ -535,27 +535,6 @@ private async Task InsertManyAsync(IEnumerable docu return InsertManyResult.FromBulkWriteResult(bulkWriteResult, DocumentSerializer); } - [Obsolete("Use Aggregation pipeline instead.")] - public virtual IAsyncCursor MapReduce(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - throw new NotImplementedException(); - } - - [Obsolete("Use Aggregation pipeline instead.")] - public virtual IAsyncCursor MapReduce(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - throw new NotImplementedException(); - } - - [Obsolete("Use Aggregation pipeline instead.")] - public abstract Task> MapReduceAsync(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - - [Obsolete("Use Aggregation pipeline instead.")] - public virtual Task> MapReduceAsync(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - throw new NotImplementedException(); - } - public abstract IFilteredMongoCollection OfType() where TDerivedDocument : TDocument; public virtual ReplaceOneResult ReplaceOne(FilterDefinition filter, TDocument replacement, ReplaceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) diff --git a/src/MongoDB.Driver/MongoCollectionImpl.cs b/src/MongoDB.Driver/MongoCollectionImpl.cs index 8cd5ceee87d..5bc6a874207 100644 --- a/src/MongoDB.Driver/MongoCollectionImpl.cs +++ b/src/MongoDB.Driver/MongoCollectionImpl.cs @@ -525,70 +525,6 @@ public override Task FindOneAndUpdateAsync(IClientSess return ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken); } - [Obsolete("Use Aggregation pipeline instead.")] - public override IAsyncCursor MapReduce(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default) - { - using var session = _operationExecutor.StartImplicitSession(); - return MapReduce(session, map, reduce, options, cancellationToken: cancellationToken); - } - - [Obsolete("Use Aggregation pipeline instead.")] - public override IAsyncCursor MapReduce(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default) - { - Ensure.IsNotNull(session, nameof(session)); - Ensure.IsNotNull(map, nameof(map)); - Ensure.IsNotNull(reduce, nameof(reduce)); - options ??= new MapReduceOptions(); - - var outputOptions = options.OutputOptions ?? MapReduceOutputOptions.Inline; - var resultSerializer = ResolveResultSerializer(options.ResultSerializer); - - var renderArgs = GetRenderArgs(); - if (outputOptions == MapReduceOutputOptions.Inline) - { - var operation = CreateMapReduceOperation(map, reduce, options, resultSerializer, renderArgs); - return ExecuteReadOperation(session, operation, options.Timeout, cancellationToken); - } - else - { - var mapReduceOperation = CreateMapReduceOutputToCollectionOperation(map, reduce, options, outputOptions, renderArgs); - ExecuteWriteOperation(session, mapReduceOperation, options.Timeout, cancellationToken); - return CreateMapReduceOutputToCollectionResultCursor(session, options, mapReduceOperation.OutputCollectionNamespace, resultSerializer); - } - } - - [Obsolete("Use Aggregation pipeline instead.")] - public override async Task> MapReduceAsync(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default) - { - using var session = _operationExecutor.StartImplicitSession(); - return await MapReduceAsync(session, map, reduce, options, cancellationToken).ConfigureAwait(false); - } - - [Obsolete("Use Aggregation pipeline instead.")] - public override async Task> MapReduceAsync(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default) - { - Ensure.IsNotNull(session, nameof(session)); - Ensure.IsNotNull(map, nameof(map)); - Ensure.IsNotNull(reduce, nameof(reduce)); - options ??= new MapReduceOptions(); - - var outputOptions = options.OutputOptions ?? MapReduceOutputOptions.Inline; - var resultSerializer = ResolveResultSerializer(options.ResultSerializer); - - var renderArgs = GetRenderArgs(); - if (outputOptions == MapReduceOutputOptions.Inline) - { - var operation = CreateMapReduceOperation(map, reduce, options, resultSerializer, renderArgs); - return await ExecuteReadOperationAsync(session, operation, options.Timeout, cancellationToken).ConfigureAwait(false); - } - else - { - var mapReduceOperation = CreateMapReduceOutputToCollectionOperation(map, reduce, options, outputOptions, renderArgs); - await ExecuteWriteOperationAsync(session, mapReduceOperation, options.Timeout, cancellationToken).ConfigureAwait(false); - return CreateMapReduceOutputToCollectionResultCursor(session, options, mapReduceOperation.OutputCollectionNamespace, resultSerializer); - } - } - public override IFilteredMongoCollection OfType() { var derivedDocumentSerializer = _settings.SerializationDomain.LookupSerializer(); @@ -1152,112 +1088,6 @@ private FindOperation CreateFindOperation( }; } -#pragma warning disable CS0618 // Type or member is obsolete - private MapReduceOperation CreateMapReduceOperation( - BsonJavaScript map, - BsonJavaScript reduce, - MapReduceOptions options, - IBsonSerializer resultSerializer, - RenderArgs renderArgs) - { - return new MapReduceOperation( -#pragma warning restore CS0618 // Type or member is obsolete - _collectionNamespace, - map, - reduce, - resultSerializer, - _messageEncoderSettings) - { - Collation = options.Collation, - Filter = options.Filter?.Render(renderArgs), - FinalizeFunction = options.Finalize, -#pragma warning disable 618 - JavaScriptMode = options.JavaScriptMode, -#pragma warning restore 618 - Limit = options.Limit, - MaxTime = options.MaxTime, - ReadConcern = _settings.ReadConcern, - Scope = options.Scope, - Sort = options.Sort?.Render(renderArgs), - Verbose = options.Verbose - }; - } - -#pragma warning disable CS0618 // Type or member is obsolete - private MapReduceOutputToCollectionOperation CreateMapReduceOutputToCollectionOperation( - BsonJavaScript map, - BsonJavaScript reduce, - MapReduceOptions options, - MapReduceOutputOptions outputOptions, - RenderArgs renderArgs) - { - var collectionOutputOptions = (MapReduceOutputOptions.CollectionOutput)outputOptions; - var databaseNamespace = collectionOutputOptions.DatabaseName == null ? - _collectionNamespace.DatabaseNamespace : - new DatabaseNamespace(collectionOutputOptions.DatabaseName); - var outputCollectionNamespace = new CollectionNamespace(databaseNamespace, collectionOutputOptions.CollectionName); - - return new MapReduceOutputToCollectionOperation( -#pragma warning restore CS0618 // Type or member is obsolete - _collectionNamespace, - outputCollectionNamespace, - map, - reduce, - _messageEncoderSettings) - { - BypassDocumentValidation = options.BypassDocumentValidation, - Collation = options.Collation, - EnableOverloadRetargeting = _database.Client.Settings.EnableOverloadRetargeting, - Filter = options.Filter?.Render(renderArgs), - FinalizeFunction = options.Finalize, -#pragma warning disable 618 - JavaScriptMode = options.JavaScriptMode, -#pragma warning restore 618 - Limit = options.Limit, - MaxAdaptiveRetries = _database.Client.Settings.MaxAdaptiveRetries, - MaxTime = options.MaxTime, -#pragma warning disable 618 - NonAtomicOutput = collectionOutputOptions.NonAtomic, -#pragma warning restore 618 - OutputMode = collectionOutputOptions.OutputMode, - RetryRequested = _database.Client.Settings.RetryWrites, - Scope = options.Scope, -#pragma warning disable 618 - ShardedOutput = collectionOutputOptions.Sharded, -#pragma warning restore 618 - Sort = options.Sort?.Render(renderArgs), - Verbose = options.Verbose, - WriteConcern = _settings.WriteConcern - }; - } - -#pragma warning disable CS0618 // Type or member is obsolete - private IAsyncCursor CreateMapReduceOutputToCollectionResultCursor(IClientSessionHandle session, MapReduceOptions options, CollectionNamespace outputCollectionNamespace, IBsonSerializer resultSerializer) -#pragma warning restore CS0618 // Type or member is obsolete - { - var findOperation = new FindOperation( - outputCollectionNamespace, - resultSerializer, - _messageEncoderSettings) - { - Collation = options.Collation, - EnableOverloadRetargeting = _database.Client.Settings.EnableOverloadRetargeting, - MaxAdaptiveRetries = _database.Client.Settings.MaxAdaptiveRetries, - MaxTime = options.MaxTime, - ReadConcern = _settings.ReadConcern, - RetryRequested = _database.Client.Settings.RetryReads - }; - - // we want to delay execution of the find because the user may - // not want to iterate the results at all... - var forkedSession = session.Fork(); - var deferredCursor = new DeferredAsyncCursor( - () => forkedSession.Dispose(), - ct => ExecuteReadOperation(forkedSession, findOperation, ReadPreference.Primary, options?.Timeout, ct), - ct => ExecuteReadOperationAsync(forkedSession, findOperation, ReadPreference.Primary, options?.Timeout, ct)); - return deferredCursor; - } - private OperationContext CreateOperationContext(IClientSessionHandle session, TimeSpan? timeout, string operationName, CancellationToken cancellationToken) { var operationContext = session.WrappedCoreSession.CurrentTransaction?.OperationContext; @@ -1412,21 +1242,6 @@ private IEnumerable RenderArrayFilters(IEnumerable ResolveResultSerializer(IBsonSerializer resultSerializer) - { - if (resultSerializer != null) - { - return resultSerializer; - } - - if (typeof(TResult) == typeof(TDocument) && _documentSerializer != null) - { - return (IBsonSerializer)_documentSerializer; - } - - return _settings.SerializationDomain.LookupSerializer(); - } - // nested types private class MongoIndexManager : MongoIndexManagerBase { diff --git a/tests/MongoDB.Driver.TestHelpers/Core/JsonDrivenTests/CommandStartedEventAsserter.cs b/tests/MongoDB.Driver.TestHelpers/Core/JsonDrivenTests/CommandStartedEventAsserter.cs index 4db40dbe90b..4be510543a5 100644 --- a/tests/MongoDB.Driver.TestHelpers/Core/JsonDrivenTests/CommandStartedEventAsserter.cs +++ b/tests/MongoDB.Driver.TestHelpers/Core/JsonDrivenTests/CommandStartedEventAsserter.cs @@ -157,21 +157,6 @@ private void AssertCommandAspect(BsonDocument actualCommand, string name, BsonVa { switch (name) { - case "out": - if (commandName == "mapReduce") - { - if (expectedValue is BsonString && - actualValue.IsBsonDocument && - actualValue.AsBsonDocument.Contains("replace") && - actualValue["replace"] == expectedValue.AsString) - { - // allow short form for "out" to be equivalent to the long form - // Assumes that the driver is correctly generating the following - // fields: db, sharded, nonAtomic - return; - } - } - break; case "encryptedFields": if (commandName == "create") // create encrypted collection { diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOperationBaseTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOperationBaseTests.cs deleted file mode 100644 index 2b99708eee4..00000000000 --- a/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOperationBaseTests.cs +++ /dev/null @@ -1,598 +0,0 @@ -/* Copyright 2010-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using FluentAssertions; -using MongoDB.Bson; -using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; -using MongoDB.TestHelpers.XunitExtensions; -using Xunit; - -namespace MongoDB.Driver.Core.Operations -{ - public class MapReduceOperationBaseTests : OperationTestBase - { - // fields - private readonly BsonJavaScript _mapFunction = "map"; - private readonly BsonJavaScript _reduceFunction = "reduce"; - - // test methods - [Theory] - [ParameterAttributeData] - public void Collation_should_get_and_set_value( - [Values(null, "en_US", "fr_CA")] - string locale) - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); - var value = locale == null ? null : new Collation(locale); - - subject.Collation = value; - var result = subject.Collation; - - result.Should().BeSameAs(value); - } - - [Theory] - [ParameterAttributeData] - public void CollectionNamespace_should_get_value( - [Values("a", "b")] - string collectionName) - { - var collectionNamespace = new CollectionNamespace(_collectionNamespace.DatabaseNamespace, collectionName); - var subject = new FakeMapReduceOperation(collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); - - var result = subject.CollectionNamespace; - - result.Should().BeSameAs(collectionNamespace); - } - - [Fact] - public void constructor_should_initialize_instance() - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); - - subject.CollectionNamespace.Should().BeSameAs(_collectionNamespace); - subject.MapFunction.Should().BeSameAs(_mapFunction); - subject.ReduceFunction.Should().BeSameAs(_reduceFunction); - subject.MessageEncoderSettings.Should().BeSameAs(_messageEncoderSettings); - - subject.Collation.Should().BeNull(); - subject.Filter.Should().BeNull(); - subject.FinalizeFunction.Should().BeNull(); -#pragma warning disable 618 - subject.JavaScriptMode.Should().NotHaveValue(); -#pragma warning restore 618 - subject.Limit.Should().NotHaveValue(); - subject.MaxTime.Should().NotHaveValue(); - subject.Scope.Should().BeNull(); - subject.Sort.Should().BeNull(); - subject.Verbose.Should().NotHaveValue(); - } - - [Fact] - public void constructor_should_throw_when_collectionNamespace_is_null() - { - var exception = Record.Exception(() => new FakeMapReduceOperation(null, _mapFunction, _reduceFunction, _messageEncoderSettings)); - - var argumentNullException = exception.Should().BeOfType().Subject; - argumentNullException.ParamName.Should().Be("collectionNamespace"); - } - - [Fact] - public void constructor_should_throw_when_mapFunction_is_null() - { - var exception = Record.Exception(() => new FakeMapReduceOperation(_collectionNamespace, null, _reduceFunction, _messageEncoderSettings)); - - var argumentNullException = exception.Should().BeOfType().Subject; - argumentNullException.ParamName.Should().Be("mapFunction"); - } - - [Fact] - public void constructor_should_throw_when_messageEncoderSettings_is_null() - { - var exception = Record.Exception(() => new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, null)); - - var argumentNullException = exception.Should().BeOfType().Subject; - argumentNullException.ParamName.Should().Be("messageEncoderSettings"); - } - - [Fact] - public void constructor_should_throw_when_reduceFunction_is_null() - { - var exception = Record.Exception(() => new FakeMapReduceOperation(_collectionNamespace, _mapFunction, null, _messageEncoderSettings)); - - var argumentNullException = exception.Should().BeOfType().Subject; - argumentNullException.ParamName.Should().Be("reduceFunction"); - } - - [Fact] - public void CreateCommand_should_return_the_expected_result() - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); - var connectionDescription = OperationTestHelper.CreateConnectionDescription(); - using var session = OperationTestHelper.CreateSession(); - using var operationContext = new OperationContext(session); - - var result = subject.CreateCommand(operationContext, connectionDescription); - - var expectedResult = new BsonDocument - { - { "mapReduce", _collectionNamespace.CollectionName }, - { "map", _mapFunction }, - { "reduce", _reduceFunction }, - { "out", new BsonDocument("fake", 1) } - }; - result.Should().Be(expectedResult); - } - - [Theory] - [ParameterAttributeData] - public void CreateCommand_should_return_the_expected_result_when_Collation_is_provided( - [Values(null, "en_US", "fr_CA")] - string locale) - { - var collation = locale == null ? null : new Collation(locale); - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) - { - Collation = collation - }; - var connectionDescription = OperationTestHelper.CreateConnectionDescription(); - using var session = OperationTestHelper.CreateSession(); - using var operationContext = new OperationContext(session); - - var result = subject.CreateCommand(operationContext, connectionDescription); - - var expectedResult = new BsonDocument - { - { "mapReduce", _collectionNamespace.CollectionName }, - { "map", _mapFunction }, - { "reduce", _reduceFunction }, - { "out", new BsonDocument("fake", 1) }, - { "collation", () => collation.ToBsonDocument(), collation != null } - }; - result.Should().Be(expectedResult); - } - - [Theory] - [ParameterAttributeData] - public void CreateCommand_should_return_the_expected_result_when_Filter_is_provided( - [Values(null, "{ x : 1 }", "{ x : 2 }")] - string filterString) - { - var filter = filterString == null ? null : BsonDocument.Parse(filterString); - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) - { - Filter = filter - }; - var connectionDescription = OperationTestHelper.CreateConnectionDescription(); - using var session = OperationTestHelper.CreateSession(); - using var operationContext = new OperationContext(session); - - var result = subject.CreateCommand(operationContext, connectionDescription); - - var expectedResult = new BsonDocument - { - { "mapReduce", _collectionNamespace.CollectionName }, - { "map", _mapFunction }, - { "reduce", _reduceFunction }, - { "out", new BsonDocument("fake", 1) }, - { "query", filter, filter != null } - }; - result.Should().Be(expectedResult); - } - - [Theory] - [ParameterAttributeData] - public void CreateCommand_should_return_the_expected_result_when_FinalizeFunction_is_provided( - [Values(null, "a", "b")] - string code) - { - var finalizeFunction = code == null ? null : new BsonJavaScript(code); - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) - { - FinalizeFunction = finalizeFunction - }; - var connectionDescription = OperationTestHelper.CreateConnectionDescription(); - using var session = OperationTestHelper.CreateSession(); - using var operationContext = new OperationContext(session); - - var result = subject.CreateCommand(operationContext, connectionDescription); - - var expectedResult = new BsonDocument - { - { "mapReduce", _collectionNamespace.CollectionName }, - { "map", _mapFunction }, - { "reduce", _reduceFunction }, - { "out", new BsonDocument("fake", 1) }, - { "finalize", finalizeFunction, finalizeFunction != null } - }; - result.Should().Be(expectedResult); - } - - [Theory] - [ParameterAttributeData] - public void CreateCommand_should_return_the_expected_result_when_JavaScriptMode_is_provided( - [Values(null, false, true)] - bool? javaScriptMode) - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) - { -#pragma warning disable 618 - JavaScriptMode = javaScriptMode -#pragma warning restore 618 - }; - var connectionDescription = OperationTestHelper.CreateConnectionDescription(); - using var session = OperationTestHelper.CreateSession(); - using var operationContext = new OperationContext(session); - - var result = subject.CreateCommand(operationContext, connectionDescription); - - var expectedResult = new BsonDocument - { - { "mapReduce", _collectionNamespace.CollectionName }, - { "map", _mapFunction }, - { "reduce", _reduceFunction }, - { "out", new BsonDocument("fake", 1) }, - { "jsMode", () => javaScriptMode.Value, javaScriptMode.HasValue } - }; - result.Should().Be(expectedResult); - } - - [Theory] - [ParameterAttributeData] - public void CreateCommand_should_return_the_expected_result_when_Limit_is_provided( - [Values(null, 1L, 2L)] - long? limit) - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) - { - Limit = limit - }; - var connectionDescription = OperationTestHelper.CreateConnectionDescription(); - using var session = OperationTestHelper.CreateSession(); - using var operationContext = new OperationContext(session); - - var result = subject.CreateCommand(operationContext, connectionDescription); - - var expectedResult = new BsonDocument - { - { "mapReduce", _collectionNamespace.CollectionName }, - { "map", _mapFunction }, - { "reduce", _reduceFunction }, - { "out", new BsonDocument("fake", 1) }, - { "limit", () => limit.Value, limit.HasValue } - }; - result.Should().Be(expectedResult); - } - - [Theory] - [InlineData(-10000, 0)] - [InlineData(0, 0)] - [InlineData(1, 1)] - [InlineData(9999, 1)] - [InlineData(10000, 1)] - [InlineData(10001, 2)] - public void CreateCommand_should_return_expected_result_when_MaxTime_is_set(long maxTimeTicks, int expectedMaxTimeMS) - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) - { - MaxTime = TimeSpan.FromTicks(maxTimeTicks) - }; - var connectionDescription = OperationTestHelper.CreateConnectionDescription(); - using var session = OperationTestHelper.CreateSession(); - using var operationContext = new OperationContext(session); - - var result = subject.CreateCommand(operationContext, connectionDescription); - - var expectedResult = new BsonDocument - { - { "mapReduce", _collectionNamespace.CollectionName }, - { "map", _mapFunction }, - { "reduce", _reduceFunction }, - { "out", new BsonDocument("fake", 1) }, - { "maxTimeMS", expectedMaxTimeMS } - }; - result.Should().Be(expectedResult); - result["maxTimeMS"].BsonType.Should().Be(BsonType.Int32); - } - - [Theory] - [InlineData(42)] - [InlineData(-1)] - public void CreateCommand_should_ignore_maxtime_if_timeout_specified(int timeoutMs) - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) - { - MaxTime = TimeSpan.FromTicks(10) - }; - var connectionDescription = OperationTestHelper.CreateConnectionDescription(); - using var operationContext = new OperationContext(OperationTestHelper.CreateSession(), timeout: TimeSpan.FromMilliseconds(timeoutMs)); - - var result = subject.CreateCommand(operationContext, connectionDescription); - - result.Should().NotContain("maxTimeMS"); - } - - [Theory] - [ParameterAttributeData] - public void CreateCommand_should_return_the_expected_result_when_Scope_is_provided( - [Values(null, "{ x : 1 }", "{ x : 2 }")] - string scopeString) - { - var scope = scopeString == null ? null : BsonDocument.Parse(scopeString); - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) - { - Scope = scope - }; - var connectionDescription = OperationTestHelper.CreateConnectionDescription(); - using var session = OperationTestHelper.CreateSession(); - using var operationContext = new OperationContext(session); - - var result = subject.CreateCommand(operationContext, connectionDescription); - - var expectedResult = new BsonDocument - { - { "mapReduce", _collectionNamespace.CollectionName }, - { "map", _mapFunction }, - { "reduce", _reduceFunction }, - { "out", new BsonDocument("fake", 1) }, - { "scope", scope, scope != null } - }; - result.Should().Be(expectedResult); - } - - [Theory] - [ParameterAttributeData] - public void CreateCommand_should_return_the_expected_result_when_Sort_is_provided( - [Values(null, "{ x : 1 }", "{ x : -1 }")] - string sortString) - { - var sort = sortString == null ? null : BsonDocument.Parse(sortString); - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) - { - Sort = sort - }; - var connectionDescription = OperationTestHelper.CreateConnectionDescription(); - using var session = OperationTestHelper.CreateSession(); - using var operationContext = new OperationContext(session); - - var result = subject.CreateCommand(operationContext, connectionDescription); - - var expectedResult = new BsonDocument - { - { "mapReduce", _collectionNamespace.CollectionName }, - { "map", _mapFunction }, - { "reduce", _reduceFunction }, - { "out", new BsonDocument("fake", 1) }, - { "sort", sort, sort != null } - }; - result.Should().Be(expectedResult); - } - - [Theory] - [ParameterAttributeData] - public void CreateCommand_should_return_the_expected_result_when_Verbose_is_provided( - [Values(null, false, true)] - bool? verbose) - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) - { - Verbose = verbose - }; - var connectionDescription = OperationTestHelper.CreateConnectionDescription(); - using var session = OperationTestHelper.CreateSession(); - using var operationContext = new OperationContext(session); - - var result = subject.CreateCommand(operationContext, connectionDescription); - - var expectedResult = new BsonDocument - { - { "mapReduce", _collectionNamespace.CollectionName }, - { "map", _mapFunction }, - { "reduce", _reduceFunction }, - { "out", new BsonDocument("fake", 1) }, - { "verbose", () => verbose.Value, verbose.HasValue } - }; - result.Should().Be(expectedResult); - } - - [Theory] - [ParameterAttributeData] - public void Filter_should_get_and_set_value( - [Values(null, "{ x : 1 }", "{ x : 2 }")] - string valueString) - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); - var value = valueString == null ? null : BsonDocument.Parse(valueString); - - subject.Filter = value; - var result = subject.Filter; - - result.Should().BeSameAs(value); - } - - [Theory] - [ParameterAttributeData] - public void FinalizeFunction_should_get_and_set_value( - [Values(null, "a", "b")] - string code) - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); - var value = code == null ? null : new BsonJavaScript(code); - - subject.FinalizeFunction = value; - var result = subject.FinalizeFunction; - - result.Should().BeSameAs(value); - } - - [Theory] - [ParameterAttributeData] - public void JavaScriptMode_should_get_and_set_value( - [Values(null, false, true)] - bool? value) - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); - -#pragma warning disable 618 - subject.JavaScriptMode = value; - var result = subject.JavaScriptMode; -#pragma warning restore 618 - - result.Should().Be(value); - } - - [Theory] - [ParameterAttributeData] - public void Limit_should_get_and_set_value( - [Values(null, 0L, 1L)] - long? value) - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); - - subject.Limit = value; - var result = subject.Limit; - - result.Should().Be(value); - } - - [Theory] - [ParameterAttributeData] - public void MapFunction_should_get_value( - [Values("a", "b")] - string code) - { - var mapFunction = new BsonJavaScript(code); - var subject = new FakeMapReduceOperation(_collectionNamespace, mapFunction, _reduceFunction, _messageEncoderSettings); - - var result = subject.MapFunction; - - result.Should().BeSameAs(mapFunction); - } - - [Theory] - [ParameterAttributeData] - public void MaxTime_get_and_set_should_work( - [Values(-10000, 0, 1, 10000, 99999)] long maxTimeTicks) - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); - var value = TimeSpan.FromTicks(maxTimeTicks); - - subject.MaxTime = value; - var result = subject.MaxTime; - - result.Should().Be(value); - } - - [Theory] - [ParameterAttributeData] - public void MaxTime_set_should_throw_when_value_is_invalid( - [Values(-10001, -9999, -1)] long maxTimeTicks) - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); - var value = TimeSpan.FromTicks(maxTimeTicks); - - var exception = Record.Exception(() => subject.MaxTime = value); - - var e = exception.Should().BeOfType().Subject; - e.ParamName.Should().Be("value"); - } - - [Fact] - public void MessageEncoderSettings_should_get_value() - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); - - var result = subject.MessageEncoderSettings; - - result.Should().BeSameAs(_messageEncoderSettings); - } - - [Fact] - public void ReduceFunction_should_get_value() - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); - - var result = subject.ReduceFunction; - - result.Should().BeSameAs(_reduceFunction); - } - - [Theory] - [ParameterAttributeData] - public void Scope_should_get_and_set_value( - [Values(null, "{ x : 1 }", "{ x : 2 }")] - string valueString) - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); - var value = valueString == null ? null : BsonDocument.Parse(valueString); - - subject.Scope = value; - var result = subject.Scope; - - result.Should().Be(value); - } - - [Theory] - [ParameterAttributeData] - public void Sort_should_get_and_set_value( - [Values(null, "{ x : 1 }", "{ x : -1 }")] - string valueString) - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); - var value = valueString == null ? null : BsonDocument.Parse(valueString); - - subject.Sort = value; - var result = subject.Sort; - - result.Should().Be(value); - } - - [Theory] - [ParameterAttributeData] - public void Verbose_should_get_and_set_value( - [Values(null, false, true)] - bool? value) - { - var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); - - subject.Verbose = value; - var result = subject.Verbose; - - result.Should().Be(value); - } - - // nested types -#pragma warning disable CS0618 // Type or member is obsolete - private class FakeMapReduceOperation : MapReduceOperationBase -#pragma warning restore CS0618 // Type or member is obsolete - { - public FakeMapReduceOperation( - CollectionNamespace collectionNamespace, - BsonJavaScript mapFunction, - BsonJavaScript reduceFunction, - MessageEncoderSettings messageEncoderSettings - ) - : base(collectionNamespace, mapFunction, reduceFunction, messageEncoderSettings) - { - } - - protected override BsonDocument CreateOutputOptions() - { - return new BsonDocument("fake", 1); - } - } - } -} diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOperationTests.cs deleted file mode 100644 index 361507786a7..00000000000 --- a/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOperationTests.cs +++ /dev/null @@ -1,593 +0,0 @@ -/* Copyright 2010-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Reflection; -using FluentAssertions; -using MongoDB.Bson; -using MongoDB.Bson.Serialization; -using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Driver.Core.Bindings; -using MongoDB.Driver.Core.Clusters; -using MongoDB.Driver.Core.TestHelpers; -using MongoDB.Driver.Core.TestHelpers.XunitExtensions; -using MongoDB.TestHelpers.XunitExtensions; -using Xunit; - -namespace MongoDB.Driver.Core.Operations -{ - public class MapReduceOperationTests : OperationTestBase - { - // fields - private readonly BsonJavaScript _mapFunction; - private readonly BsonJavaScript _reduceFunction; - private readonly IBsonSerializer _resultSerializer; - - // constructors - public MapReduceOperationTests() - { - _mapFunction = "function() { emit(this.x, this.v); }"; - _reduceFunction = "function(key, values) { var sum = 0; for (var i = 0; i < values.length; i++) { sum += values[i]; }; return sum; }"; - _resultSerializer = BsonDocumentSerializer.Instance; - } - - // test methods - [Fact] - public void constructor_should_initialize_instance() - { -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - - subject.CollectionNamespace.Should().BeSameAs(_collectionNamespace); - subject.MapFunction.Should().BeSameAs(_mapFunction); - subject.ReduceFunction.Should().BeSameAs(_reduceFunction); - subject.ResultSerializer.Should().BeSameAs(_resultSerializer); - subject.MessageEncoderSettings.Should().BeSameAs(_messageEncoderSettings); - - subject.Collation.Should().BeNull(); - subject.Filter.Should().BeNull(); - subject.FinalizeFunction.Should().BeNull(); -#pragma warning disable 618 - subject.JavaScriptMode.Should().NotHaveValue(); -#pragma warning restore 618 - subject.Limit.Should().NotHaveValue(); - subject.MaxTime.Should().NotHaveValue(); - subject.ReadConcern.Should().BeSameAs(ReadConcern.Default); - subject.Scope.Should().BeNull(); - subject.Sort.Should().BeNull(); - subject.Verbose.Should().NotHaveValue(); - } - - [Fact] - public void constructor_should_throw_when_resultSerializer_is_null() - { -#pragma warning disable CS0618 // Type or member is obsolete - var exception = Record.Exception(() => new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, null, _messageEncoderSettings)); -#pragma warning restore CS0618 // Type or member is obsolete - - var argumentNullException = exception.Should().BeOfType().Subject; - argumentNullException.ParamName.Should().Be("resultSerializer"); - } - - [Theory] - [ParameterAttributeData] - public void ReadConcern_get_and_set_should_work( - [Values(ReadConcernLevel.Linearizable, ReadConcernLevel.Local)] - ReadConcernLevel level) - { -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - var value = new ReadConcern(level); - - subject.ReadConcern = value; - var result = subject.ReadConcern; - - result.Should().Be(value); - } - - [Fact] - public void ReadConcern_set_should_throw_when_value_is_null() - { -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - - var exception = Record.Exception(() => subject.ReadConcern = null); - - var argumentNullException = exception.Should().BeOfType().Subject; - argumentNullException.ParamName.Should().Be("value"); - } - - [Fact] - public void ResultSerializer_should_get_value() - { -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - - var result = subject.ResultSerializer; - - result.Should().BeSameAs(_resultSerializer); - } - - [Fact] - public void CreateOutputOptions_should_return_expected_result() - { -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - var subjectReflector = new Reflector(subject); - - var result = subjectReflector.CreateOutputOptions(); - - result.Should().Be("{ inline : 1 }"); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_results( - [Values(false, true)] - bool async) - { - RequireServer.Check(); - EnsureTestData(); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - - var cursor = ExecuteOperation(subject, async); - var results = ReadCursorToEnd(cursor, async); - - results.Should().BeEquivalentTo( - BsonDocument.Parse("{ _id : 1, value : 3 }"), - BsonDocument.Parse("{ _id : 2, value : 4 }")); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_results_when_Collation_is_set( - [Values(false, true)] - bool caseSensitive, - [Values(false, true)] - bool async) - { - RequireServer.Check(); - EnsureTestData(); - var collation = new Collation("en_US", caseLevel: caseSensitive, strength: CollationStrength.Primary); - var filter = BsonDocument.Parse("{ y : 'a' }"); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - Collation = collation, - Filter = filter - }; - - var cursor = ExecuteOperation(subject, async); - var results = ReadCursorToEnd(cursor, async); - - BsonDocument[] expectedResults; - if (caseSensitive) - { - expectedResults = new[] - { - BsonDocument.Parse("{ _id : 1, value : 1 }"), - BsonDocument.Parse("{ _id : 2, value : 4 }") - }; - } - else - { - expectedResults = new[] - { - BsonDocument.Parse("{ _id : 1, value : 3 }"), - BsonDocument.Parse("{ _id : 2, value : 4 }") - }; - } - results.Should().BeEquivalentTo(expectedResults); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_results_when_Filter_is_set( - [Values(false, true)] - bool async) - { - RequireServer.Check(); - EnsureTestData(); - var filter = BsonDocument.Parse("{ y : 'a' }"); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - Filter = filter - }; - - var cursor = ExecuteOperation(subject, async); - var results = ReadCursorToEnd(cursor, async); - - results.Should().BeEquivalentTo( - BsonDocument.Parse("{ _id : 1, value : 1 }"), - BsonDocument.Parse("{ _id : 2, value : 4 }")); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_results_when_FinalizeFunction_is_set( - [Values(false, true)] - bool async) - { - RequireServer.Check(); - EnsureTestData(); - var finalizeFunction = new BsonJavaScript("function(key, reduced) { return -reduced; }"); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - FinalizeFunction = finalizeFunction - }; - - var cursor = ExecuteOperation(subject, async); - var results = ReadCursorToEnd(cursor, async); - - results.Should().BeEquivalentTo( - BsonDocument.Parse("{ _id : 1, value : -3 }"), - BsonDocument.Parse("{ _id : 2, value : -4 }")); - } - - // TODO: figure out why test fails when JavaScriptMode = true (server bug?) - - //[Theory] - //[ParameterAttributeData] - //public void Execute_should_return_expected_results_when_JavaScriptMode_is_set( - // [Values(null, false, true)] - // bool? javaScriptMode, - // [Values(false, true)] - // bool async) - //{ - // RequireServer.Check(); - // EnsureTestData(); - // var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) - // { - // JavaScriptMode = javaScriptMode - // }; - - // var cursor = ExecuteOperation(subject, async); - // var results = ReadCursorToEnd(cursor, async); - - // // the results are the same either way, but at least we're smoke testing JavaScriptMode - // results.Should().Equal( - // BsonDocument.Parse("{ _id : 1, value : 3 }"), - // BsonDocument.Parse("{ _id : 2, value : 4 }")); - //} - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_results_when_Limit_is_set( - [Values(1, 2)] - long limit, - [Values(false, true)] - bool async) - { - RequireServer.Check(); - EnsureTestData(); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - Limit = limit - }; - - var cursor = ExecuteOperation(subject, async); - var results = ReadCursorToEnd(cursor, async); - - var expectedResults = new[] - { - new BsonDocument { { "_id", 1 }, { "value", limit == 1 ? 1 : 3 } } - }; - results.Should().Equal(expectedResults); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_results_when_MaxTime_is_set( - [Values(null, 1000)] - int? seconds, - [Values(false, true)] - bool async) - { - RequireServer.Check(); - EnsureTestData(); - var maxTime = seconds.HasValue ? TimeSpan.FromSeconds(seconds.Value) : (TimeSpan?)null; -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - MaxTime = maxTime - }; - - var cursor = ExecuteOperation(subject, async); - var results = ReadCursorToEnd(cursor, async); - - // results should be the same whether MaxTime was used or not - results.Should().BeEquivalentTo( - BsonDocument.Parse("{ _id : 1, value : 3 }"), - BsonDocument.Parse("{ _id : 2, value : 4 }")); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_results_when_ReadConcern_is_set( - [Values(null, ReadConcernLevel.Local)] // only use values that are valid on StandAlone servers - ReadConcernLevel? readConcernLevel, - [Values(false, true)] - bool async) - { - RequireServer.Check(); - EnsureTestData(); - var readConcern = new ReadConcern(readConcernLevel); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - ReadConcern = readConcern - }; - - // results should be the same whether ReadConcern was used or not - var cursor = ExecuteOperation(subject, async); - var results = ReadCursorToEnd(cursor, async); - - results.Should().BeEquivalentTo( - BsonDocument.Parse("{ _id : 1, value : 3 }"), - BsonDocument.Parse("{ _id : 2, value : 4 }")); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_results_when_ResultSerializer_is_set( - [Values(false, true)] - bool async) - { - RequireServer.Check(); - EnsureTestData(); - var resultSerializer = new ElementDeserializer("value", new DoubleSerializer()); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, resultSerializer, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - - var cursor = ExecuteOperation(subject, async); - var results = ReadCursorToEnd(cursor, async); - - results.Sort(); - results.Should().Equal(3, 4); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_results_when_Scope_is_set( - [Values(false, true)] - bool async) - { - RequireServer.Check(); - EnsureTestData(); - var finalizeFunction = new BsonJavaScript("function(key, reduced) { return reduced + zeroFromScope; }"); - var scope = new BsonDocument("zeroFromScope", 0); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - FinalizeFunction = finalizeFunction, - Scope = scope - }; - - var cursor = ExecuteOperation(subject, async); - var results = ReadCursorToEnd(cursor, async); - - results.Should().BeEquivalentTo( - BsonDocument.Parse("{ _id : 1, value : 3 }"), - BsonDocument.Parse("{ _id : 2, value : 4 }")); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_results_when_Sort_is_set( - [Values(1, -1)] - int direction, - [Values(false, true)] - bool async) - { - RequireServer.Check(); - EnsureTestData(); - var sort = new BsonDocument("_id", direction); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - Limit = 2, - Sort = sort - }; - - var cursor = ExecuteOperation(subject, async); - var results = ReadCursorToEnd(cursor, async); - - BsonDocument[] expectedResults; - if (direction == 1) - { - expectedResults = new[] - { - BsonDocument.Parse("{ _id : 1, value : 3 }") - }; - } - else - { - expectedResults = new[] - { - BsonDocument.Parse("{ _id : 1, value : 2 }"), - BsonDocument.Parse("{ _id : 2, value : 4 }") - }; - } - results.Should().BeEquivalentTo(expectedResults); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_throw_when_binding_is_null( - [Values(false, true)] - bool async) - { -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - using var session = OperationTestHelper.CreateSession(); - using var operationContext = new OperationContext(session); - - var exception = Record.Exception(() => ExecuteOperation(operationContext, subject, (IReadBinding)null, async)); - - var argumentNullException = exception.Should().BeOfType().Subject; - argumentNullException.ParamName.Should().Be("binding"); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_throw_when_maxTime_is_exceeded( - [Values(false, true)] bool async) - { - RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - subject.MaxTime = TimeSpan.FromSeconds(9001); - using var session = OperationTestHelper.CreateSession(); - using var operationContext = new OperationContext(session); - - using (var failPoint = FailPoint.ConfigureAlwaysOn(FailPointName.MaxTimeAlwaysTimeout)) - { - var exception = Record.Exception(() => ExecuteOperation(operationContext, subject, failPoint.Binding, async)); - - exception.Should().BeOfType(); - } - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_send_session_id_when_supported( - [Values(false, true)] bool async) - { - RequireServer.Check(); - EnsureTestData(); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - - VerifySessionIdWasSentWhenSupported(subject, "mapReduce", async); - } - - [Theory] - [ParameterAttributeData] - public void CreateCommand_should_return_expected_result_when_ReadConcern_is_set( - [Values(null, ReadConcernLevel.Linearizable, ReadConcernLevel.Local)] - ReadConcernLevel? level) - { - var readConcern = level.HasValue ? new ReadConcern(level.Value) : ReadConcern.Default; -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - ReadConcern = readConcern - }; - var connectionDescription = OperationTestHelper.CreateConnectionDescription(); - using var session = OperationTestHelper.CreateSession(); - using var operationContext = new OperationContext(session); - - var result = subject.CreateCommand(operationContext, connectionDescription); - - var expectedResult = new BsonDocument - { - { "mapReduce", _collectionNamespace.CollectionName }, - { "map", _mapFunction }, - { "reduce", _reduceFunction }, - { "out", new BsonDocument("inline", 1) }, - { "readConcern", () => readConcern.ToBsonDocument(), !readConcern.IsServerDefault } - }; - result.Should().Be(expectedResult); - } - - [Theory] - [ParameterAttributeData] - public void CreateCommand_should_return_the_expected_result_when_using_causal_consistency( - [Values(null, ReadConcernLevel.Linearizable, ReadConcernLevel.Local)] - ReadConcernLevel? level) - { - var readConcern = level.HasValue ? new ReadConcern(level.Value) : ReadConcern.Default; -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - ReadConcern = readConcern - }; - var connectionDescription = OperationTestHelper.CreateConnectionDescription(supportsSessions: true); - using var session = OperationTestHelper.CreateSession(isCausallyConsistent: true, operationTime: new BsonTimestamp(100)); - using var operationContext = new OperationContext(session); - - var result = subject.CreateCommand(operationContext, connectionDescription); - - var expectedReadConcernDocument = readConcern.ToBsonDocument(); - expectedReadConcernDocument["afterClusterTime"] = new BsonTimestamp(100); - - var expectedResult = new BsonDocument - { - { "mapReduce", _collectionNamespace.CollectionName }, - { "map", _mapFunction }, - { "reduce", _reduceFunction }, - { "out", new BsonDocument("inline", 1) }, - { "readConcern", expectedReadConcernDocument } - }; - result.Should().Be(expectedResult); - } - - // helper methods - private void EnsureTestData() - { - DropCollection(); - Insert( - new BsonDocument { { "_id", 1 }, { "x", 1 }, { "v", 1 }, { "y", "a" } }, - new BsonDocument { { "_id", 2 }, { "x", 1 }, { "v", 2 }, { "y", "A" } }, - new BsonDocument { { "_id", 3 }, { "x", 2 }, { "v", 4 }, { "y", "a" } }); - } - - // nested types - private class Reflector - { - // fields -#pragma warning disable CS0618 // Type or member is obsolete - private readonly MapReduceOperation _instance; - - // constructor - public Reflector(MapReduceOperation instance) - { - _instance = instance; - } - - // methods - public BsonDocument CreateOutputOptions() - { - var method = typeof(MapReduceOperation).GetMethod("CreateOutputOptions", BindingFlags.NonPublic | BindingFlags.Instance); - return (BsonDocument)method.Invoke(_instance, new object[0]); - } -#pragma warning restore CS0618 // Type or member is obsolete - } - } -} diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOutputToCollectionOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOutputToCollectionOperationTests.cs deleted file mode 100644 index 0ee6df9c2c1..00000000000 --- a/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOutputToCollectionOperationTests.cs +++ /dev/null @@ -1,669 +0,0 @@ -/* Copyright 2010-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Reflection; -using FluentAssertions; -using MongoDB.Bson; -using MongoDB.Driver.Core.Clusters; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.TestHelpers.XunitExtensions; -using MongoDB.TestHelpers.XunitExtensions; -using Xunit; - -namespace MongoDB.Driver.Core.Operations -{ - public class MapReduceOutputToCollectionOperationTests : OperationTestBase - { - // fields - private readonly BsonJavaScript _mapFunction; - private CollectionNamespace _outputCollectionNamespace; - private readonly BsonJavaScript _reduceFunction; - - // constructors - public MapReduceOutputToCollectionOperationTests() - { - _mapFunction = "function() { emit(this.x, this.v); }"; - _outputCollectionNamespace = new CollectionNamespace(_databaseNamespace, _collectionNamespace + "Output"); - _reduceFunction = "function(key, values) { var sum = 0; for (var i = 0; i < values.length; i++) { sum += values[i]; }; return sum; }"; - } - - // test methods - [Fact] - public void constructor_should_initialize_instance() - { -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - - subject.CollectionNamespace.Should().BeSameAs(_collectionNamespace); - subject.OutputCollectionNamespace.Should().BeSameAs(_outputCollectionNamespace); - subject.MapFunction.Should().BeSameAs(_mapFunction); - subject.ReduceFunction.Should().BeSameAs(_reduceFunction); - subject.MessageEncoderSettings.Should().BeSameAs(_messageEncoderSettings); - - subject.BypassDocumentValidation.Should().NotHaveValue(); - subject.Collation.Should().BeNull(); - subject.Filter.Should().BeNull(); - subject.FinalizeFunction.Should().BeNull(); -#pragma warning disable 618 - subject.JavaScriptMode.Should().NotHaveValue(); -#pragma warning restore 618 - subject.Limit.Should().NotHaveValue(); - subject.MaxTime.Should().NotHaveValue(); -#pragma warning disable 618 - subject.NonAtomicOutput.Should().NotHaveValue(); - subject.OutputMode.Should().Be(MapReduceOutputMode.Replace); -#pragma warning restore 618 - subject.Scope.Should().BeNull(); - subject.Sort.Should().BeNull(); - subject.Verbose.Should().NotHaveValue(); - } - - [Fact] - public void constructor_should_throw_when_outputCollectionNamespace_is_null() - { -#pragma warning disable CS0618 // Type or member is obsolete - var exception = Record.Exception(() => new MapReduceOutputToCollectionOperation(_collectionNamespace, null, _mapFunction, _reduceFunction, _messageEncoderSettings)); -#pragma warning restore CS0618 // Type or member is obsolete - - var argumentNullException = exception.Should().BeOfType().Subject; - argumentNullException.ParamName.Should().Be("outputCollectionNamespace"); - } - - [Theory] - [ParameterAttributeData] - public void BypassDocumentValidation_get_and_set_should_work( - [Values(null, false, true)] - bool? value) - { -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - - subject.BypassDocumentValidation = value; - var result = subject.BypassDocumentValidation; - - result.Should().Be(value); - } - - [Theory] - [ParameterAttributeData] - public void Filter_should_get_and_set_value( - [Values(null, "{ x : 1 }", "{ x : 2 }")] - string valueString) - { -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - var value = valueString == null ? null : BsonDocument.Parse(valueString); - - subject.Filter = value; - var result = subject.Filter; - - result.Should().Be(value); - } - - [Theory] - [ParameterAttributeData] - public void NonAtomicOutput_get_and_set_should_work( - [Values(null, false, true)] - bool? value) - { -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - -#pragma warning disable 618 - subject.NonAtomicOutput = value; - var result = subject.NonAtomicOutput; -#pragma warning restore 618 - - result.Should().Be(value); - } - - [Theory] - [ParameterAttributeData] - public void OutputCollectionNamespace_get_and_set_should_work( - [Values("a", "b")] - string collectionName) - { - var outputCollectionNamespace = new CollectionNamespace(_databaseNamespace, collectionName); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - - var result = subject.OutputCollectionNamespace; - - result.Should().BeSameAs(outputCollectionNamespace); - } - - [Theory] - [ParameterAttributeData] - public void OutputMode_get_and_set_should_work( -#pragma warning disable CS0618 // Type or member is obsolete - [Values((int)MapReduceOutputMode.Merge, (int)MapReduceOutputMode.Reduce)] - int valueInt) - { - var value = (MapReduceOutputMode)valueInt; - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); - - subject.OutputMode = value; - var result = subject.OutputMode; -#pragma warning restore CS0618 // Type or member is obsolete - - result.Should().Be(value); - } - - [Theory] - [ParameterAttributeData] - public void ShardedOutput_get_and_set_should_work( - [Values(null, false, true)] - bool? value) - { -#pragma warning disable 618 - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); - - subject.ShardedOutput = value; - var result = subject.ShardedOutput; -#pragma warning restore 618 - - result.Should().Be(value); - } - - [Theory] - [ParameterAttributeData] - public void WriteConcern_get_and_set_should_work( - [Values(null, 1, 2)] - int? w) - { -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - var value = w.HasValue ? new WriteConcern(w.Value) : null; - - subject.WriteConcern = value; - var result = subject.WriteConcern; - - result.Should().BeSameAs(value); - } - - [Theory] - [ParameterAttributeData] - public void CreateCommand_should_return_expected_result_when_BypassDocumentValidation_is_set( - [Values(null, false, true)] - bool? bypassDocumentValidation) - { -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - BypassDocumentValidation = bypassDocumentValidation - }; - var connectionDescription = OperationTestHelper.CreateConnectionDescription(); - using var session = OperationTestHelper.CreateSession(); - using var operationContext = new OperationContext(session); - - var result = subject.CreateCommand(operationContext, connectionDescription); - - var expectedResult = new BsonDocument - { - { "mapReduce", _collectionNamespace.CollectionName }, - { "map", _mapFunction }, - { "reduce", _reduceFunction }, - { "out", new BsonDocument { {"replace", _outputCollectionNamespace.CollectionName }, { "db", _databaseNamespace.DatabaseName } } }, - { "bypassDocumentValidation", () => bypassDocumentValidation.Value, bypassDocumentValidation.HasValue } - }; - result.Should().Be(expectedResult); - } - - [Theory] - [ParameterAttributeData] - public void CreateCommand_should_return_expected_result_when_WriteConcern_is_set( - [Values(null, 1, 2)] - int? w) - { - var writeConcern = w.HasValue ? new WriteConcern(w.Value) : null; -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - WriteConcern = writeConcern - }; - var connectionDescription = OperationTestHelper.CreateConnectionDescription(); - using var session = OperationTestHelper.CreateSession(); - using var operationContext = new OperationContext(session); - - var result = subject.CreateCommand(operationContext, connectionDescription); - - var expectedResult = new BsonDocument - { - { "mapReduce", _collectionNamespace.CollectionName }, - { "map", _mapFunction }, - { "reduce", _reduceFunction }, - { "out", new BsonDocument { {"replace", _outputCollectionNamespace.CollectionName }, { "db", _databaseNamespace.DatabaseName } } }, - { "writeConcern", () => writeConcern.ToBsonDocument(), writeConcern != null } - }; - result.Should().Be(expectedResult); - } - - [Fact] - public void CreateOutputOptions_should_return_expected_result() - { -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - var subjectReflector = new Reflector(subject); - - var result = subjectReflector.CreateOutputOptions(); - - var expectedResult = new BsonDocument - { - { "replace", _outputCollectionNamespace.CollectionName }, - { "db", _outputCollectionNamespace.DatabaseNamespace.DatabaseName } - }; - result.Should().Be(expectedResult); - } - - [Theory] - [ParameterAttributeData] - public void CreateOutputOptions_should_return_expected_result_when_ShardedOutput_is_set( - [Values(null, false, true)] - bool? shardedOutput) - { -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { -#pragma warning disable 618 - ShardedOutput = shardedOutput -#pragma warning restore 618 - }; - var subjectReflector = new Reflector(subject); - - var result = subjectReflector.CreateOutputOptions(); - - var expectedResult = new BsonDocument - { - { "replace", _outputCollectionNamespace.CollectionName }, - { "db", _outputCollectionNamespace.DatabaseNamespace.DatabaseName }, - { "sharded", () => shardedOutput.Value, shardedOutput.HasValue } - }; - result.Should().Be(expectedResult); - } - - [Theory] - [ParameterAttributeData] - public void CreateOutputOptions_should_return_expected_result_when_NonAtomicOutput_is_provided( - [Values(null, false, true)] - bool? nonAtomicOutput) - { -#pragma warning disable 618 - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) - { - NonAtomicOutput = nonAtomicOutput -#pragma warning restore 618 - }; - var subjectReflector = new Reflector(subject); - var expectedResult = new BsonDocument - { - { "replace", _outputCollectionNamespace.CollectionName }, - { "db", _outputCollectionNamespace.DatabaseNamespace.DatabaseName }, - { "nonAtomic", () => nonAtomicOutput.Value, nonAtomicOutput.HasValue } - }; - - var result = subjectReflector.CreateOutputOptions(); - - result.Should().Be(expectedResult); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_result( - [Values(false, true)] - bool async) - { - RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); - EnsureTestData(); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - - ExecuteOperation(subject, async); - - ReadAllFromCollection(_outputCollectionNamespace).Should().BeEquivalentTo( - BsonDocument.Parse("{ _id : 1, value : 3 }"), - BsonDocument.Parse("{ _id : 2, value : 4 }")); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_results_when_Collation_is_set( - [Values(false, true)] - bool caseSensitive, - [Values(false, true)] - bool async) - { - RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); - if (CoreTestConfiguration.ServerVersion >= SemanticVersion.Parse("v8.3.0-alpha3-105-g1227af8")) - { - DropCollection(_outputCollectionNamespace); - } - - EnsureTestData(); - var collation = new Collation("en_US", caseLevel: caseSensitive, strength: CollationStrength.Primary); - var filter = BsonDocument.Parse("{ y : 'a' }"); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - Collation = collation, - Filter = filter - }; - - ExecuteOperation(subject, async); - - BsonDocument[] expectedResults; - if (caseSensitive) - { - expectedResults = new[] - { - BsonDocument.Parse("{ _id : 1, value : 1 }"), - BsonDocument.Parse("{ _id : 2, value : 4 }") - }; - } - else - { - expectedResults = new[] - { - BsonDocument.Parse("{ _id : 1, value : 3 }"), - BsonDocument.Parse("{ _id : 2, value : 4 }") - }; - } - ReadAllFromCollection(_outputCollectionNamespace).Should().BeEquivalentTo(expectedResults); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_results_when_Filter_is_set( - [Values(false, true)] - bool async) - { - RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); - EnsureTestData(); - var filter = BsonDocument.Parse("{ y : 'a' }"); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - Filter = filter - }; - - ExecuteOperation(subject, async); - - ReadAllFromCollection(_outputCollectionNamespace).Should().BeEquivalentTo( - BsonDocument.Parse("{ _id : 1, value : 1 }"), - BsonDocument.Parse("{ _id : 2, value : 4 }")); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_results_when_FinalizeFunction_is_set( - [Values(false, true)] - bool async) - { - RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); - EnsureTestData(); - var finalizeFunction = new BsonJavaScript("function(key, reduced) { return -reduced; }"); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - FinalizeFunction = finalizeFunction - }; - - ExecuteOperation(subject, async); - - ReadAllFromCollection(_outputCollectionNamespace).Should().BeEquivalentTo( - BsonDocument.Parse("{ _id : 1, value : -3 }"), - BsonDocument.Parse("{ _id : 2, value : -4 }")); - } - - // TODO: figure out why test fails when JavaScriptMode = true (server bug?) - - //[Theory] - //[ParameterAttributeData] - //public void Execute_should_return_expected_results_when_JavaScriptMode_is_set( - // [Values(null, false, true)] - // bool? javaScriptMode, - // [Values(false, true)] - // bool async) - //{ - // RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); - // EnsureTestData(); - // var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) - // { - // JavaScriptMode = javaScriptMode - // }; - - // ExecuteOperation(subject, async); - - // // the results are the same either way, but at least we're smoke testing JavaScriptMode - // ReadAllFromCollection(_outputCollectionNamespace).Should().Equal( - // BsonDocument.Parse("{ _id : 1, value : 3 }"), - // BsonDocument.Parse("{ _id : 2, value : 4 }")); - //} - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_results_when_Limit_is_set( - [Values(1, 2)] - long limit, - [Values(false, true)] - bool async) - { - RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); - EnsureTestData(); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - Limit = limit - }; - - ExecuteOperation(subject, async); - - var expectedResults = new[] - { - new BsonDocument { { "_id", 1 }, { "value", limit == 1 ? 1 : 3 } } - }; - ReadAllFromCollection(_outputCollectionNamespace).Should().Equal(expectedResults); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_results_when_MaxTime_is_set( - [Values(null, 1000)] - int? seconds, - [Values(false, true)] - bool async) - { - RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); - EnsureTestData(); - var maxTime = seconds.HasValue ? TimeSpan.FromSeconds(seconds.Value) : (TimeSpan?)null; -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - MaxTime = maxTime - }; - - ExecuteOperation(subject, async); - - // results should be the same whether MaxTime was used or not - ReadAllFromCollection(_outputCollectionNamespace).Should().BeEquivalentTo( - BsonDocument.Parse("{ _id : 1, value : 3 }"), - BsonDocument.Parse("{ _id : 2, value : 4 }")); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_results_when_Scope_is_set( - [Values(false, true)] - bool async) - { - RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); - EnsureTestData(); - var finalizeFunction = new BsonJavaScript("function(key, reduced) { return reduced + zeroFromScope; }"); - var scope = new BsonDocument("zeroFromScope", 0); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - FinalizeFunction = finalizeFunction, - Scope = scope - }; - - ExecuteOperation(subject, async); - - ReadAllFromCollection(_outputCollectionNamespace).Should().BeEquivalentTo( - BsonDocument.Parse("{ _id : 1, value : 3 }"), - BsonDocument.Parse("{ _id : 2, value : 4 }")); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_results_when_Sort_is_set( - [Values(1, -1)] - int direction, - [Values(false, true)] - bool async) - { - RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); - EnsureTestData(); - var sort = new BsonDocument("_id", direction); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - Limit = 2, - Sort = sort - }; - - ExecuteOperation(subject, async); - - BsonDocument[] expectedResults; - if (direction == 1) - { - expectedResults = new[] - { - BsonDocument.Parse("{ _id : 1, value : 3 }") - }; - } - else - { - expectedResults = new[] - { - BsonDocument.Parse("{ _id : 1, value : 2 }"), - BsonDocument.Parse("{ _id : 2, value : 4 }") - }; - } - ReadAllFromCollection(_outputCollectionNamespace).Should().BeEquivalentTo(expectedResults); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_throw_when_binding_is_null( - [Values(false, true)] - bool async) - { -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - using var session = OperationTestHelper.CreateSession(); - using var operationContext = new OperationContext(session); - - var exception = Record.Exception(() => ExecuteOperation(operationContext, subject, null, async)); - - var argumentNullException = exception.Should().BeOfType().Subject; - argumentNullException.ParamName.Should().Be("binding"); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_throw_when_a_write_concern_error_occurs( - [Values(false, true)] - bool async) - { - RequireServer.Check().ClusterType(ClusterType.ReplicaSet); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) -#pragma warning restore CS0618 // Type or member is obsolete - { - WriteConcern = new WriteConcern(9) - }; - - var exception = Record.Exception(() => ExecuteOperation(subject, async)); - - exception.Should().BeOfType(); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_send_session_id_when_supported( - [Values(false, true)] bool async) - { - RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); - EnsureTestData(); -#pragma warning disable CS0618 // Type or member is obsolete - var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); -#pragma warning restore CS0618 // Type or member is obsolete - - VerifySessionIdWasSentWhenSupported(subject, "mapReduce", async); - } - - // helper methods - private void EnsureTestData() - { - DropCollection(); - Insert( - new BsonDocument { { "_id", 1 }, { "x", 1 }, { "v", 1 }, { "y", "a" } }, - new BsonDocument { { "_id", 2 }, { "x", 1 }, { "v", 2 }, { "y", "A" } }, - new BsonDocument { { "_id", 3 }, { "x", 2 }, { "v", 4 }, { "y", "a" } }); - } - - // nested types - private class Reflector - { - // fields -#pragma warning disable CS0618 // Type or member is obsolete - private readonly MapReduceOutputToCollectionOperation _instance; - - // constructor - public Reflector(MapReduceOutputToCollectionOperation instance) - { - _instance = instance; - } - - // methods - public BsonDocument CreateOutputOptions() - { - var method = typeof(MapReduceOutputToCollectionOperation).GetMethod("CreateOutputOptions", BindingFlags.NonPublic | BindingFlags.Instance); - return (BsonDocument)method.Invoke(_instance, new object[0]); - } -#pragma warning restore CS0618 // Type or member is obsolete - } - } -} diff --git a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenMapReduceTest.cs b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenMapReduceTest.cs deleted file mode 100644 index 9a5c63f965d..00000000000 --- a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenMapReduceTest.cs +++ /dev/null @@ -1,123 +0,0 @@ -/* Copyright 2019-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using FluentAssertions; -using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.JsonDrivenTests; -using MongoDB.Driver.Core.Operations; - -namespace MongoDB.Driver.Tests.JsonDrivenTests -{ - public sealed class JsonDrivenMapReduceTest : JsonDrivenCollectionTest - { - // private fields - private BsonJavaScript _map; -#pragma warning disable CS0618 // Type or member is obsolete - private MapReduceOptions _options = new MapReduceOptions(); -#pragma warning restore CS0618 // Type or member is obsolete - private BsonJavaScript _reduce; - private List _result; - private IClientSessionHandle _session; - - // public constructors - public JsonDrivenMapReduceTest(IMongoCollection collection, Dictionary objectMap) - : base(collection, objectMap) - { - } - - // public methods - public override void Arrange(BsonDocument document) - { - JsonDrivenHelper.EnsureAllFieldsAreValid(document, "name", "object", "collectionOptions", "arguments", "result", "error"); - base.Arrange(document); - } - - // protected methods - protected override void AssertResult() - { - _result.Should().Equal(_expectedResult.AsBsonArray.Cast()); - } - - protected override void CallMethod(CancellationToken cancellationToken) - { - IAsyncCursor cursor; - if (_session == null) - { -#pragma warning disable CS0618 // Type or member is obsolete - cursor = _collection.MapReduce(_map, _reduce, _options, cancellationToken); -#pragma warning restore CS0618 // Type or member is obsolete - } - else - { -#pragma warning disable CS0618 // Type or member is obsolete - cursor = _collection.MapReduce(_session, _map, _reduce, _options, cancellationToken); -#pragma warning restore CS0618 // Type or member is obsolete - } - - _result = cursor.ToList(); - } - - protected override async Task CallMethodAsync(CancellationToken cancellationToken) - { - IAsyncCursor cursor; - if (_session == null) - { -#pragma warning disable CS0618 // Type or member is obsolete - cursor = await _collection.MapReduceAsync(_map, _reduce, _options, cancellationToken).ConfigureAwait(false); -#pragma warning restore CS0618 // Type or member is obsolete - } - else - { -#pragma warning disable CS0618 // Type or member is obsolete - cursor = await _collection.MapReduceAsync(_session, _map, _reduce, _options, cancellationToken).ConfigureAwait(false); -#pragma warning restore CS0618 // Type or member is obsolete - } - - _result = await cursor.ToListAsync(cancellationToken).ConfigureAwait(false); - } - - protected override void SetArgument(string name, BsonValue value) - { - switch (name) - { - case "map": - _map = BsonJavaScript.Create(value); - return; - - case "reduce": - _reduce = BsonJavaScript.Create(value); - return; - - case "out": - _options.OutputOptions = value is BsonString -#pragma warning disable CS0618 // Type or member is obsolete - ? new MapReduceOutputOptions.CollectionOutput(value.AsString, MapReduceOutputMode.Replace) - : MapReduceOutputOptions.Inline; // TODO: Clean this up. -#pragma warning restore CS0618 // Type or member is obsolete - return; - - case "session": - _session = (IClientSessionHandle)_objectMap[value.AsString]; - return; - } - - base.SetArgument(name, value); - } - } -} diff --git a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs index 0b28834b5ca..333d71840d2 100644 --- a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs +++ b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs @@ -172,7 +172,6 @@ public JsonDrivenTest CreateTest(string receiver, string name) case "insertOne": return new JsonDrivenInsertOneTest(collection, _objectMap); case "listIndexes": return new JsonDrivenListIndexesTest(collection, _objectMap); case "listIndexNames": throw new SkipException(".NET/C# driver does not implement a ListIndexNames helper."); - case "mapReduce": return new JsonDrivenMapReduceTest(collection, _objectMap); case "replaceOne": return new JsonDrivenReplaceOneTest(collection, _objectMap); case "updateMany": return new JsonDrivenUpdateManyTest(collection, _objectMap); case "updateOne": return new JsonDrivenUpdateOneTest(collection, _objectMap); diff --git a/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs b/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs index 2cf34897e4d..5e71c65d2ff 100644 --- a/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs @@ -2891,200 +2891,6 @@ public void InsertMany_should_respect_AssignIdOnInsert( document.Contains("_id").Should().Be(assignIdOnInsert); } - [Theory] - [ParameterAttributeData] - public void MapReduce_with_inline_output_mode_should_execute_a_MapReduceOperation( - [Values(false, true)] bool usingSession, - [Values(false, true)] bool async) - { - var subject = CreateSubject(); - var session = CreateSession(usingSession); - var map = new BsonJavaScript("map"); - var reduce = new BsonJavaScript("reduce"); - var filterDocument = new BsonDocument("filter", 1); - var filterDefinition = (FilterDefinition)filterDocument; - var sortDocument = new BsonDocument("sort", 1); - var sortDefinition = (SortDefinition)sortDocument; -#pragma warning disable CS0618 // Type or member is obsolete - var options = new MapReduceOptions -#pragma warning restore CS0618 // Type or member is obsolete - { - Collation = new Collation("en_US"), - Filter = filterDefinition, - Finalize = new BsonJavaScript("finalizer"), -#pragma warning disable 618 - JavaScriptMode = true, -#pragma warning restore 618 - Limit = 10, - MaxTime = TimeSpan.FromMinutes(2), -#pragma warning disable CS0618 // Type or member is obsolete - OutputOptions = MapReduceOutputOptions.Inline, -#pragma warning restore CS0618 // Type or member is obsolete - Scope = new BsonDocument("test", 3), - Sort = sortDefinition, - Verbose = true - }; - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - - if (usingSession) - { - if (async) - { -#pragma warning disable CS0618 // Type or member is obsolete - subject.MapReduceAsync(session, map, reduce, options, cancellationToken).GetAwaiter().GetResult(); -#pragma warning restore CS0618 // Type or member is obsolete - } - else - { -#pragma warning disable CS0618 // Type or member is obsolete - subject.MapReduce(session, map, reduce, options, cancellationToken); -#pragma warning restore CS0618 // Type or member is obsolete - } - } - else - { - if (async) - { -#pragma warning disable CS0618 // Type or member is obsolete - subject.MapReduceAsync(map, reduce, options, cancellationToken).GetAwaiter().GetResult(); -#pragma warning restore CS0618 // Type or member is obsolete - } - else - { -#pragma warning disable CS0618 // Type or member is obsolete - subject.MapReduce(map, reduce, options, cancellationToken); -#pragma warning restore CS0618 // Type or member is obsolete - } - } - - var call = _operationExecutor.GetReadCall>(); - VerifySessionAndCancellationToken(call, session, cancellationToken); - -#pragma warning disable CS0618 // Type or member is obsolete - var operation = call.Operation.Should().BeOfType>().Subject; -#pragma warning restore CS0618 // Type or member is obsolete - operation.Collation.Should().BeSameAs(options.Collation); - operation.CollectionNamespace.Should().Be(subject.CollectionNamespace); - operation.Filter.Should().Be(filterDocument); - operation.FinalizeFunction.Should().Be(options.Finalize); -#pragma warning disable 618 - operation.JavaScriptMode.Should().Be(options.JavaScriptMode); -#pragma warning restore 618 - operation.Limit.Should().Be(options.Limit); - operation.MapFunction.Should().Be(map); - operation.MaxTime.Should().Be(options.MaxTime); - operation.ReadConcern.Should().Be(subject.Settings.ReadConcern); - operation.ReduceFunction.Should().Be(reduce); - operation.ResultSerializer.Should().Be(BsonDocumentSerializer.Instance); - operation.Scope.Should().Be(options.Scope); - operation.Sort.Should().Be(sortDocument); - operation.Verbose.Should().Be(options.Verbose); - } - - [Theory] - [ParameterAttributeData] - public void MapReduce_with_collection_output_mode_should_execute_a_MapReduceOutputToCollectionOperation( - [Values(false, true)] bool usingSession, - [Values(false, true)] bool async) - { - var writeConcern = new WriteConcern(1); - var subject = CreateSubject().WithWriteConcern(writeConcern); - var session = CreateSession(usingSession); - var map = new BsonJavaScript("map"); - var reduce = new BsonJavaScript("reduce"); - var filterDocument = new BsonDocument("filter", 1); - var filterDefinition = (FilterDefinition)filterDocument; - var sortDocument = new BsonDocument("sort", 1); - var sortDefinition = (SortDefinition)sortDocument; -#pragma warning disable CS0618 // Type or member is obsolete - var options = new MapReduceOptions -#pragma warning restore CS0618 // Type or member is obsolete - { - BypassDocumentValidation = true, - Collation = new Collation("en_US"), - Filter = filterDefinition, - Finalize = new BsonJavaScript("finalizer"), -#pragma warning disable 618 - JavaScriptMode = true, -#pragma warning restore 618 - Limit = 10, - MaxTime = TimeSpan.FromMinutes(2), -#pragma warning disable 618 - OutputOptions = MapReduceOutputOptions.Replace("awesome", "otherDB", true), -#pragma warning restore 618 - Scope = new BsonDocument("test", 3), - Sort = sortDefinition, - Verbose = true - }; - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - - if (usingSession) - { - if (async) - { -#pragma warning disable CS0618 // Type or member is obsolete - subject.MapReduceAsync(session, map, reduce, options, cancellationToken).GetAwaiter().GetResult(); -#pragma warning restore CS0618 // Type or member is obsolete - } - else - { -#pragma warning disable CS0618 // Type or member is obsolete - subject.MapReduce(session, map, reduce, options, cancellationToken); -#pragma warning restore CS0618 // Type or member is obsolete - } - } - else - { - if (async) - { -#pragma warning disable CS0618 // Type or member is obsolete - subject.MapReduceAsync(map, reduce, options, cancellationToken).GetAwaiter().GetResult(); -#pragma warning restore CS0618 // Type or member is obsolete - } - else - { -#pragma warning disable CS0618 // Type or member is obsolete - subject.MapReduce(map, reduce, options, cancellationToken); -#pragma warning restore CS0618 // Type or member is obsolete - } - } - - var call = _operationExecutor.GetWriteCall(); - VerifySessionAndCancellationToken(call, session, cancellationToken); - -#pragma warning disable CS0618 // Type or member is obsolete - var operation = call.Operation.Should().BeOfType().Subject; -#pragma warning restore CS0618 // Type or member is obsolete - operation.BypassDocumentValidation.Should().Be(options.BypassDocumentValidation); - operation.Collation.Should().BeSameAs(options.Collation); - operation.CollectionNamespace.Should().Be(subject.CollectionNamespace); - operation.Filter.Should().Be(filterDocument); - operation.FinalizeFunction.Should().Be(options.Finalize); -#pragma warning disable 618 - operation.JavaScriptMode.Should().Be(options.JavaScriptMode); -#pragma warning restore 618 - operation.Limit.Should().Be(options.Limit); - operation.MapFunction.Should().Be(map); - operation.MaxTime.Should().Be(options.MaxTime); -#pragma warning disable 618 - operation.NonAtomicOutput.Should().NotHaveValue(); -#pragma warning restore 618 - operation.OutputCollectionNamespace.Should().Be(CollectionNamespace.FromFullName("otherDB.awesome")); -#pragma warning disable CS0618 // Type or member is obsolete - operation.OutputMode.Should().Be(Core.Operations.MapReduceOutputMode.Replace); -#pragma warning restore CS0618 // Type or member is obsolete - operation.ReduceFunction.Should().Be(reduce); - operation.Scope.Should().Be(options.Scope); -#pragma warning disable 618 - operation.ShardedOutput.Should().Be(true); -#pragma warning restore 618 - operation.Sort.Should().Be(sortDocument); - operation.Verbose.Should().Be(options.Verbose); - operation.WriteConcern.Should().BeSameAs(writeConcern); - } - [Theory] [ParameterAttributeData] public void ReplaceOne_should_execute_a_BulkMixedOperation( diff --git a/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs b/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs index b89f33ffa31..5df82e835f7 100644 --- a/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs +++ b/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs @@ -786,80 +786,6 @@ public void FindOneAndUpdate_with_session_should_not_throw_when_options_is_null( } } - [Theory] - [ParameterAttributeData] - public async Task MapReduce_should_include_the_filter_when_one_was_not_provided( - [Values(false, true)] bool async) - { - var subject = CreateSubject(); - - if (async) - { -#pragma warning disable CS0618 // Type or member is obsolete - await subject.MapReduceAsync("map", "reduce", null, CancellationToken.None); - - _mockDerivedCollection.Verify( - c => c.MapReduceAsync( - "map", - "reduce", - It.Is>(o => RenderFilter(o.Filter).Equals(_ofTypeFilter)), - CancellationToken.None), - Times.Once); - } - else - { - subject.MapReduce("map", "reduce", null, CancellationToken.None); - - _mockDerivedCollection.Verify( - c => c.MapReduce( - "map", - "reduce", - It.Is>(o => RenderFilter(o.Filter).Equals(_ofTypeFilter)), - CancellationToken.None), - Times.Once); -#pragma warning restore CS0618 // Type or member is obsolete - } - } - - [Theory] - [ParameterAttributeData] - public async Task MapReduce_should_include_the_filter( - [Values(false, true)] bool async) - { - var subject = CreateSubject(); -#pragma warning disable CS0618 // Type or member is obsolete - var options = new MapReduceOptions - { - Filter = _providedFilter - }; - - if (async) - { - await subject.MapReduceAsync("map", "reduce", options, CancellationToken.None); - - _mockDerivedCollection.Verify( - c => c.MapReduceAsync( - "map", - "reduce", - It.Is>(o => RenderFilter(o.Filter).Equals(_expectedFilter)), - CancellationToken.None), - Times.Once); - } - else - { - subject.MapReduce("map", "reduce", options, CancellationToken.None); - - _mockDerivedCollection.Verify( - c => c.MapReduce( - "map", - "reduce", - It.Is>(o => RenderFilter(o.Filter).Equals(_expectedFilter)), - CancellationToken.None), - Times.Once); - } -#pragma warning restore CS0618 // Type or member is obsolete - } - [Fact] public void OfType_should_resort_to_root_collections_OfType() { diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedMapReduceOperation.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedMapReduceOperation.cs deleted file mode 100644 index d231630b623..00000000000 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedMapReduceOperation.cs +++ /dev/null @@ -1,120 +0,0 @@ -/* Copyright 2010-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Threading; -using System.Threading.Tasks; -using MongoDB.Bson; -using MongoDB.Driver.Core.Misc; - -namespace MongoDB.Driver.Tests.UnifiedTestOperations -{ - public class UnifiedMapReduceOperation : IUnifiedEntityTestOperation - { - private readonly IMongoCollection _collection; - private readonly BsonJavaScript _map; - private readonly BsonJavaScript _reduce; - - public UnifiedMapReduceOperation( - IMongoCollection collection, - BsonJavaScript map, - BsonJavaScript reduce) - { - _collection = collection; - _map = Ensure.IsNotNull(map, nameof(map)); - _reduce = Ensure.IsNotNull(reduce, nameof(reduce)); - } - - /// - /// Executes the specified cancellation token. - /// - /// The cancellation token. - /// - public OperationResult Execute(CancellationToken cancellationToken) - { - try - { -#pragma warning disable CS0618 // Type or member is obsolete - var cursor = _collection.MapReduce(_map, _reduce); -#pragma warning restore CS0618 // Type or member is obsolete - - var result = cursor.ToList(cancellationToken); - return OperationResult.FromResult(new BsonArray(result)); - } - catch (Exception exception) - { - return OperationResult.FromException(exception); - } - } - - public async Task ExecuteAsync(CancellationToken cancellationToken) - { - try - { -#pragma warning disable CS0618 // Type or member is obsolete - var cursor = await _collection.MapReduceAsync(_map, _reduce); -#pragma warning restore CS0618 // Type or member is obsolete - - var result = await cursor.ToListAsync(cancellationToken); - return OperationResult.FromResult(new BsonArray(result)); - } - catch (Exception exception) - { - return OperationResult.FromException(exception); - } - } - } - - public class UnifiedMapReduceOperationBuilder - { - private readonly UnifiedEntityMap _entityMap; - - public UnifiedMapReduceOperationBuilder(UnifiedEntityMap entityMap) - { - _entityMap = entityMap; - } - - public UnifiedMapReduceOperation Build(string targetCollectionId, BsonDocument arguments) - { - var collection = _entityMap.Collections[targetCollectionId]; - - BsonJavaScript map = null, reduce = null; - - foreach (var argument in arguments) - { - switch (argument.Name) - { - case "map": - map = argument.Value.AsBsonJavaScript; - break; - case "reduce": - reduce = argument.Value.AsBsonJavaScript; - break; - case "out": - var outDocument = argument.Value.AsBsonDocument; - if (!outDocument.Equals(new("inline", 1))) - { - throw new FormatException($"Invalid out setting '{argument.Value}'."); - } - break; - default: - throw new FormatException($"Invalid CountOperation argument name: '{argument.Name}'."); - } - } - - return new UnifiedMapReduceOperation(collection, map, reduce); - } - } -} diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs index ae64a0eb443..3a28a8c83a4 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs @@ -112,7 +112,6 @@ public IUnifiedTestOperation CreateOperation(string operationName, string target "insertOne" => new UnifiedInsertOneOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "listIndexes" => new UnifiedListIndexesOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "listSearchIndexes" => new UnifiedListSearchIndexesOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), - "mapReduce" => new UnifiedMapReduceOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "rename" => new UnifiedRenameCollectionOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "replaceOne" => new UnifiedReplaceOneOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "updateMany" => new UnifiedUpdateManyOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), From 5b782972af0c5c13d1107dc958bf325d65e7bceb Mon Sep 17 00:00:00 2001 From: adelinowona Date: Wed, 29 Jul 2026 16:36:28 -0400 Subject: [PATCH 04/13] CSHARP-5996: Remove obsolete Count and CountAsync Removes the deprecated count helpers in favour of CountDocuments and EstimatedDocumentCount: - IMongoCollection.Count / CountAsync and their implementations in MongoCollectionBase, MongoCollectionImpl and FilteredMongoCollectionBase. - The Count / CountAsync expression overloads on IMongoCollectionExtensions. - IFindFluent.Count / CountAsync and the FindFluentBase and FindFluent implementations. - MongoCollectionImpl.CreateCountOperation, orphaned by the above. CountOperation itself stays: EstimatedDocumentCountOperation builds one, so the count command is still sent and still covered. Test changes: - CausalConsistencyTests and PlainAuthenticationTests used Count only as a convenient read; both now use CountDocuments. The causal-consistency event captures move from the count command to aggregate accordingly. - OfTypeMongoCollectionTests' two derived-type counting tests assert discriminator filtering rather than the Count API, so they move to CountDocuments and are renamed to match. - Deletes the tests that only exercised the removed API, along with JsonDrivenCountTest and UnifiedCountOperation. - Both spec-test factories now skip the "count" operation with a reason. The spec suites still drive it in 44 places, and every one of those paths is also covered by countDocuments or estimatedDocumentCount, so no unique conformance coverage is lost. The spec's own test names call these cases "Deprecated count". Also fixes an error in the preceding MapReduce commit, which claimed no spec fixtures referenced mapReduce and deleted its factory registrations outright. The fixtures live in the repo-root specifications/ tree, not under tests/, so the earlier search missed four files and left six retryable-reads cases failing with "Invalid method name: 'mapReduce'". Both factories now skip mapReduce the same way count is skipped. Removing public API is a breaking change and targets the 4.0 major release. --- .../FilteredMongoCollectionBase.cs | 24 --- src/MongoDB.Driver/FindFluent.cs | 28 ---- src/MongoDB.Driver/FindFluentBase.cs | 11 -- src/MongoDB.Driver/IFindFluent.cs | 16 -- src/MongoDB.Driver/IMongoCollection.cs | 50 ------- .../IMongoCollectionExtensions.cs | 84 ----------- src/MongoDB.Driver/MongoCollectionBase.cs | 21 --- src/MongoDB.Driver/MongoCollectionImpl.cs | 57 -------- .../CausalConsistencyTests.cs | 44 ++---- .../Security/PlainAuthenticationTests.cs | 4 +- tests/MongoDB.Driver.Tests/FindFluentTests.cs | 86 ----------- .../IMongoCollectionExtensionsTests.cs | 50 ------- .../JsonDrivenTests/JsonDrivenCountTest.cs | 100 ------------- .../JsonDrivenTests/JsonDrivenTestFactory.cs | 3 +- .../MongoCollectionImplTests.cs | 66 --------- .../OfTypeMongoCollectionTests.cs | 60 +------- .../UnifiedCountOperation.cs | 138 ------------------ .../UnifiedTestOperationFactory.cs | 4 +- 18 files changed, 29 insertions(+), 817 deletions(-) delete mode 100644 tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenCountTest.cs delete mode 100644 tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedCountOperation.cs diff --git a/src/MongoDB.Driver/FilteredMongoCollectionBase.cs b/src/MongoDB.Driver/FilteredMongoCollectionBase.cs index 7a27fd8e40d..48c049971dd 100644 --- a/src/MongoDB.Driver/FilteredMongoCollectionBase.cs +++ b/src/MongoDB.Driver/FilteredMongoCollectionBase.cs @@ -148,30 +148,6 @@ protected IMongoCollection WrappedCollection return _wrappedCollection.BulkWriteAsync(session, CombineModelFilters(requests), options, cancellationToken); } - [Obsolete("Use CountDocuments or EstimatedDocumentCount instead.")] - public override long Count(FilterDefinition filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - return _wrappedCollection.Count(CombineFilters(filter), options, cancellationToken); - } - - [Obsolete("Use CountDocuments or EstimatedDocumentCount instead.")] - public override long Count(IClientSessionHandle session, FilterDefinition filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - return _wrappedCollection.Count(session, CombineFilters(filter), options, cancellationToken); - } - - [Obsolete("Use CountDocumentsAsync or EstimatedDocumentCountAsync instead.")] - public override Task CountAsync(FilterDefinition filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - return _wrappedCollection.CountAsync(CombineFilters(filter), options, cancellationToken); - } - - [Obsolete("Use CountDocumentsAsync or EstimatedDocumentCountAsync instead.")] - public override Task CountAsync(IClientSessionHandle session, FilterDefinition filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - return _wrappedCollection.CountAsync(session, CombineFilters(filter), options, cancellationToken); - } - public override long CountDocuments(FilterDefinition filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) { return _wrappedCollection.CountDocuments(CombineFilters(filter), options, cancellationToken); diff --git a/src/MongoDB.Driver/FindFluent.cs b/src/MongoDB.Driver/FindFluent.cs index 022e9fe6b6e..651c885bf34 100644 --- a/src/MongoDB.Driver/FindFluent.cs +++ b/src/MongoDB.Driver/FindFluent.cs @@ -58,34 +58,6 @@ public override IFindFluent As(IBsonSerializer CountAsync(CancellationToken cancellationToken) - { - var options = CreateCountOptions(); - if (_session == null) - { - return _collection.CountAsync(_filter, options, cancellationToken); - } - else - { - return _collection.CountAsync(_session, _filter, options, cancellationToken); - } - } - public override long CountDocuments(CancellationToken cancellationToken) { var options = CreateCountOptions(); diff --git a/src/MongoDB.Driver/FindFluentBase.cs b/src/MongoDB.Driver/FindFluentBase.cs index 6670b669d08..bd2c2bdd54c 100644 --- a/src/MongoDB.Driver/FindFluentBase.cs +++ b/src/MongoDB.Driver/FindFluentBase.cs @@ -36,17 +36,6 @@ public abstract class FindFluentBase : IOrderedFindFluen /// public abstract IFindFluent As(IBsonSerializer resultSerializer); - /// - [Obsolete("Use CountDocuments instead.")] - public virtual long Count(CancellationToken cancellationToken = default(CancellationToken)) - { - throw new NotImplementedException(); - } - - /// - [Obsolete("Use CountDocumentsAsync instead.")] - public abstract Task CountAsync(CancellationToken cancellationToken = default(CancellationToken)); - /// public virtual long CountDocuments(CancellationToken cancellationToken = default(CancellationToken)) { diff --git a/src/MongoDB.Driver/IFindFluent.cs b/src/MongoDB.Driver/IFindFluent.cs index f2027e0c9a4..d73b903570a 100644 --- a/src/MongoDB.Driver/IFindFluent.cs +++ b/src/MongoDB.Driver/IFindFluent.cs @@ -49,22 +49,6 @@ public interface IFindFluent : IAsyncCursorSourceThe fluent find interface. IFindFluent As(IBsonSerializer resultSerializer = null); - /// - /// Counts the number of documents. - /// - /// The cancellation token. - /// The count. - [Obsolete("Use CountDocuments instead.")] - long Count(CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Counts the number of documents. - /// - /// The cancellation token. - /// A Task whose result is the count. - [Obsolete("Use CountDocumentsAsync instead.")] - Task CountAsync(CancellationToken cancellationToken = default(CancellationToken)); - /// /// Counts the number of documents. /// diff --git a/src/MongoDB.Driver/IMongoCollection.cs b/src/MongoDB.Driver/IMongoCollection.cs index 91ea55bd760..6446ed3d68b 100644 --- a/src/MongoDB.Driver/IMongoCollection.cs +++ b/src/MongoDB.Driver/IMongoCollection.cs @@ -198,56 +198,6 @@ public interface IMongoCollection // TODO: derive from IMongoCollecti /// Task> BulkWriteAsync(IClientSessionHandle session, IEnumerable> requests, BulkWriteOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - /// - /// Counts the number of documents in the collection. - /// - /// The filter. - /// The options. - /// The cancellation token. - /// - /// The number of documents in the collection. - /// - [Obsolete("Use CountDocuments or EstimatedDocumentCount instead.")] - long Count(FilterDefinition filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Counts the number of documents in the collection. - /// - /// The session. - /// The filter. - /// The options. - /// The cancellation token. - /// - /// The number of documents in the collection. - /// - [Obsolete("Use CountDocuments or EstimatedDocumentCount instead.")] - long Count(IClientSessionHandle session, FilterDefinition filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Counts the number of documents in the collection. - /// - /// The filter. - /// The options. - /// The cancellation token. - /// - /// The number of documents in the collection. - /// - [Obsolete("Use CountDocumentsAsync or EstimatedDocumentCountAsync instead.")] - Task CountAsync(FilterDefinition filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Counts the number of documents in the collection. - /// - /// The session. - /// The filter. - /// The options. - /// The cancellation token. - /// - /// The number of documents in the collection. - /// - [Obsolete("Use CountDocumentsAsync or EstimatedDocumentCountAsync instead.")] - Task CountAsync(IClientSessionHandle session, FilterDefinition filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - /// /// Counts the number of documents in the collection. /// For a fast estimate of the total documents in a collection see . diff --git a/src/MongoDB.Driver/IMongoCollectionExtensions.cs b/src/MongoDB.Driver/IMongoCollectionExtensions.cs index 2e5507dca7f..465ca66a773 100644 --- a/src/MongoDB.Driver/IMongoCollectionExtensions.cs +++ b/src/MongoDB.Driver/IMongoCollectionExtensions.cs @@ -91,90 +91,6 @@ public static IQueryable AsQueryable(this IMongoCollection return AsQueryableHelper(collection, session, aggregateOptions); } - /// - /// Counts the number of documents in the collection. - /// - /// The type of the document. - /// The collection. - /// The filter. - /// The options. - /// The cancellation token. - /// - /// The number of documents in the collection. - /// - [Obsolete("Use CountDocuments or EstimatedDocumentCount instead.")] - public static long Count(this IMongoCollection collection, Expression> filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - Ensure.IsNotNull(collection, nameof(collection)); - Ensure.IsNotNull(filter, nameof(filter)); - - return collection.Count(new ExpressionFilterDefinition(filter), options, cancellationToken); - } - - /// - /// Counts the number of documents in the collection. - /// - /// The type of the document. - /// The session. - /// The collection. - /// The filter. - /// The options. - /// The cancellation token. - /// - /// The number of documents in the collection. - /// - [Obsolete("Use CountDocuments or EstimatedDocumentCount instead.")] - public static long Count(this IMongoCollection collection, IClientSessionHandle session, Expression> filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - Ensure.IsNotNull(collection, nameof(collection)); - Ensure.IsNotNull(session, nameof(session)); - Ensure.IsNotNull(filter, nameof(filter)); - - return collection.Count(session, new ExpressionFilterDefinition(filter), options, cancellationToken); - } - - /// - /// Counts the number of documents in the collection. - /// - /// The type of the document. - /// The collection. - /// The filter. - /// The options. - /// The cancellation token. - /// - /// The number of documents in the collection. - /// - [Obsolete("Use CountDocumentsAsync or EstimatedDocumentCountAsync instead.")] - public static Task CountAsync(this IMongoCollection collection, Expression> filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - Ensure.IsNotNull(collection, nameof(collection)); - Ensure.IsNotNull(filter, nameof(filter)); - - return collection.CountAsync(new ExpressionFilterDefinition(filter), options, cancellationToken); - } - - /// - /// Counts the number of documents in the collection. - /// - /// The type of the document. - /// The collection. - /// The session. - /// The filter. - /// The options. - /// The cancellation token. - /// - /// The number of documents in the collection. - /// - [Obsolete("Use CountDocumentsAsync or EstimatedDocumentCountAsync instead.")] - public static Task CountAsync(this IMongoCollection collection, IClientSessionHandle session, Expression> filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - Ensure.IsNotNull(collection, nameof(collection)); - Ensure.IsNotNull(session, nameof(session)); - Ensure.IsNotNull(filter, nameof(filter)); - - return collection.CountAsync(session, new ExpressionFilterDefinition(filter), options, cancellationToken); - } - /// /// Counts the number of documents in the collection. /// For a fast estimate of the total documents in a collection see . diff --git a/src/MongoDB.Driver/MongoCollectionBase.cs b/src/MongoDB.Driver/MongoCollectionBase.cs index 36e595ebad9..536b139a3a5 100644 --- a/src/MongoDB.Driver/MongoCollectionBase.cs +++ b/src/MongoDB.Driver/MongoCollectionBase.cs @@ -93,27 +93,6 @@ internal abstract class MongoCollectionBase : IMongoCollection filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - throw new NotImplementedException(); - } - - [Obsolete("Use CountDocuments or EstimatedDocumentCount instead.")] - public virtual long Count(IClientSessionHandle session, FilterDefinition filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - throw new NotImplementedException(); - } - - [Obsolete("Use CountDocumentsAsync or EstimatedDocumentCountAsync instead.")] - public abstract Task CountAsync(FilterDefinition filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); - - [Obsolete("Use CountDocumentsAsync or EstimatedDocumentCountAsync instead.")] - public virtual Task CountAsync(IClientSessionHandle session, FilterDefinition filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - throw new NotImplementedException(); - } - public virtual long CountDocuments(FilterDefinition filter, CountOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); diff --git a/src/MongoDB.Driver/MongoCollectionImpl.cs b/src/MongoDB.Driver/MongoCollectionImpl.cs index 5bc6a874207..8f6abe2525b 100644 --- a/src/MongoDB.Driver/MongoCollectionImpl.cs +++ b/src/MongoDB.Driver/MongoCollectionImpl.cs @@ -248,40 +248,6 @@ public override async Task> BulkWriteAsync(IClientSes } } - [Obsolete("Use CountDocuments or EstimatedDocumentCount instead.")] - public override long Count(FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default) - { - using var session = _operationExecutor.StartImplicitSession(); - return Count(session, filter, options, cancellationToken); - } - - [Obsolete("Use CountDocuments or EstimatedDocumentCount instead.")] - public override long Count(IClientSessionHandle session, FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default) - { - Ensure.IsNotNull(session, nameof(session)); - Ensure.IsNotNull(filter, nameof(filter)); - - var operation = CreateCountOperation(filter, options); - return ExecuteReadOperation(session, operation, options?.Timeout, cancellationToken); - } - - [Obsolete("Use CountDocumentsAsync or EstimatedDocumentCountAsync instead.")] - public override async Task CountAsync(FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default) - { - using var session = _operationExecutor.StartImplicitSession(); - return await CountAsync(session, filter, options, cancellationToken).ConfigureAwait(false); - } - - [Obsolete("Use CountDocumentsAsync or EstimatedDocumentCountAsync instead.")] - public override Task CountAsync(IClientSessionHandle session, FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default) - { - Ensure.IsNotNull(session, nameof(session)); - Ensure.IsNotNull(filter, nameof(filter)); - - var operation = CreateCountOperation(filter, options); - return ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken); - } - public override long CountDocuments(FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default) { using var session = _operationExecutor.StartImplicitSession(); @@ -859,29 +825,6 @@ private CountDocumentsOperation CreateCountDocumentsOperation( }; } - private CountOperation CreateCountOperation( - FilterDefinition filter, - CountOptions options) - { - options ??= new CountOptions(); - var renderArgs = GetRenderArgs(); - - return new CountOperation(_collectionNamespace, _messageEncoderSettings) - { - Collation = options.Collation, - Comment = options.Comment, - EnableOverloadRetargeting = _database.Client.Settings.EnableOverloadRetargeting, - Filter = filter.Render(renderArgs), - Hint = options.Hint, - Limit = options.Limit, - MaxAdaptiveRetries = _database.Client.Settings.MaxAdaptiveRetries, - MaxTime = options.MaxTime, - ReadConcern = _settings.ReadConcern, - RetryRequested = _database.Client.Settings.RetryReads, - Skip = options.Skip - }; - } - private DistinctOperation CreateDistinctOperation( FieldDefinition field, FilterDefinition filter, diff --git a/tests/MongoDB.Driver.Tests/CausalConsistencyTests.cs b/tests/MongoDB.Driver.Tests/CausalConsistencyTests.cs index 043d3726182..4335321dc76 100644 --- a/tests/MongoDB.Driver.Tests/CausalConsistencyTests.cs +++ b/tests/MongoDB.Driver.Tests/CausalConsistencyTests.cs @@ -48,15 +48,13 @@ public void AfterClusterTime_should_be_empty_on_the_first_operation() { RequireServer.Check().SupportsCausalConsistency(); - var events = new EventCapturer().Capture(x => x.CommandName == "count"); + var events = new EventCapturer().Capture(x => x.CommandName == "aggregate"); using (var client = GetClient(events)) using (var session = client.StartSession()) { -#pragma warning disable 618 client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName) - .Count(session, FilterDefinition.Empty); -#pragma warning restore + .CountDocuments(session, FilterDefinition.Empty); var commandStartedEvent = (CommandStartedEvent)events.Next(); commandStartedEvent.Command.GetValue("readConcern", null).Should().BeNull(); @@ -69,16 +67,14 @@ public void Session_OperationTime_should_get_updated_after_an_operation() RequireServer.Check().SupportsCausalConsistency(); var events = new EventCapturer() - .Capture(x => x.CommandName == "count") - .Capture(x => x.CommandName == "count"); + .Capture(x => x.CommandName == "aggregate") + .Capture(x => x.CommandName == "aggregate"); using (var client = GetClient(events)) using (var session = client.StartSession()) { -#pragma warning disable 618 client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName) - .Count(session, FilterDefinition.Empty); -#pragma warning restore + .CountDocuments(session, FilterDefinition.Empty); var commandStartedEvent = (CommandStartedEvent)events.Next(); commandStartedEvent.Command.GetValue("readConcern", null).Should().BeNull(); @@ -94,7 +90,7 @@ public void AfterClusterTime_should_be_sent_after_the_first_read_operation() RequireServer.Check().SupportsCausalConsistency(); var events = new EventCapturer() - .Capture(x => x.CommandName == "count") + .Capture(x => x.CommandName == "aggregate") .Capture(x => x.CommandName == "find"); using (var client = GetClient(events)) using (var session = client.StartSession()) @@ -107,11 +103,9 @@ public void AfterClusterTime_should_be_sent_after_the_first_read_operation() session.OperationTime.Should().Be(commandSucceededEvent.Reply.GetValue("operationTime")); var operationTime = session.OperationTime; -#pragma warning disable 618 client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName) - .Count(session, FilterDefinition.Empty); -#pragma warning restore + .CountDocuments(session, FilterDefinition.Empty); var commandStartedEvent = (CommandStartedEvent)events.Next(); commandStartedEvent.Command["readConcern"]["afterClusterTime"].AsBsonTimestamp.Should().Be(operationTime); @@ -124,7 +118,7 @@ public void AfterClusterTime_should_be_sent_after_the_first_write_operation() RequireServer.Check().SupportsCausalConsistency(); var events = new EventCapturer() - .Capture(x => x.CommandName == "count") + .Capture(x => x.CommandName == "aggregate") .Capture(x => x.CommandName == "insert"); using (var client = GetClient(events)) using (var session = client.StartSession()) @@ -137,11 +131,9 @@ public void AfterClusterTime_should_be_sent_after_the_first_write_operation() session.OperationTime.Should().Be(commandSucceededEvent.Reply.GetValue("operationTime")); var operationTime = session.OperationTime; -#pragma warning disable 618 client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName) - .Count(session, FilterDefinition.Empty); -#pragma warning restore + .CountDocuments(session, FilterDefinition.Empty); var commandStartedEvent = (CommandStartedEvent)events.Next(); commandStartedEvent.Command["readConcern"]["afterClusterTime"].AsBsonTimestamp.Should().Be(operationTime); @@ -154,15 +146,13 @@ public void AfterClusterTime_should_not_be_sent_when_the_session_is_not_causally RequireServer.Check().SupportsCausalConsistency(); var events = new EventCapturer() - .Capture(x => x.CommandName == "count"); + .Capture(x => x.CommandName == "aggregate"); using (var client = GetClient(events)) using (var session = client.StartSession(new ClientSessionOptions { CausalConsistency = false })) { -#pragma warning disable 618 client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName) - .Count(session, FilterDefinition.Empty); -#pragma warning restore + .CountDocuments(session, FilterDefinition.Empty); var commandStartedEvent = (CommandStartedEvent)events.Next(); commandStartedEvent.Command.Contains("readConcern").Should().BeFalse(); @@ -175,7 +165,7 @@ public void ReadConcern_should_not_include_level_when_using_the_server_default() RequireServer.Check().SupportsCausalConsistency(); var events = new EventCapturer() - .Capture(x => x.CommandName == "count"); + .Capture(x => x.CommandName == "aggregate"); using (var client = GetClient(events)) using (var session = client.StartSession()) { @@ -183,12 +173,10 @@ public void ReadConcern_should_not_include_level_when_using_the_server_default() .GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName) .InsertOne(session, new BsonDocument("x", 1)); -#pragma warning disable 618 client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName) .WithReadConcern(ReadConcern.Default) - .Count(session, FilterDefinition.Empty); -#pragma warning restore + .CountDocuments(session, FilterDefinition.Empty); var commandStartedEvent = (CommandStartedEvent)events.Next(); commandStartedEvent.Command["readConcern"].AsBsonDocument.Contains("level").Should().BeFalse(); @@ -201,7 +189,7 @@ public void ReadConcern_should_include_level_when_not_using_the_server_default() RequireServer.Check().SupportsCausalConsistency(); var events = new EventCapturer() - .Capture(x => x.CommandName == "count"); + .Capture(x => x.CommandName == "aggregate"); using (var client = GetClient(events)) using (var session = client.StartSession()) { @@ -209,12 +197,10 @@ public void ReadConcern_should_include_level_when_not_using_the_server_default() .GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName) .InsertOne(session, new BsonDocument("x", 1)); -#pragma warning disable 618 client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName) .WithReadConcern(ReadConcern.Majority) - .Count(session, FilterDefinition.Empty); -#pragma warning restore + .CountDocuments(session, FilterDefinition.Empty); var commandStartedEvent = (CommandStartedEvent)events.Next(); commandStartedEvent.Command["readConcern"].AsBsonDocument.Contains("level").Should().BeTrue(); diff --git a/tests/MongoDB.Driver.Tests/Communication/Security/PlainAuthenticationTests.cs b/tests/MongoDB.Driver.Tests/Communication/Security/PlainAuthenticationTests.cs index 10dd96160bc..e47d689ae24 100644 --- a/tests/MongoDB.Driver.Tests/Communication/Security/PlainAuthenticationTests.cs +++ b/tests/MongoDB.Driver.Tests/Communication/Security/PlainAuthenticationTests.cs @@ -44,12 +44,10 @@ public void TestNoCredentials() Assert.Throws(() => { -#pragma warning disable 618 client .GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection(__collectionName) - .Count(new BsonDocument()); -#pragma warning restore + .CountDocuments(new BsonDocument()); }); } diff --git a/tests/MongoDB.Driver.Tests/FindFluentTests.cs b/tests/MongoDB.Driver.Tests/FindFluentTests.cs index 78faff1ec9b..358411ead1f 100644 --- a/tests/MongoDB.Driver.Tests/FindFluentTests.cs +++ b/tests/MongoDB.Driver.Tests/FindFluentTests.cs @@ -73,92 +73,6 @@ public void As_should_change_the_result_type( } } - [Theory] - [ParameterAttributeData] - public void Count_should_call_collection_Count( - [Values(false, true)] bool usingSession, - [Values(false, true)] bool async) - { - var session = CreateSession(usingSession); - var filter = new BsonDocumentFilterDefinition(new BsonDocument("filter", 1)); - var hint = new BsonDocument("hint", 1); - var findOptions = new FindOptions - { - Collation = new Collation("en-us"), - Hint = hint, - Limit = 1, - MaxTime = TimeSpan.FromSeconds(2), - Skip = 3 - }; - var subject = CreateSubject(session: session, filter: filter, options: findOptions); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - - Predicate matchesExpectedOptions = countOptions => - countOptions.Collation.Equals(findOptions.Collation) && - countOptions.Hint.Equals(hint) && - countOptions.Limit.Equals((long?)findOptions.Limit) && - countOptions.MaxTime.Equals(findOptions.MaxTime) && - countOptions.Skip.Equals((long?)findOptions.Skip); - - if (async) - { - if (usingSession) - { -#pragma warning disable 618 - subject.CountAsync(cancellationToken).GetAwaiter().GetResult(); - _mockCollection.Verify( - m => m.CountAsync( - session, - filter, - It.Is(o => matchesExpectedOptions(o)), - cancellationToken), - Times.Once); -#pragma warning restore - } - else - { -#pragma warning disable 618 - subject.CountAsync(cancellationToken).GetAwaiter().GetResult(); - _mockCollection.Verify( - m => m.CountAsync( - filter, - It.Is(o => matchesExpectedOptions(o)), - cancellationToken), - Times.Once); - } -#pragma warning restore - } - else - { - if (usingSession) - { -#pragma warning disable 618 - subject.Count(cancellationToken); - _mockCollection.Verify( - m => m.Count( - session, - filter, - It.Is(o => matchesExpectedOptions(o)), - cancellationToken), - Times.Once); -#pragma warning restore - } - else - { -#pragma warning disable 618 - subject.Count(cancellationToken); - _mockCollection.Verify( - m => m.Count( - filter, - It.Is(o => matchesExpectedOptions(o)), - cancellationToken), - Times.Once); -#pragma warning restore - } - } - } - [Theory] [ParameterAttributeData] public void CountDocuments_should_call_collection_CountDocuments( diff --git a/tests/MongoDB.Driver.Tests/IMongoCollectionExtensionsTests.cs b/tests/MongoDB.Driver.Tests/IMongoCollectionExtensionsTests.cs index 643e501ab32..d2a701bca48 100644 --- a/tests/MongoDB.Driver.Tests/IMongoCollectionExtensionsTests.cs +++ b/tests/MongoDB.Driver.Tests/IMongoCollectionExtensionsTests.cs @@ -81,56 +81,6 @@ public void AsQueryable_should_return_expected_result( provider._session().Should().BeSameAs(session); } - [Theory] - [ParameterAttributeData] - public void Count_should_call_collection_with_expected_arguments( - [Values(false, true)] bool usingSession, - [Values(false, true)] bool async) - { - var mockCollection = CreateMockCollection(); - var collection = mockCollection.Object; - var session = new Mock().Object; - var filterExpression = (Expression>)(x => x.FirstName == "Jack"); - var options = new CountOptions(); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - - if (usingSession) - { - if (async) - { -#pragma warning disable 618 - IMongoCollectionExtensions.CountAsync(collection, session, filterExpression, options, cancellationToken); - mockCollection.Verify(s => s.CountAsync(session, It.IsAny>(), options, cancellationToken), Times.Once); -#pragma warning restore - } - else - { -#pragma warning disable 618 - IMongoCollectionExtensions.Count(collection, session, filterExpression, options, cancellationToken); - mockCollection.Verify(s => s.Count(session, It.IsAny>(), options, cancellationToken), Times.Once); -#pragma warning restore - } - } - else - { - if (async) - { -#pragma warning disable 618 - IMongoCollectionExtensions.CountAsync(collection, filterExpression, options, cancellationToken); - mockCollection.Verify(s => s.CountAsync(It.IsAny>(), options, cancellationToken), Times.Once); -#pragma warning restore - } - else - { -#pragma warning disable 618 - IMongoCollectionExtensions.Count(collection, filterExpression, options, cancellationToken); - mockCollection.Verify(s => s.Count(It.IsAny>(), options, cancellationToken), Times.Once); -#pragma warning restore - } - } - } - [Theory] [ParameterAttributeData] public void CountDocuments_should_call_collection_with_expected_arguments( diff --git a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenCountTest.cs b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenCountTest.cs deleted file mode 100644 index 4bc1bb92799..00000000000 --- a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenCountTest.cs +++ /dev/null @@ -1,100 +0,0 @@ -/* Copyright 2018-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using FluentAssertions; -using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.JsonDrivenTests; - -namespace MongoDB.Driver.Tests.JsonDrivenTests -{ - public sealed class JsonDrivenCountTest : JsonDrivenCollectionTest - { - // private fields - private FilterDefinition _filter = new BsonDocument(); - private CountOptions _options = new CountOptions(); - private long _result; - private IClientSessionHandle _session; - - // public constructors - public JsonDrivenCountTest(IMongoCollection collection, Dictionary objectMap) - : base(collection, objectMap) - { - } - - // public methods - public override void Arrange(BsonDocument document) - { - JsonDrivenHelper.EnsureAllFieldsAreValid(document, "name", "object", "collectionOptions", "arguments", "result", "error"); - base.Arrange(document); - } - - // protected methods - protected override void AssertResult() - { - _result.Should().Be(_expectedResult.ToInt64()); - } - - protected override void CallMethod(CancellationToken cancellationToken) - { - if (_session == null) - { -#pragma warning disable 618 - _result = _collection.Count(_filter, _options, cancellationToken); -#pragma warning restore - } - else - { -#pragma warning disable 618 - _result = _collection.Count(_session, _filter, _options, cancellationToken); -#pragma warning restore - } - } - - protected override async Task CallMethodAsync(CancellationToken cancellationToken) - { - if (_session == null) - { -#pragma warning disable 618 - _result = await _collection.CountAsync(_filter, _options, cancellationToken).ConfigureAwait(false); -#pragma warning restore - } - else - { -#pragma warning disable 618 - _result = await _collection.CountAsync(_session, _filter, _options, cancellationToken).ConfigureAwait(false); -#pragma warning restore - } - } - - protected override void SetArgument(string name, BsonValue value) - { - switch (name) - { - case "filter": - _filter = new BsonDocumentFilterDefinition(value.AsBsonDocument); - return; - - case "session": - _session = (IClientSessionHandle)_objectMap[value.AsString]; - return; - } - - base.SetArgument(name, value); - } - } -} diff --git a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs index 333d71840d2..6dc31185715 100644 --- a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs +++ b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs @@ -153,7 +153,7 @@ public JsonDrivenTest CreateTest(string receiver, string name) { case "aggregate": return new JsonDrivenAggregateTest(collection, _objectMap); case "bulkWrite": return new JsonDrivenBulkWriteTest(collection, _objectMap); - case "count": return new JsonDrivenCountTest(collection, _objectMap); + case "count": throw new SkipException(".NET/C# driver does not implement a Count helper; use CountDocuments or EstimatedDocumentCount."); case "countDocuments": return new JsonDrivenCountDocumentsTest(collection, _objectMap); case "createIndex": return new JsonDrivenCreateIndexTest(collection, _objectMap); case "deleteMany": return new JsonDrivenDeleteManyTest(collection, _objectMap); @@ -172,6 +172,7 @@ public JsonDrivenTest CreateTest(string receiver, string name) case "insertOne": return new JsonDrivenInsertOneTest(collection, _objectMap); case "listIndexes": return new JsonDrivenListIndexesTest(collection, _objectMap); case "listIndexNames": throw new SkipException(".NET/C# driver does not implement a ListIndexNames helper."); + case "mapReduce": throw new SkipException(".NET/C# driver does not implement a MapReduce helper; use an aggregation pipeline."); case "replaceOne": return new JsonDrivenReplaceOneTest(collection, _objectMap); case "updateMany": return new JsonDrivenUpdateManyTest(collection, _objectMap); case "updateOne": return new JsonDrivenUpdateOneTest(collection, _objectMap); diff --git a/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs b/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs index 5e71c65d2ff..20bd60699e8 100644 --- a/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs @@ -800,72 +800,6 @@ public void BulkWrite_should_throw_if_model_is_invalid([Values(false, true)] boo } } - [Theory] - [ParameterAttributeData] - public void Count_should_execute_a_CountOperation( - [Values(false, true)] bool usingSession, - [Values(false, true)] bool async) - { - var subject = CreateSubject(); - var session = CreateSession(usingSession); - var filter = new BsonDocument("x", 1); - var options = new CountOptions - { - Collation = new Collation("en_US"), - Hint = "funny", - Limit = 10, - MaxTime = TimeSpan.FromSeconds(20), - Skip = 30 - }; - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - - if (usingSession) - { - if (async) - { -#pragma warning disable 618 - subject.CountAsync(session, filter, options, cancellationToken).GetAwaiter().GetResult(); -#pragma warning restore - } - else - { -#pragma warning disable 618 - subject.Count(session, filter, options, cancellationToken); -#pragma warning restore - } - } - else - { - if (async) - { -#pragma warning disable 618 - subject.CountAsync(filter, options, cancellationToken).GetAwaiter().GetResult(); -#pragma warning restore - } - else - { -#pragma warning disable 618 - subject.Count(filter, options, cancellationToken); -#pragma warning restore - } - } - - var call = _operationExecutor.GetReadCall(); - VerifySessionAndCancellationToken(call, session, cancellationToken); - - var operation = call.Operation.Should().BeOfType().Subject; - operation.Collation.Should().BeSameAs(options.Collation); - operation.CollectionNamespace.Should().Be(subject.CollectionNamespace); - operation.Filter.Should().Be(filter); - operation.Hint.Should().Be(options.Hint); - operation.Limit.Should().Be(options.Limit); - operation.MaxTime.Should().Be(options.MaxTime); - operation.ReadConcern.Should().Be(_readConcern); - operation.RetryRequested.Should().BeTrue(); - operation.Skip.Should().Be(options.Skip); - } - [Theory] [ParameterAttributeData] public void CountDocuments_should_execute_a_CountDocumentsOperation( diff --git a/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs b/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs index 5df82e835f7..371b7bad27d 100644 --- a/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs +++ b/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs @@ -465,42 +465,6 @@ public async Task BulkWrite_with_UpdateOne( } } - [Theory] - [ParameterAttributeData] - public async Task Count_should_include_the_filter( - [Values(false, true)] bool async) - { - var subject = CreateSubject(); - var options = new CountOptions(); - - if (async) - { -#pragma warning disable 618 - await subject.CountAsync(_providedFilter, options, CancellationToken.None); - - _mockDerivedCollection.Verify( - c => c.CountAsync( - It.Is>(f => RenderFilter(f).Equals(_expectedFilter)), - options, - CancellationToken.None), - Times.Once); -#pragma warning restore - } - else - { -#pragma warning disable 618 - subject.Count(_providedFilter, options, CancellationToken.None); - - _mockDerivedCollection.Verify( - c => c.Count( - It.Is>(f => RenderFilter(f).Equals(_expectedFilter)), - options, - CancellationToken.None), - Times.Once); -#pragma warning restore - } - } - [Theory] [ParameterAttributeData] public async Task CountDocuments_should_include_the_filter( @@ -946,7 +910,7 @@ public OfTypeCollectionIntegrationTests() [Theory] [ParameterAttributeData] - public void Count_should_only_count_derived_types( + public void CountDocuments_should_only_count_derived_types( [Values(false, true)] bool async) { var subject = CreateSubject(); @@ -954,17 +918,13 @@ public void Count_should_only_count_derived_types( long result1, result2; if (async) { -#pragma warning disable 618 - result1 = subject.CountAsync("{}").GetAwaiter().GetResult(); - result2 = subject.OfType().CountAsync("{}").GetAwaiter().GetResult(); -#pragma warning restore + result1 = subject.CountDocumentsAsync("{}").GetAwaiter().GetResult(); + result2 = subject.OfType().CountDocumentsAsync("{}").GetAwaiter().GetResult(); } else { -#pragma warning disable 618 - result1 = subject.Count("{}"); - result2 = subject.OfType().Count("{}"); -#pragma warning restore + result1 = subject.CountDocuments("{}"); + result2 = subject.OfType().CountDocuments("{}"); } result1.Should().Be(6); @@ -973,7 +933,7 @@ public void Count_should_only_count_derived_types( [Theory] [ParameterAttributeData] - public void Count_should_only_count_derived_types_with_a_filter( + public void CountDocuments_should_only_count_derived_types_with_a_filter( [Values(false, true)] bool async) { var subject = CreateSubject(); @@ -981,15 +941,11 @@ public void Count_should_only_count_derived_types_with_a_filter( long result; if (async) { -#pragma warning disable 618 - result = subject.CountAsync(x => x.PropB > 2).GetAwaiter().GetResult(); -#pragma warning restore + result = subject.CountDocumentsAsync(x => x.PropB > 2).GetAwaiter().GetResult(); } else { -#pragma warning disable 618 - result = subject.Count(x => x.PropB > 2); -#pragma warning restore + result = subject.CountDocuments(x => x.PropB > 2); } result.Should().Be(4); diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedCountOperation.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedCountOperation.cs deleted file mode 100644 index 56c9d67d52f..00000000000 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedCountOperation.cs +++ /dev/null @@ -1,138 +0,0 @@ -/* Copyright 2021-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Threading; -using System.Threading.Tasks; -using MongoDB.Bson; - -namespace MongoDB.Driver.Tests.UnifiedTestOperations -{ - public class UnifiedCountOperation : IUnifiedEntityTestOperation - { - private readonly IMongoCollection _collection; - private readonly FilterDefinition _filter; - private readonly CountOptions _options; - private readonly IClientSessionHandle _session; - - public UnifiedCountOperation( - IMongoCollection collection, - FilterDefinition filter, - CountOptions options, - IClientSessionHandle session) - { - _collection = collection; - _filter = filter; - _options = options; - _session = session; - } - - public OperationResult Execute(CancellationToken cancellationToken) - { - try - { -#pragma warning disable CS0618 // Type or member is obsolete - var result = _session == null - ? _collection.Count(_filter, _options, cancellationToken) - : _collection.Count(_session, _filter, _options, cancellationToken); -#pragma warning restore CS0618 // Type or member is obsolete - - return OperationResult.FromResult(result); - } - catch (Exception exception) - { - return OperationResult.FromException(exception); - } - } - - public async Task ExecuteAsync(CancellationToken cancellationToken) - { - try - { -#pragma warning disable CS0618 // Type or member is obsolete - var result = _session == null - ? await _collection.CountAsync(_filter, _options, cancellationToken) - : await _collection.CountAsync(_session, _filter, _options, cancellationToken); -#pragma warning restore CS0618 // Type or member is obsolete - - return OperationResult.FromResult(result); - } - catch (Exception exception) - { - return OperationResult.FromException(exception); - } - } - } - - public class UnifiedCountOperationBuilder - { - private readonly UnifiedEntityMap _entityMap; - - public UnifiedCountOperationBuilder(UnifiedEntityMap entityMap) - { - _entityMap = entityMap; - } - - public UnifiedCountOperation Build(string targetCollectionId, BsonDocument arguments) - { - var collection = _entityMap.Collections[targetCollectionId]; - - FilterDefinition filter = null; - CountOptions options = null; - IClientSessionHandle session = null; - - foreach (var argument in arguments) - { - switch (argument.Name) - { - case "collation": - options ??= new CountOptions(); - options.Collation = Collation.FromBsonDocument(argument.Value.AsBsonDocument); - break; - case "comment": - options ??= new CountOptions(); - options.Comment = argument.Value; - break; - case "filter": - filter = new BsonDocumentFilterDefinition(argument.Value.AsBsonDocument); - break; - case "limit": - options ??= new CountOptions(); - options.Limit = argument.Value.AsInt32; - break; - case "maxTimeMS": - options ??= new CountOptions(); - options.MaxTime = TimeSpan.FromMilliseconds(argument.Value.AsInt32); - break; - case "session": - session = _entityMap.Sessions[argument.Value.AsString]; - break; - case "skip": - options ??= new CountOptions(); - options.Skip = argument.Value.AsInt32; - break; - case "timeoutMS": - options ??= new CountOptions(); - options.Timeout = UnifiedEntityMap.ParseTimeout(argument.Value); - break; - default: - throw new FormatException($"Invalid CountOperation argument name: '{argument.Name}'."); - } - } - - return new UnifiedCountOperation(collection, filter, options, session); - } - } -} diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs index 3a28a8c83a4..14df0d1a7fc 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs @@ -16,6 +16,7 @@ using System; using System.Collections.Generic; using MongoDB.Bson; +using Xunit.Sdk; namespace MongoDB.Driver.Tests.UnifiedTestOperations { @@ -90,7 +91,7 @@ public IUnifiedTestOperation CreateOperation(string operationName, string target { "aggregate" => new UnifiedAggregateOperationBuilder(_entityMap).BuildCollectionOperation(targetEntityId, operationArguments), "bulkWrite" => new UnifiedBulkWriteOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), - "count" => new UnifiedCountOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), + "count" => throw new SkipException(".NET/C# driver does not implement a Count helper; use CountDocuments or EstimatedDocumentCount."), "countDocuments" => new UnifiedCountDocumentsOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "createChangeStream" => new UnifiedCreateChangeStreamOnCollectionOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "createFindCursor" => new UnifiedCreateFindCursorOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), @@ -112,6 +113,7 @@ public IUnifiedTestOperation CreateOperation(string operationName, string target "insertOne" => new UnifiedInsertOneOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "listIndexes" => new UnifiedListIndexesOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "listSearchIndexes" => new UnifiedListSearchIndexesOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), + "mapReduce" => throw new SkipException(".NET/C# driver does not implement a MapReduce helper; use an aggregation pipeline."), "rename" => new UnifiedRenameCollectionOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "replaceOne" => new UnifiedReplaceOneOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "updateMany" => new UnifiedUpdateManyOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), From 2d3676e1d68870e29f8031a4e8f405fa76978861 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Wed, 29 Jul 2026 17:20:47 -0400 Subject: [PATCH 05/13] CSHARP-5996: Remove remaining obsolete members Clears the scattered obsolete members that had no cluster of their own. 29 members across the Bson, Driver and Encryption assemblies: - AggregateOptions.UseCursor and AggregateOperation.UseCursor, along with the backing fields and the two call sites that copied one into the other. Server 3.6 and newer always use a cursor. - ConnectionId.LocalValue / ServerValue, superseded by LongLocalValue and LongServerValue. - ConnectionDescription.ServerVersion and ServerDescription.Version. MaxWireVersion is the supported way to ask what a server can do. - Server.Invalidate(string), replaced by the overload taking a TopologyDescription. - BulkWriteInsertOneResult.InsertedId, superseded by DocumentId. - MongoCredential.Password, superseded by Evidence. PasswordEvidence.ToInsecureString covers the two test call sites. - PipelineDefinition.Serializer, superseded by OutputSerializer. - The two-argument RenderedFieldDefinition constructor. - IAggregateFluent.Unwind and its extension overload that took a serializer instead of AggregateUnwindOptions. - The two BatchableSource constructors taking a bare IEnumerable / IEnumerator. - MessageEncoderSettingsName.GuidRepresentation. - The ServerHeartbeatFailedEvent constructor without a duration. - IServerSession.AdvanceTransactionNumber / WasUsed and their ServerSession implementations. The ICoreServerSession members of the same name are unaffected. - IMongoCollection.InsertOneAsync(TDocument, CancellationToken). - GridFSFileInfo.IdAsBsonValue. Note the "IdAsBsonValue" string stays: it is the registered serializer member name that the live Id property reads _id through. - BsonDocument(params BsonElement[]). - ClientEncryption.CreateEncryptedCollection / Async overloads taking DataKeyOptions rather than a masterKey, plus the tests for validation that only existed on them. - DatabaseNamespace.SystemIndexesCollection / SystemNamespacesCollection, already internal and gone from the server in 4.2. Test-side notes: tests that only exercised a removed member are deleted; tests where the member was incidental are migrated. The libmongocrypt smoke test compared ServerDescription.Version against a semantic version to gate a SERVER-106469 workaround; it now compares MaxWireVersion, spelled out as a constant because WireVersion is internal and that project only sees the public surface. Removing public API is a breaking change and targets the 4.0 major release. --- src/MongoDB.Bson/ObjectModel/BsonDocument.cs | 11 ----- .../ClientEncryption.cs | 42 ------------------ src/MongoDB.Driver/AggregateOptions.cs | 10 ----- .../BulkWriteInsertOneResult.cs | 13 ------ .../Core/Connections/ConnectionDescription.cs | 14 ------ .../Core/Connections/ConnectionId.cs | 24 ----------- src/MongoDB.Driver/Core/DatabaseNamespace.cs | 12 ------ .../Core/Events/ServerHeartbeatFailedEvent.cs | 16 ------- .../Core/Misc/BatchableSource.cs | 24 ----------- .../Core/Operations/AggregateOperation.cs | 14 ------ src/MongoDB.Driver/Core/Servers/Server.cs | 6 --- .../Core/Servers/ServerDescription.cs | 12 ------ .../Encoders/MessageEncoderSettings.cs | 2 - src/MongoDB.Driver/FieldDefinition.cs | 11 ----- .../GridFS/GridFSFileInfoCompat.cs | 12 ------ src/MongoDB.Driver/IAggregateFluent.cs | 12 ------ .../IAggregateFluentExtensions.cs | 18 -------- src/MongoDB.Driver/IServerSession.cs | 14 ------ src/MongoDB.Driver/MongoCollectionImpl.cs | 5 +-- src/MongoDB.Driver/MongoCredential.cs | 18 -------- src/MongoDB.Driver/MongoDatabase.cs | 5 +-- src/MongoDB.Driver/PipelineDefinition.cs | 9 ---- src/MongoDB.Driver/ServerSession.cs | 15 ------- .../ObjectModel/BsonDocumentTests.cs | 21 --------- .../Core/Connections/ConnectionIdTests.cs | 10 ----- .../Core/DatabaseNamespaceTests.cs | 22 ---------- .../Operations/AggregateOperationTests.cs | 43 ------------------- .../Core/Servers/ServerDescriptionTests.cs | 3 -- ...ilarLastUpdateTimestampEqualityComparer.cs | 3 -- .../Encryption/ClientEncryptionTests.cs | 14 ------ .../GridFS/GridFSFileInfoTests.cs | 29 ------------- .../GridFSFileInfoFindProjectionTests.cs | 21 --------- .../MongoCollectionImplTests.cs | 12 ------ .../MongoCredentialTests.cs | 5 +-- .../MongoDatabaseTests.cs | 12 ------ .../ServerSessionTests.cs | 27 ------------ .../Specifications/auth/AuthTestRunner.cs | 5 +-- .../LibmongocryptTests.cs | 17 -------- 38 files changed, 6 insertions(+), 557 deletions(-) diff --git a/src/MongoDB.Bson/ObjectModel/BsonDocument.cs b/src/MongoDB.Bson/ObjectModel/BsonDocument.cs index 653f75bc870..6f07ad1c808 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonDocument.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonDocument.cs @@ -123,17 +123,6 @@ public BsonDocument(IEnumerable elements) AddRange(elements); } - /// - /// Initializes a new instance of the BsonDocument class and adds one or more elements. - /// - /// One or more elements to add to the document. - [Obsolete("Use BsonDocument(IEnumerable elements) instead.")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] - public BsonDocument(params BsonElement[] elements) - { - AddRange(elements); - } - /// /// Initializes a new instance of the BsonDocument class and creates and adds a new element. /// diff --git a/src/MongoDB.Driver.Encryption/ClientEncryption.cs b/src/MongoDB.Driver.Encryption/ClientEncryption.cs index fd75d182f42..8315e2030da 100644 --- a/src/MongoDB.Driver.Encryption/ClientEncryption.cs +++ b/src/MongoDB.Driver.Encryption/ClientEncryption.cs @@ -79,27 +79,6 @@ public BsonDocument AddAlternateKeyName(Guid id, string alternateKeyName, Cancel public Task AddAlternateKeyNameAsync(Guid id, string alternateKeyName, CancellationToken cancellationToken = default) => _libMongoCryptController.AddAlternateKeyNameAsync(id, alternateKeyName, cancellationToken); - /// - /// Create encrypted collection. - /// - /// The database. - /// The collection name. - /// The create collection options. - /// The kms provider. - /// The datakey options. - /// The cancellation token. - /// The operation result. - /// - /// If EncryptionFields contains a keyId with a null value, a data key will be automatically generated and returned in . - /// - [Obsolete("Use the overload with masterKey instead.")] - public CreateEncryptedCollectionResult CreateEncryptedCollection(IMongoDatabase database, string collectionName, CreateCollectionOptions createCollectionOptions, string kmsProvider, DataKeyOptions dataKeyOptions, CancellationToken cancellationToken = default) - { - Ensure.That(dataKeyOptions?.AlternateKeyNames == null && dataKeyOptions?.KeyMaterial == null, $"{nameof(CreateEncryptedCollection)} supports only {nameof(dataKeyOptions.MasterKey)} in {nameof(DataKeyOptions)}."); - - return CreateEncryptedCollection(database, collectionName, createCollectionOptions, kmsProvider, dataKeyOptions?.MasterKey, cancellationToken); - } - /// /// Create encrypted collection. /// @@ -142,27 +121,6 @@ public CreateEncryptedCollectionResult CreateEncryptedCollection(IMongoDatabase return new CreateEncryptedCollectionResult(encryptedFields); } - /// - /// Create encrypted collection. - /// - /// The database. - /// The collection name. - /// The create collection options. - /// The kms provider. - /// The datakey options. - /// The cancellation token. - /// The operation result. - /// - /// If EncryptionFields contains a keyId with a null value, a data key will be automatically generated and returned in . - /// - [Obsolete("Use the overload with masterKey instead.")] - public Task CreateEncryptedCollectionAsync(IMongoDatabase database, string collectionName, CreateCollectionOptions createCollectionOptions, string kmsProvider, DataKeyOptions dataKeyOptions, CancellationToken cancellationToken = default) - { - Ensure.That(dataKeyOptions?.AlternateKeyNames == null && dataKeyOptions?.KeyMaterial == null, $"{nameof(CreateEncryptedCollection)} supports only {nameof(dataKeyOptions.MasterKey)} in {nameof(DataKeyOptions)}."); - - return CreateEncryptedCollectionAsync(database, collectionName, createCollectionOptions, kmsProvider, dataKeyOptions?.MasterKey, cancellationToken); - } - /// /// Create encrypted collection. /// diff --git a/src/MongoDB.Driver/AggregateOptions.cs b/src/MongoDB.Driver/AggregateOptions.cs index 55134aa07d4..a33f96c9dd4 100644 --- a/src/MongoDB.Driver/AggregateOptions.cs +++ b/src/MongoDB.Driver/AggregateOptions.cs @@ -36,7 +36,6 @@ public class AggregateOptions private TimeSpan? _maxTime; private TimeSpan? _timeout; private ExpressionTranslationOptions _translationOptions; - private bool? _useCursor; // implicit conversions /// @@ -147,14 +146,5 @@ public ExpressionTranslationOptions TranslationOptions set { _translationOptions = value; } } - /// - /// Gets or sets a value indicating whether to use a cursor. - /// - [Obsolete("Server versions 3.6 and newer always use a cursor.")] - public bool? UseCursor - { - get { return _useCursor; } - set { _useCursor = value; } - } } } diff --git a/src/MongoDB.Driver/BulkWriteInsertOneResult.cs b/src/MongoDB.Driver/BulkWriteInsertOneResult.cs index 290b65462ed..dc730409475 100644 --- a/src/MongoDB.Driver/BulkWriteInsertOneResult.cs +++ b/src/MongoDB.Driver/BulkWriteInsertOneResult.cs @@ -23,19 +23,6 @@ namespace MongoDB.Driver /// public class BulkWriteInsertOneResult { - /// - /// The id of the inserted document. - /// - [Obsolete("InsertedId is deprecated and will be removed in future versions. Use DocumentId instead.")] - public BsonValue InsertedId - { - get => BsonValue.Create(DocumentId); - init - { - DocumentId = value; - } - } - /// /// The id of the inserted document. /// diff --git a/src/MongoDB.Driver/Core/Connections/ConnectionDescription.cs b/src/MongoDB.Driver/Core/Connections/ConnectionDescription.cs index d4de1486866..06b63b04625 100644 --- a/src/MongoDB.Driver/Core/Connections/ConnectionDescription.cs +++ b/src/MongoDB.Driver/Core/Connections/ConnectionDescription.cs @@ -36,7 +36,6 @@ public sealed class ConnectionDescription : IEquatable private readonly int _maxMessageSize; private readonly int _maxWireVersion; private readonly int _minWireVersion; - private readonly SemanticVersion _serverVersion; private readonly ObjectId? _serviceId; // constructors @@ -57,7 +56,6 @@ public ConnectionDescription(ConnectionId connectionId, HelloResult helloResult) _maxWireVersion = helloResult.MaxWireVersion; _minWireVersion = helloResult.MinWireVersion; _serviceId = helloResult.ServiceId; - _serverVersion = WireVersion.ToServerVersion(_maxWireVersion); } // properties @@ -157,18 +155,6 @@ public int MinWireVersion get { return _minWireVersion; } } - /// - /// Gets the server version. - /// - /// - /// The server version. - /// - [Obsolete("Use MaxWireVersion instead.")] - public SemanticVersion ServerVersion - { - get { return _serverVersion; } - } - /// /// Gets the service identifier. /// diff --git a/src/MongoDB.Driver/Core/Connections/ConnectionId.cs b/src/MongoDB.Driver/Core/Connections/ConnectionId.cs index 6ae0e2d8d09..18b6f684f3a 100644 --- a/src/MongoDB.Driver/Core/Connections/ConnectionId.cs +++ b/src/MongoDB.Driver/Core/Connections/ConnectionId.cs @@ -74,18 +74,6 @@ public ServerId ServerId get { return _serverId; } } - /// - /// Gets the local value. - /// - /// - /// The local value. - /// - [Obsolete("Use LongLocalValue instead.")] - public int LocalValue - { - get { return (int)_localValue; } - } - /// /// Gets the local value. /// @@ -97,18 +85,6 @@ public long LongLocalValue get { return _localValue; } } - /// - /// Gets the server value. - /// - /// - /// The server value. - /// - [Obsolete("Use LongServerValue instead.")] - public int? ServerValue - { - get { return (int?)_serverValue; } - } - /// /// Gets the server value. /// diff --git a/src/MongoDB.Driver/Core/DatabaseNamespace.cs b/src/MongoDB.Driver/Core/DatabaseNamespace.cs index 360a527065b..e2c0e2a8495 100644 --- a/src/MongoDB.Driver/Core/DatabaseNamespace.cs +++ b/src/MongoDB.Driver/Core/DatabaseNamespace.cs @@ -86,18 +86,6 @@ public string DatabaseName get { return _databaseName; } } - [Obsolete("This collection namespace was removed in server version 4.2. As such, this property will be removed in a later release.")] - internal CollectionNamespace SystemIndexesCollection - { - get { return new CollectionNamespace(this, "system.indexes"); } - } - - [Obsolete("This collection namespace was removed in server version 4.2. As such, this property will be removed in a later release.")] - internal CollectionNamespace SystemNamespacesCollection - { - get { return new CollectionNamespace(this, "system.namespaces"); } - } - // methods /// public bool Equals(DatabaseNamespace other) diff --git a/src/MongoDB.Driver/Core/Events/ServerHeartbeatFailedEvent.cs b/src/MongoDB.Driver/Core/Events/ServerHeartbeatFailedEvent.cs index 1ab8bb865cc..acd43c902c9 100644 --- a/src/MongoDB.Driver/Core/Events/ServerHeartbeatFailedEvent.cs +++ b/src/MongoDB.Driver/Core/Events/ServerHeartbeatFailedEvent.cs @@ -31,22 +31,6 @@ public struct ServerHeartbeatFailedEvent : IEvent private readonly Exception _exception; private readonly DateTime _timestamp; - /// - /// Initializes a new instance of the struct. - /// - /// The connection identifier. - /// The exception. - /// The awaited flag. - [Obsolete("Use the other contstructor instead")] - public ServerHeartbeatFailedEvent(ConnectionId connectionId, Exception exception, bool awaited) - { - _awaited = awaited; - _connectionId = connectionId; - _duration = TimeSpan.MinValue; - _exception = exception; - _timestamp = DateTime.UtcNow; - } - /// /// Initializes a new instance of the struct. /// diff --git a/src/MongoDB.Driver/Core/Misc/BatchableSource.cs b/src/MongoDB.Driver/Core/Misc/BatchableSource.cs index 4452dac3a99..85726b51c65 100644 --- a/src/MongoDB.Driver/Core/Misc/BatchableSource.cs +++ b/src/MongoDB.Driver/Core/Misc/BatchableSource.cs @@ -46,30 +46,6 @@ private static IReadOnlyList EnumeratorToList(IEnumerator enumerator) private int _processedCount; // constructors - /// - /// Initializes a new instance of the class. - /// - /// - /// Use this overload when you know the batch is small and won't have to be broken up into sub-batches. - /// In that case using this overload is simpler than using an enumerator and using the other constructor. - /// - /// The single batch. - [Obsolete("Use one of the other constructors instead.")] - public BatchableSource(IEnumerable batch) - : this(Ensure.IsNotNull(batch, nameof(batch)).ToList(), canBeSplit: true) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The enumerator that will provide the items for the batch. - [Obsolete("Use one of the other constructors instead.")] - public BatchableSource(IEnumerator enumerator) - : this(EnumeratorToList(Ensure.IsNotNull(enumerator, nameof(enumerator))), canBeSplit: true) - { - } - /// /// Initializes a new instance of the class. /// diff --git a/src/MongoDB.Driver/Core/Operations/AggregateOperation.cs b/src/MongoDB.Driver/Core/Operations/AggregateOperation.cs index 8b11b541461..f8eba289c87 100644 --- a/src/MongoDB.Driver/Core/Operations/AggregateOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/AggregateOperation.cs @@ -49,7 +49,6 @@ internal sealed class AggregateOperation : IReadOperation _resultSerializer; private bool _retryRequested; - private bool? _useCursor; // constructors /// @@ -275,19 +274,6 @@ public bool RetryRequested public bool IsOperationRetryable => true; - /// - /// Gets or sets a value indicating whether the server should use a cursor to return the results. - /// - /// - /// A value indicating whether the server should use a cursor to return the results. - /// - [Obsolete("Server versions 3.6 and newer always use a cursor.")] - public bool? UseCursor - { - get { return _useCursor; } - set { _useCursor = value; } - } - // methods /// public IAsyncCursor Execute(OperationContext operationContext, IReadBinding binding) diff --git a/src/MongoDB.Driver/Core/Servers/Server.cs b/src/MongoDB.Driver/Core/Servers/Server.cs index 62c44c3866e..e7b17bfe02a 100644 --- a/src/MongoDB.Driver/Core/Servers/Server.cs +++ b/src/MongoDB.Driver/Core/Servers/Server.cs @@ -184,12 +184,6 @@ public void Initialize() } } - [Obsolete("Use Invalidate with TopologyDescription instead.")] - public void Invalidate(string reasonInvalidated) - { - Invalidate(reasonInvalidated, responseTopologyDescription: null); - } - public void Invalidate(string reasonInvalidated, TopologyVersion responseTopologyDescription) { Invalidate(reasonInvalidated, clearConnectionPool: true, responseTopologyDescription); diff --git a/src/MongoDB.Driver/Core/Servers/ServerDescription.cs b/src/MongoDB.Driver/Core/Servers/ServerDescription.cs index 8f199fda1e9..d8131a2560a 100644 --- a/src/MongoDB.Driver/Core/Servers/ServerDescription.cs +++ b/src/MongoDB.Driver/Core/Servers/ServerDescription.cs @@ -480,18 +480,6 @@ public TopologyVersion TopologyVersion get { return _topologyVersion; } } - /// - /// Gets the approximate server version (only the major and minor version numbers are known). - /// - /// - /// The server version. - /// - [Obsolete("This property will be removed in a later release.")] - public SemanticVersion Version - { - get { return _version; } - } - /// /// Gets the wire version range. /// diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/MessageEncoderSettings.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/MessageEncoderSettings.cs index 30c2e33e2fd..73cfea12103 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/MessageEncoderSettings.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/MessageEncoderSettings.cs @@ -24,8 +24,6 @@ internal static class MessageEncoderSettingsName // encoder settings used by the binary encoders public const string BinaryDocumentFieldDecryptor = nameof(BinaryDocumentFieldDecryptor); public const string BinaryDocumentFieldEncryptor = nameof(BinaryDocumentFieldEncryptor); - [Obsolete("Configure serializers instead.")] - public const string GuidRepresentation = nameof(GuidRepresentation); public const string MaxDocumentSize = nameof(MaxDocumentSize); public const string MaxMessageSize = nameof(MaxMessageSize); public const string MaxSerializationDepth = nameof(MaxSerializationDepth); diff --git a/src/MongoDB.Driver/FieldDefinition.cs b/src/MongoDB.Driver/FieldDefinition.cs index 363ecc91bef..c1860cdad36 100644 --- a/src/MongoDB.Driver/FieldDefinition.cs +++ b/src/MongoDB.Driver/FieldDefinition.cs @@ -86,17 +86,6 @@ public sealed class RenderedFieldDefinition private readonly IBsonSerializer _underlyingSerializer; private readonly IBsonSerializer _valueSerializer; - /// - /// Initializes a new instance of the class. - /// - /// The field name. - /// The field serializer. - [Obsolete("Use the constructor that takes 4 arguments instead.")] - public RenderedFieldDefinition(string fieldName, IBsonSerializer fieldSerializer) - : this(fieldName, fieldSerializer, fieldSerializer, fieldSerializer) - { - } - /// /// Initializes a new instance of the class. /// diff --git a/src/MongoDB.Driver/GridFS/GridFSFileInfoCompat.cs b/src/MongoDB.Driver/GridFS/GridFSFileInfoCompat.cs index 73911fe58dd..c43c7fe8077 100644 --- a/src/MongoDB.Driver/GridFS/GridFSFileInfoCompat.cs +++ b/src/MongoDB.Driver/GridFS/GridFSFileInfoCompat.cs @@ -84,18 +84,6 @@ public ObjectId Id get { return GetValue("IdAsBsonValue").AsObjectId; } } - /// - /// Gets the identifier as a BsonValue. - /// - /// - /// The identifier as a BsonValue. - /// - [Obsolete("All new GridFS files should use an ObjectId as the Id.")] - public BsonValue IdAsBsonValue - { - get { return GetValue("IdAsBsonValue"); } - } - /// /// Gets the length. /// diff --git a/src/MongoDB.Driver/IAggregateFluent.cs b/src/MongoDB.Driver/IAggregateFluent.cs index e8d825c66e6..9800cae18e4 100644 --- a/src/MongoDB.Driver/IAggregateFluent.cs +++ b/src/MongoDB.Driver/IAggregateFluent.cs @@ -524,18 +524,6 @@ IAggregateFluent UnionWith( IMongoCollection withCollection, PipelineDefinition withPipeline = null); - /// - /// Appends an unwind stage to the pipeline. - /// - /// The type of the result of the stage. - /// The field. - /// The new result serializer. - /// - /// The fluent aggregate interface. - /// - [Obsolete("Use the Unwind overload which takes an options parameter.")] - IAggregateFluent Unwind(FieldDefinition field, IBsonSerializer newResultSerializer); - /// /// Appends an unwind stage to the pipeline. /// diff --git a/src/MongoDB.Driver/IAggregateFluentExtensions.cs b/src/MongoDB.Driver/IAggregateFluentExtensions.cs index 810c8b52c9e..ec6f8890f45 100644 --- a/src/MongoDB.Driver/IAggregateFluentExtensions.cs +++ b/src/MongoDB.Driver/IAggregateFluentExtensions.cs @@ -1034,24 +1034,6 @@ public static IAggregateFluent Unwind(this IAggregateFlue return aggregate.AppendStage(PipelineStageDefinitionBuilder.Unwind(field)); } - /// - /// Appends an unwind stage to the pipeline. - /// - /// The type of the result. - /// The type of the new result. - /// The aggregate. - /// The field to unwind. - /// The new result serializer. - /// - /// The fluent aggregate interface. - /// - [Obsolete("Use the Unwind overload which takes an options parameter.")] - public static IAggregateFluent Unwind(this IAggregateFluent aggregate, Expression> field, IBsonSerializer newResultSerializer) - { - Ensure.IsNotNull(aggregate, nameof(aggregate)); - return aggregate.AppendStage(PipelineStageDefinitionBuilder.Unwind(field, new AggregateUnwindOptions { ResultSerializer = newResultSerializer })); - } - /// /// Appends an unwind stage to the pipeline. /// diff --git a/src/MongoDB.Driver/IServerSession.cs b/src/MongoDB.Driver/IServerSession.cs index f2ea7989f5d..289c8948323 100644 --- a/src/MongoDB.Driver/IServerSession.cs +++ b/src/MongoDB.Driver/IServerSession.cs @@ -39,19 +39,5 @@ public interface IServerSession : IDisposable /// The time this server session was last used (in UTC). /// DateTime? LastUsedAt { get; } - - /// - /// Gets the next transaction number. - /// - /// The transaction number. - [Obsolete("Let the driver handle when to advance the transaction number.")] - long AdvanceTransactionNumber(); - - // methods - /// - /// Called by the driver when the session is used (i.e. sent to the server). - /// - [Obsolete("Let the driver handle tracking when the session was last used.")] - void WasUsed(); } } diff --git a/src/MongoDB.Driver/MongoCollectionImpl.cs b/src/MongoDB.Driver/MongoCollectionImpl.cs index 8f6abe2525b..4dca0a41124 100644 --- a/src/MongoDB.Driver/MongoCollectionImpl.cs +++ b/src/MongoDB.Driver/MongoCollectionImpl.cs @@ -670,10 +670,7 @@ private AggregateOperation CreateAggregateOperation(RenderedPi MaxAwaitTime = options.MaxAwaitTime, MaxTime = options.MaxTime, ReadConcern = _settings.ReadConcern, - RetryRequested = _database.Client.Settings.RetryReads, -#pragma warning disable 618 - UseCursor = options.UseCursor -#pragma warning restore 618 + RetryRequested = _database.Client.Settings.RetryReads }; } diff --git a/src/MongoDB.Driver/MongoCredential.cs b/src/MongoDB.Driver/MongoCredential.cs index 92d6c48ba18..ab2c0813b1d 100644 --- a/src/MongoDB.Driver/MongoCredential.cs +++ b/src/MongoDB.Driver/MongoCredential.cs @@ -85,24 +85,6 @@ public string Mechanism get { return _mechanism; } } - /// - /// Gets the password. - /// - [Obsolete("Use Evidence instead.")] - public string Password - { - get - { - var passwordEvidence = _evidence as PasswordEvidence; - if (passwordEvidence != null) - { - return SecureStringHelper.ToInsecureString(passwordEvidence.SecurePassword); - } - - return null; - } - } - /// /// Gets the source. /// diff --git a/src/MongoDB.Driver/MongoDatabase.cs b/src/MongoDB.Driver/MongoDatabase.cs index 35547290b4a..dfce9d7cfd9 100644 --- a/src/MongoDB.Driver/MongoDatabase.cs +++ b/src/MongoDB.Driver/MongoDatabase.cs @@ -551,10 +551,7 @@ private AggregateOperation CreateAggregateOperation(RenderedPi MaxAwaitTime = options.MaxAwaitTime, MaxTime = options.MaxTime, ReadConcern = _settings.ReadConcern, - RetryRequested = _client.Settings.RetryReads, -#pragma warning disable 618 - UseCursor = options.UseCursor -#pragma warning restore 618 + RetryRequested = _client.Settings.RetryReads }; } diff --git a/src/MongoDB.Driver/PipelineDefinition.cs b/src/MongoDB.Driver/PipelineDefinition.cs index fbd9d69d5c0..1872fc5ec8e 100644 --- a/src/MongoDB.Driver/PipelineDefinition.cs +++ b/src/MongoDB.Driver/PipelineDefinition.cs @@ -307,15 +307,6 @@ public PipelineStagePipelineDefinition(IEnumerable sta /// public override IBsonSerializer OutputSerializer => _outputSerializer; - /// - /// Gets the serializer. - /// - [Obsolete("Use OutputSerializer instead.")] - public IBsonSerializer Serializer - { - get { return _outputSerializer; } - } - /// /// Gets the stages. /// diff --git a/src/MongoDB.Driver/ServerSession.cs b/src/MongoDB.Driver/ServerSession.cs index 6174172681a..5da26747886 100644 --- a/src/MongoDB.Driver/ServerSession.cs +++ b/src/MongoDB.Driver/ServerSession.cs @@ -44,25 +44,10 @@ public ServerSession(ICoreServerSession coreServerSession) public DateTime? LastUsedAt => _coreServerSession.LastUsedAt; // public methods - /// - [Obsolete("Let the driver handle when to advance the transaction number.")] - public long AdvanceTransactionNumber() - { - // do nothing - return -1; - } - /// public void Dispose() { // do nothing (the ServerSession does NOT own the wrapped core server session) } - - /// - [Obsolete("Let the driver handle tracking when the session was last used.")] - public void WasUsed() - { - // do nothing - } } } diff --git a/tests/MongoDB.Bson.Tests/ObjectModel/BsonDocumentTests.cs b/tests/MongoDB.Bson.Tests/ObjectModel/BsonDocumentTests.cs index 8944dec3f66..4c35cc5689b 100644 --- a/tests/MongoDB.Bson.Tests/ObjectModel/BsonDocumentTests.cs +++ b/tests/MongoDB.Bson.Tests/ObjectModel/BsonDocumentTests.cs @@ -227,27 +227,6 @@ public void TestConstructorElementsDocumentDuplicateNames() documentB.Elements.ShouldAllBeEquivalentTo(documentA.Elements); } - [Fact] - public void TestConstructorElementsParams() - { - var element1 = new BsonElement("x", 1); - var element2 = new BsonElement("y", 2); -#pragma warning disable 618 - var document = new BsonDocument(element1, element2); -#pragma warning restore - Assert.False(document.AllowDuplicateNames); - Assert.Equal(2, document.ElementCount); - Assert.Equal(2, document.ElementCount); - Assert.Equal(1, document["x"].AsInt32); - Assert.Equal(2, document["y"].AsInt32); - Assert.Equal(true, document.Contains("x")); - Assert.Equal(true, document.Contains("y")); - Assert.Equal(true, document.ContainsValue(1)); - Assert.Equal(true, document.ContainsValue(2)); - Assert.Same(element1.Value, document.GetElement("x").Value); - Assert.Same(element2.Value, document.GetElement("y").Value); - } - [Fact] public void TestConstructorDictionaryGeneric() { diff --git a/tests/MongoDB.Driver.Tests/Core/Connections/ConnectionIdTests.cs b/tests/MongoDB.Driver.Tests/Core/Connections/ConnectionIdTests.cs index 860d654b00e..94fc04351c3 100644 --- a/tests/MongoDB.Driver.Tests/Core/Connections/ConnectionIdTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Connections/ConnectionIdTests.cs @@ -92,16 +92,6 @@ public void LongLocalValue_should_be_what_was_specified_in_the_constructor(long subject.LongLocalValue.Should().Be(localValue); } - [Fact] - public void ServerValue_should_return_null_when_null() - { - var subject = new ConnectionId(__serverId, 10); - -#pragma warning disable CS0618 // Type or member is obsolete - subject.ServerValue.ShouldBeEquivalentTo(null); -#pragma warning restore CS0618 // Type or member is obsolete - } - [Theory] [InlineData(0)] [InlineData(int.MaxValue)] diff --git a/tests/MongoDB.Driver.Tests/Core/DatabaseNamespaceTests.cs b/tests/MongoDB.Driver.Tests/Core/DatabaseNamespaceTests.cs index f6496a91181..04f7013f0bc 100644 --- a/tests/MongoDB.Driver.Tests/Core/DatabaseNamespaceTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/DatabaseNamespaceTests.cs @@ -71,28 +71,6 @@ public void DatabaseName_should_report_the_provided_database_name() subject.DatabaseName.Should().Be("test"); } - [Fact] - public void SystemIndexesCollection_should_return_the_system_indexes_collection() - { - var subject = new DatabaseNamespace("test"); - -#pragma warning disable CS0618 // Type or member is obsolete - var commandCollection = subject.SystemIndexesCollection; -#pragma warning restore CS0618 // Type or member is obsolete - commandCollection.FullName.Should().Be("test.system.indexes"); - } - - [Fact] - public void SystemNamespacesCollection_should_return_the_system_namespaces_collection() - { - var subject = new DatabaseNamespace("test"); - -#pragma warning disable CS0618 // Type or member is obsolete - var commandCollection = subject.SystemNamespacesCollection; -#pragma warning restore CS0618 // Type or member is obsolete - commandCollection.FullName.Should().Be("test.system.namespaces"); - } - [Theory] [InlineData("one", "one", true)] [InlineData("one", "two", false)] diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/AggregateOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/AggregateOperationTests.cs index d3f97137ce4..cbbb87964e6 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/AggregateOperationTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/AggregateOperationTests.cs @@ -51,9 +51,6 @@ public void Constructor_with_database_should_create_a_valid_instance() subject.MaxAwaitTime.Should().NotHaveValue(); subject.MaxTime.Should().NotHaveValue(); subject.ReadConcern.IsServerDefault.Should().BeTrue(); -#pragma warning disable 618 - subject.UseCursor.Should().NotHaveValue(); -#pragma warning restore 618 subject.RetryRequested.Should().BeFalse(); } @@ -110,9 +107,6 @@ public void Constructor_with_collection_should_create_a_valid_instance() subject.MaxAwaitTime.Should().NotHaveValue(); subject.MaxTime.Should().NotHaveValue(); subject.ReadConcern.IsServerDefault.Should().BeTrue(); -#pragma warning disable 618 - subject.UseCursor.Should().NotHaveValue(); -#pragma warning restore 618 subject.RetryRequested.Should().BeFalse(); } @@ -287,19 +281,6 @@ public void RetryRequested_get_and_set_should_work( result.Should().Be(value); } - [Fact] - public void UseCursor_get_and_set_should_work() - { - var subject = new AggregateOperation(_collectionNamespace, __pipeline, __resultSerializer, _messageEncoderSettings); - -#pragma warning disable 618 - subject.UseCursor = true; - var result = subject.UseCursor; -#pragma warning restore 618 - - result.Should().BeTrue(); - } - [Fact] public void CreateCommand_should_return_the_expected_result() { @@ -892,30 +873,6 @@ public void Execute_should_return_expected_result_when_ReadConcern_is_set( result.Should().HaveCount(1); } - [Theory] - [ParameterAttributeData] - public void Execute_should_return_expected_result_when_UseCursor_is_set( - [Values(null, false, true)] - bool? useCursor, - [Values(false, true)] - bool async) - { - RequireServer.Check(); - EnsureTestData(); - var subject = new AggregateOperation(_collectionNamespace, __pipeline, __resultSerializer, _messageEncoderSettings) - { -#pragma warning disable 618 - UseCursor = useCursor -#pragma warning restore 618 - }; - - var cursor = ExecuteOperation(subject, async); - var result = ReadCursorToEnd(cursor, async); - - result.Should().NotBeNull(); - result.Should().HaveCount(1); - } - [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( diff --git a/tests/MongoDB.Driver.Tests/Core/Servers/ServerDescriptionTests.cs b/tests/MongoDB.Driver.Tests/Core/Servers/ServerDescriptionTests.cs index d013497a819..6ecae16d216 100644 --- a/tests/MongoDB.Driver.Tests/Core/Servers/ServerDescriptionTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Servers/ServerDescriptionTests.cs @@ -57,9 +57,6 @@ public void Constructor_with_serverId_and_endPoint_only_should_return_disconnect subject.State.Should().Be(ServerState.Disconnected); subject.Tags.Should().BeNull(); subject.Type.Should().Be(ServerType.Unknown); -#pragma warning disable CS0618 // Type or member is obsolete - subject.Version.Should().BeNull(); -#pragma warning restore CS0618 // Type or member is obsolete subject.WireVersionRange.Should().BeNull(); } diff --git a/tests/MongoDB.Driver.Tests/Core/Servers/ServerDescriptionWithSimilarLastUpdateTimestampEqualityComparer.cs b/tests/MongoDB.Driver.Tests/Core/Servers/ServerDescriptionWithSimilarLastUpdateTimestampEqualityComparer.cs index af26e88fa40..1a534e1cdb8 100644 --- a/tests/MongoDB.Driver.Tests/Core/Servers/ServerDescriptionWithSimilarLastUpdateTimestampEqualityComparer.cs +++ b/tests/MongoDB.Driver.Tests/Core/Servers/ServerDescriptionWithSimilarLastUpdateTimestampEqualityComparer.cs @@ -47,9 +47,6 @@ public bool Equals(ServerDescription x, ServerDescription y) x.State.Equals(y.State) && object.Equals(x.Tags, y.Tags) && x.Type.Equals(y.Type) && -#pragma warning disable CS0618 // Type or member is obsolete - object.Equals(x.Version, y.Version) && -#pragma warning restore CS0618 // Type or member is obsolete object.Equals(x.WireVersionRange, y.WireVersionRange); } diff --git a/tests/MongoDB.Driver.Tests/Encryption/ClientEncryptionTests.cs b/tests/MongoDB.Driver.Tests/Encryption/ClientEncryptionTests.cs index 1c3a1c0b330..7e475623c47 100644 --- a/tests/MongoDB.Driver.Tests/Encryption/ClientEncryptionTests.cs +++ b/tests/MongoDB.Driver.Tests/Encryption/ClientEncryptionTests.cs @@ -94,20 +94,6 @@ public async Task CreateEncryptedCollection_should_handle_input_arguments() ShouldBeArgumentNullException(Record.Exception(() => subject.CreateEncryptedCollection(database, collectionName: collectionName, createCollectionOptions, kmsProvider: null, masterKey)), expectedParamName: "kmsProvider"); ShouldBeArgumentNullException(await Record.ExceptionAsync(() => subject.CreateEncryptedCollectionAsync(database, collectionName, createCollectionOptions, kmsProvider: null, masterKey)), expectedParamName: "kmsProvider"); - - var invalidDataKeyOptions = new DataKeyOptions(alternateKeyNames: Optional.Create(Mock.Of>())); -#pragma warning disable CS0618 // Type or member is obsolete - Record.Exception(() => subject.CreateEncryptedCollection(database, collectionName: collectionName, createCollectionOptions, kmsProvider, dataKeyOptions: invalidDataKeyOptions)) - .Should().BeOfType().Which.Message.Should().Be("CreateEncryptedCollection supports only MasterKey in DataKeyOptions."); - (await Record.ExceptionAsync(() => subject.CreateEncryptedCollectionAsync(database, collectionName, createCollectionOptions, kmsProvider, dataKeyOptions: invalidDataKeyOptions))) - .Should().BeOfType().Which.Message.Should().Be("CreateEncryptedCollection supports only MasterKey in DataKeyOptions."); - - invalidDataKeyOptions = new DataKeyOptions(keyMaterial: new BsonBinaryData(new byte[0])); - Record.Exception(() => subject.CreateEncryptedCollection(database, collectionName: collectionName, createCollectionOptions, kmsProvider, dataKeyOptions: invalidDataKeyOptions)) - .Should().BeOfType().Which.Message.Should().Be("CreateEncryptedCollection supports only MasterKey in DataKeyOptions."); - (await Record.ExceptionAsync(() => subject.CreateEncryptedCollectionAsync(database, collectionName, createCollectionOptions, kmsProvider, dataKeyOptions: invalidDataKeyOptions))) - .Should().BeOfType().Which.Message.Should().Be("CreateEncryptedCollection supports only MasterKey in DataKeyOptions."); -#pragma warning restore CS0618 // Type or member is obsolete } } diff --git a/tests/MongoDB.Driver.Tests/GridFS/GridFSFileInfoTests.cs b/tests/MongoDB.Driver.Tests/GridFS/GridFSFileInfoTests.cs index c9f0d692405..077dbef6e43 100644 --- a/tests/MongoDB.Driver.Tests/GridFS/GridFSFileInfoTests.cs +++ b/tests/MongoDB.Driver.Tests/GridFS/GridFSFileInfoTests.cs @@ -145,35 +145,6 @@ public void Id_should_be_deserialized_correctly() var result = DeserializeFilesCollectionDocument(document); result.Id.Should().Be(document["_id"].AsObjectId); -#pragma warning disable 618 - result.IdAsBsonValue.Should().Be(document["_id"]); -#pragma warning restore - } - - [Fact] - public void Id_should_be_deserialized_correctly_when_id_is_not_an_ObjectId() - { - var document = CreateFilesCollectionDocument(); - document["_id"] = 123; - - var result = DeserializeFilesCollectionDocument(document); - -#pragma warning disable 618 - result.IdAsBsonValue.Should().Be(document["_id"]); -#pragma warning restore - } - - [Fact] - public void IdAsBsonValue_get_should_return_the_expected_result() - { - var value = (BsonValue)123; - var subject = CreateSubject(idAsBsonValue: value); - -#pragma warning disable 618 - var result = subject.IdAsBsonValue; -#pragma warning restore - - result.Should().Be(value); } [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Integration/GridFSFileInfoFindProjectionTests.cs b/tests/MongoDB.Driver.Tests/Linq/Integration/GridFSFileInfoFindProjectionTests.cs index 2f3588b5c5e..de35196d3a4 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Integration/GridFSFileInfoFindProjectionTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Integration/GridFSFileInfoFindProjectionTests.cs @@ -49,27 +49,6 @@ public void Project_Id_should_work() result.Should().Be(ObjectId.Parse("111111111111111111111111")); } -#pragma warning disable CS0618 // Type or member is obsolete - [Fact] - public void Project_IdAsBsonValue_should_work() - { - var collection = Fixture.Collection; - - var find = collection - .Find(Builders.Filter.Where(x => x.IdAsBsonValue == new BsonObjectId(ObjectId.Parse("111111111111111111111111")))) - .Project(x => x.IdAsBsonValue); - - var filter = TranslateFindFilter(collection, find); - filter.Should().Be("{ _id : { $oid : '111111111111111111111111' } }"); - - var projection = TranslateFindProjection(collection, find); - projection.Should().Be("{ _id : 1 }"); - - var result = find.Single(); - result.Should().Be(ObjectId.Parse("111111111111111111111111")); - } -#pragma warning restore CS0618 // Type or member is obsolete - public sealed class ClassFixture : MongoCollectionFixture { protected override IEnumerable InitialData => diff --git a/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs b/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs index 20bd60699e8..ad0178fc1dc 100644 --- a/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs @@ -94,9 +94,6 @@ public void Aggregate_should_execute_an_AggregateOperation_when_out_is_not_speci Let = new BsonDocument("y", "z"), MaxAwaitTime = TimeSpan.FromSeconds(4), MaxTime = TimeSpan.FromSeconds(3), -#pragma warning disable 618 - UseCursor = false -#pragma warning restore 618 }; using var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; @@ -142,9 +139,6 @@ public void Aggregate_should_execute_an_AggregateOperation_when_out_is_not_speci operation.ReadConcern.Should().Be(_readConcern); operation.RetryRequested.Should().BeTrue(); operation.ResultSerializer.Should().BeSameAs(renderedPipeline.OutputSerializer); -#pragma warning disable 618 - operation.UseCursor.Should().Be(options.UseCursor); -#pragma warning restore 618 } [Theory] @@ -174,9 +168,6 @@ public void Aggregate_should_execute_an_AggregateToCollectionOperation_and_a_Fin Hint = new BsonDocument("x", 1), Let = new BsonDocument("y", "z"), MaxTime = TimeSpan.FromSeconds(3), -#pragma warning disable 618 - UseCursor = false -#pragma warning restore 618 }; using var cancellationTokenSource1 = new CancellationTokenSource(); @@ -346,9 +337,6 @@ public void AggregateToCollection_should_execute_an_AggregateToCollectionOperati Hint = new BsonDocument("x", 1), Let = new BsonDocument("y", "z"), MaxTime = TimeSpan.FromSeconds(3), -#pragma warning disable 618 - UseCursor = false -#pragma warning restore 618 }; using var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; diff --git a/tests/MongoDB.Driver.Tests/MongoCredentialTests.cs b/tests/MongoDB.Driver.Tests/MongoCredentialTests.cs index 65bc3016d43..0987a74f340 100644 --- a/tests/MongoDB.Driver.Tests/MongoCredentialTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoCredentialTests.cs @@ -113,9 +113,8 @@ public void TestEquals() public void TestPassword() { var credentials = MongoCredential.CreateCredential("database", "username", "password"); -#pragma warning disable 618 - Assert.Equal("password", credentials.Password); -#pragma warning restore 618 + var passwordEvidence = credentials.Evidence.Should().BeOfType().Subject; + Assert.Equal("password", passwordEvidence.ToInsecureString()); } [Fact] diff --git a/tests/MongoDB.Driver.Tests/MongoDatabaseTests.cs b/tests/MongoDB.Driver.Tests/MongoDatabaseTests.cs index 45ce1436646..fc03df63cfa 100644 --- a/tests/MongoDB.Driver.Tests/MongoDatabaseTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoDatabaseTests.cs @@ -87,9 +87,6 @@ public void Aggregate_should_execute_an_AggregateOperation_when_out_is_not_speci Let = new BsonDocument("y", "z"), MaxAwaitTime = TimeSpan.FromSeconds(4), MaxTime = TimeSpan.FromSeconds(3), -#pragma warning disable 618 - UseCursor = false -#pragma warning restore 618 }; using var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; @@ -136,9 +133,6 @@ public void Aggregate_should_execute_an_AggregateOperation_when_out_is_not_speci operation.ReadConcern.Should().Be(subject.Settings.ReadConcern); operation.RetryRequested.Should().BeTrue(); operation.ResultSerializer.Should().BeSameAs(renderedPipeline.OutputSerializer); -#pragma warning disable 618 - operation.UseCursor.Should().Be(options.UseCursor); -#pragma warning restore 618 } [Theory] @@ -166,9 +160,6 @@ public void Aggregate_should_execute_an_AggregateToCollectionOperation_and_a_Fin Hint = new BsonDocument("x", 1), Let = new BsonDocument("y", "z"), MaxTime = TimeSpan.FromSeconds(3), -#pragma warning disable 618 - UseCursor = false -#pragma warning restore 618 }; var cancellationToken1 = new CancellationTokenSource().Token; var cancellationToken2 = new CancellationTokenSource().Token; @@ -282,9 +273,6 @@ public void AggregateToCollection_should_execute_an_AggregateToCollectionOperati Hint = new BsonDocument("x", 1), Let = new BsonDocument("y", "z"), MaxTime = TimeSpan.FromSeconds(3), -#pragma warning disable 618 - UseCursor = false -#pragma warning restore 618 }; using var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; diff --git a/tests/MongoDB.Driver.Tests/ServerSessionTests.cs b/tests/MongoDB.Driver.Tests/ServerSessionTests.cs index 7d99b8ab3e1..778e1e5501c 100644 --- a/tests/MongoDB.Driver.Tests/ServerSessionTests.cs +++ b/tests/MongoDB.Driver.Tests/ServerSessionTests.cs @@ -71,20 +71,6 @@ public void LastUsedAt_should_call_coreServerSession_LastUsedAt() mockCoreServerSession.VerifyGet(m => m.LastUsedAt, Times.Once); } - [Fact] - public void AdvanceTransactionNumber_should_do_nothing() - { - Mock mockCoreServerSession; - var subject = CreateSubject(out mockCoreServerSession); - -#pragma warning disable 618 - var result = subject.AdvanceTransactionNumber(); -#pragma warning restore - - result.Should().Be(-1); - mockCoreServerSession.Verify(m => m.AdvanceTransactionNumber(), Times.Never); - } - [Fact] public void Dispose_should_not_call_coreServerSession_Dispose() { @@ -96,19 +82,6 @@ public void Dispose_should_not_call_coreServerSession_Dispose() mockCoreServerSession.Verify(m => m.Dispose(), Times.Never); } - [Fact] - public void WasUsed_should_do_nothing() - { - Mock mockCoreServerSession; - var subject = CreateSubject(out mockCoreServerSession); - -#pragma warning disable 618 - subject.WasUsed(); -#pragma warning restore - - mockCoreServerSession.Verify(m => m.WasUsed(), Times.Never); - } - // private methods private ServerSession CreateSubject(out Mock mockCoreServerSession) { diff --git a/tests/MongoDB.Driver.Tests/Specifications/auth/AuthTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/auth/AuthTestRunner.cs index 67194200a53..df243e3d462 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/auth/AuthTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/auth/AuthTestRunner.cs @@ -119,9 +119,8 @@ private void AssertValid(IAuthenticator authenticator, MongoCredential mongoCred { JsonDrivenHelper.EnsureAllFieldsAreValid(expectedCredential, "username", "password", "source", "mechanism", "mechanism_properties"); mongoCredential.Username.Should().Be(ValueToString(expectedCredential["username"])); -#pragma warning disable 618 - mongoCredential.Password.Should().Be(ValueToString(expectedCredential["password"])); -#pragma warning restore 618 + var actualPassword = (mongoCredential.Evidence as PasswordEvidence)?.ToInsecureString(); + actualPassword.Should().Be(ValueToString(expectedCredential["password"])); mongoCredential.Source.Should().Be(ValueToString(expectedCredential["source"])); mongoCredential.Mechanism.Should().Be(ValueToString(expectedCredential["mechanism"])); diff --git a/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LibmongocryptTests.cs b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LibmongocryptTests.cs index 0aab076d304..a6012a4ed7a 100644 --- a/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LibmongocryptTests.cs +++ b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LibmongocryptTests.cs @@ -15,9 +15,7 @@ using System; using System.Collections.Generic; -using System.Runtime.InteropServices; using System.Threading; -using FluentAssertions; using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Driver.Core.Configuration; @@ -110,21 +108,6 @@ public void Explicit_encryption_with_libmongocrypt_package_works() var result = collection.Find(FilterDefinition.Empty).First(); _output.WriteLine(result.ToJson()); } - catch (Exception ex) - { - // SERVER-106469 -#pragma warning disable CS0618 // Type or member is obsolete - var serverVersion = client.Cluster.Description.Servers[0].Version; -#pragma warning restore CS0618 // Type or member is obsolete - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && - serverVersion >= new SemanticVersion(8, 1, 9999)) - { - ex.Should().BeOfType(); - return; - } - - throw; - } finally { ClusterRegistry.Instance.UnregisterAndDisposeCluster(client.Cluster); From b9fbee91a176ba4fbd34a7e22ead6a944441ade1 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Wed, 29 Jul 2026 18:18:16 -0400 Subject: [PATCH 06/13] CSHARP-5996: Remove obsolete Ssl properties in favour of Tls Removes the eight SSL-named properties that were thin aliases over the TLS fields: - ConnectionString.Ssl and SslVerifyCertificate (use Tls and TlsInsecure) - MongoUrl.UseSsl and VerifySslCertificate - MongoUrlBuilder.UseSsl and VerifySslCertificate - MongoClientSettings.UseSsl and VerifySslCertificate (use UseTls and AllowInsecureTls) Each read or wrote the same backing field as the TLS-named property declared immediately beside it, so no behaviour is defined by these members. Connection string parsing is deliberately untouched. The ssl and sslVerifyCertificate keywords still parse exactly as before: their handlers write _tls and _tlsInsecure, which the non-obsolete Tls and TlsInsecure properties expose. The ssl keyword is required by the URI options spec and shares a case with tls. No connection string that worked before behaves differently. Tests are migrated rather than dropped wherever they carried coverage the TLS-named properties did not already have: - ConnectionStringTests' ssl and sslVerifyCertificate keyword tests now assert through Tls and TlsInsecure, keeping the proof that both keywords are honoured. Note sslVerifyCertificate is the inverse of tlsInsecure. - The connection-string spec runner's "ssl" case asserts against Tls, so the spec fixtures still verify the option. - MongoClientSettingsTests' defaults and clone-equality tests had no AllowInsecureTls coverage, so their VerifySslCertificate assertions were converted rather than deleted. - TestUseSsl and TestVerifySslCertificate are deleted: TestUseTls and TestAllowInsecureTls already cover the same ground, including the SslSettings.CheckCertificateRevocation side effect. WaitQueueSize, WaitQueueMultiple and ComputedWaitQueueSize are left in place. They have no replacement to migrate to and need their own investigation. Removing public API is a breaking change and targets the 4.0 major release. --- .../Core/Configuration/ConnectionString.cs | 15 ----- src/MongoDB.Driver/MongoClientSettings.cs | 29 ---------- src/MongoDB.Driver/MongoUrl.cs | 12 ---- src/MongoDB.Driver/MongoUrlBuilder.cs | 20 ------- .../Configuration/ConnectionStringTests.cs | 18 ++---- .../MongoClientSettingsTests.cs | 58 +------------------ .../MongoUrlBuilderTests.cs | 18 ------ tests/MongoDB.Driver.Tests/MongoUrlTests.cs | 6 -- .../ConnectionStringTestRunner.cs | 5 +- 9 files changed, 8 insertions(+), 173 deletions(-) diff --git a/src/MongoDB.Driver/Core/Configuration/ConnectionString.cs b/src/MongoDB.Driver/Core/Configuration/ConnectionString.cs index 7c8ee885d8d..2513d797167 100644 --- a/src/MongoDB.Driver/Core/Configuration/ConnectionString.cs +++ b/src/MongoDB.Driver/Core/Configuration/ConnectionString.cs @@ -499,21 +499,6 @@ public TimeSpan? SocketTimeout /// public string SrvServiceName => _srvServiceName; - /// - /// Gets whether to use SSL. - /// - [Obsolete("Use Tls instead.")] - public bool? Ssl - { - get { return _tls; } - } - - /// - /// Gets whether to verify SSL certificates. - /// - [Obsolete("Use TlsInsecure instead.")] - public bool? SslVerifyCertificate => !_tlsInsecure; - /// /// Gets the per-operation timeout. /// diff --git a/src/MongoDB.Driver/MongoClientSettings.cs b/src/MongoDB.Driver/MongoClientSettings.cs index c601821ce8c..9203f292904 100644 --- a/src/MongoDB.Driver/MongoClientSettings.cs +++ b/src/MongoDB.Driver/MongoClientSettings.cs @@ -772,20 +772,6 @@ public ExpressionTranslationOptions TranslationOptions } } - /// - /// Gets or sets a value indicating whether to use SSL. - /// - [Obsolete("Use UseTls instead.")] - public bool UseSsl - { - get { return _useTls; } - set - { - if (_isFrozen) { throw new InvalidOperationException("MongoClientSettings is frozen."); } - _useTls = value; - } - } - /// /// Gets or sets a value indicating whether to use TLS. /// @@ -799,21 +785,6 @@ public bool UseTls } } - /// - /// Gets or sets a value indicating whether to verify an SSL certificate. - /// - [Obsolete("Use AllowInsecureTls instead.")] - public bool VerifySslCertificate - { - get { return !_allowInsecureTls; } - set - { - if (_isFrozen) { throw new InvalidOperationException("MongoClientSettings is frozen."); } - // use property instead of private field because setter has additional side effects - AllowInsecureTls = !value; - } - } - /// /// Gets or sets the wait queue size. /// diff --git a/src/MongoDB.Driver/MongoUrl.cs b/src/MongoDB.Driver/MongoUrl.cs index a74cec4c4fc..6c11ab3c1ec 100644 --- a/src/MongoDB.Driver/MongoUrl.cs +++ b/src/MongoDB.Driver/MongoUrl.cs @@ -535,23 +535,11 @@ public string Username get { return _username; } } - /// - /// Gets a value indicating whether to use SSL. - /// - [Obsolete("Use UseTls instead.")] - public bool UseSsl => _useTls; - /// /// Gets a value indicating whether to use TLS. /// public bool UseTls => _useTls; - /// - /// Gets a value indicating whether to verify an SSL certificate. - /// - [Obsolete("Use AllowInsecureTls instead.")] - public bool VerifySslCertificate => !_allowInsecureTls; - /// /// Gets the W component of the write concern. /// diff --git a/src/MongoDB.Driver/MongoUrlBuilder.cs b/src/MongoDB.Driver/MongoUrlBuilder.cs index 5dabf557918..1b590086078 100644 --- a/src/MongoDB.Driver/MongoUrlBuilder.cs +++ b/src/MongoDB.Driver/MongoUrlBuilder.cs @@ -705,16 +705,6 @@ public string Username set { _username = value; } } - /// - /// Gets or sets a value indicating whether to use SSL. - /// - [Obsolete("Use UseTls instead.")] - public bool UseSsl - { - get { return _useTls; } - set { _useTls = value; } - } - /// /// Gets or sets a value indicating whether to use TLS. /// @@ -724,16 +714,6 @@ public bool UseTls set => _useTls = value; } - /// - /// Gets or sets a value indicating whether to verify an SSL certificate. - /// - [Obsolete("Use AllowInsecureTls instead.")] - public bool VerifySslCertificate - { - get => !_allowInsecureTls; - set => _allowInsecureTls = !value; - } - /// /// Gets or sets the W component of the write concern. /// diff --git a/tests/MongoDB.Driver.Tests/Core/Configuration/ConnectionStringTests.cs b/tests/MongoDB.Driver.Tests/Core/Configuration/ConnectionStringTests.cs index 289140353bb..1c10201c70f 100644 --- a/tests/MongoDB.Driver.Tests/Core/Configuration/ConnectionStringTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Configuration/ConnectionStringTests.cs @@ -385,10 +385,6 @@ public void When_nothing_is_specified(string connectionString) subject.LocalThreshold.Should().Be(null); subject.SocketTimeout.Should().Be(null); subject.ServerMonitoringMode.Should().Be(null); -#pragma warning disable 618 - subject.Ssl.Should().Be(null); - subject.SslVerifyCertificate.Should().Be(null); -#pragma warning restore 618 subject.Timeout.Should().Be(null); subject.Tls.Should().Be(null); subject.TlsInsecure.Should().Be(null); @@ -490,10 +486,6 @@ public void When_everything_is_specified() subject.LocalThreshold.Should().Be(TimeSpan.FromMilliseconds(50)); subject.SocketTimeout.Should().Be(TimeSpan.FromMilliseconds(40)); subject.ServerMonitoringMode.Should().Be(ServerMonitoringMode.Stream); -#pragma warning disable 618 - subject.Ssl.Should().BeFalse(); - subject.SslVerifyCertificate.Should().Be(true); -#pragma warning restore 618 #if DEBUG // TODO: CSOT: Make it public when CSOT will be ready for GA subject.Timeout.Should().Be(TimeSpan.FromMilliseconds(42)); #endif @@ -1084,9 +1076,8 @@ public void When_ssl_is_specified(string connectionString, bool ssl) { var subject = new ConnectionString(connectionString); -#pragma warning disable 618 - subject.Ssl.Should().Be(ssl); -#pragma warning restore 618 + // the ssl keyword remains supported and is an alias for tls + subject.Tls.Should().Be(ssl); } [Theory] @@ -1096,9 +1087,8 @@ public void When_sslVerifyCertificate_is_specified(string connectionString, bool { var subject = new ConnectionString(connectionString); -#pragma warning disable 618 - subject.SslVerifyCertificate.Should().Be(sslVerifyCertificate); -#pragma warning restore 618 + // the sslVerifyCertificate keyword remains supported and is the inverse of tlsInsecure + subject.TlsInsecure.Should().Be(!sslVerifyCertificate); } #if DEBUG // TODO: CSOT: Make it public when CSOT will be ready for GA diff --git a/tests/MongoDB.Driver.Tests/MongoClientSettingsTests.cs b/tests/MongoDB.Driver.Tests/MongoClientSettingsTests.cs index d66bd7e703e..758eafb051e 100644 --- a/tests/MongoDB.Driver.Tests/MongoClientSettingsTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoClientSettingsTests.cs @@ -339,13 +339,8 @@ public void TestDefaults() Assert.Equal(MongoDefaults.SocketTimeout, settings.SocketTimeout); Assert.Equal(null, settings.Socks5ProxySettings); Assert.Null(settings.SslSettings); -#pragma warning disable 618 - Assert.Equal(false, settings.UseSsl); -#pragma warning restore 618 Assert.Equal(false, settings.UseTls); -#pragma warning disable 618 - Assert.Equal(true, settings.VerifySslCertificate); -#pragma warning restore 618 + Assert.Equal(false, settings.AllowInsecureTls); #pragma warning disable 618 Assert.Equal(MongoDefaults.ComputedWaitQueueSize, settings.WaitQueueSize); #pragma warning restore 618 @@ -527,20 +522,12 @@ public void TestEquals() clone.SslSettings = new SslSettings { CheckCertificateRevocation = false }; Assert.False(clone.Equals(settings)); - clone = settings.Clone(); -#pragma warning disable 618 - clone.UseSsl = !settings.UseSsl; -#pragma warning restore 618 - Assert.False(clone.Equals(settings)); - clone = settings.Clone(); clone.UseTls = !settings.UseTls; Assert.False(clone.Equals(settings)); clone = settings.Clone(); -#pragma warning disable 618 - clone.VerifySslCertificate = !settings.VerifySslCertificate; -#pragma warning restore 618 + clone.AllowInsecureTls = !settings.AllowInsecureTls; Assert.False(clone.Equals(settings)); clone = settings.Clone(); @@ -717,14 +704,8 @@ public void TestFromUrl() Assert.Equal(url.ProxyPort, settings.Socks5ProxySettings.Port); Assert.Equal(url.ProxyUsername, ((Socks5AuthenticationSettings.UsernamePasswordAuthenticationSettings)settings.Socks5ProxySettings.Authentication).Username); Assert.Equal(url.ProxyPassword, ((Socks5AuthenticationSettings.UsernamePasswordAuthenticationSettings)settings.Socks5ProxySettings.Authentication).Password); -#pragma warning disable 618 Assert.Equal(url.TlsDisableCertificateRevocationCheck, !settings.SslSettings.CheckCertificateRevocation); - Assert.Equal(url.UseSsl, settings.UseSsl); -#pragma warning restore 618 Assert.Equal(url.UseTls, settings.UseTls); -#pragma warning disable 618 - Assert.Equal(url.VerifySslCertificate, settings.VerifySslCertificate); -#pragma warning restore 618 #pragma warning disable 618 Assert.Equal(url.ComputedWaitQueueSize, settings.WaitQueueSize); @@ -1333,23 +1314,6 @@ public void TestSslSettings() Assert.Throws(() => { settings.SslSettings = sslSettings; }); } - [Fact] - public void TestUseSsl() - { -#pragma warning disable 618 - var settings = new MongoClientSettings(); - Assert.Equal(false, settings.UseSsl); - - var useSsl = true; - settings.UseSsl = useSsl; - Assert.Equal(useSsl, settings.UseSsl); - - settings.Freeze(); - Assert.Equal(useSsl, settings.UseSsl); - Assert.Throws(() => { settings.UseSsl = useSsl; }); -#pragma warning restore 618 - } - [Fact] public void TestUseTls() { @@ -1365,24 +1329,6 @@ public void TestUseTls() Assert.Throws(() => { settings.UseTls = useTls; }); } - [Fact] - public void TestVerifySslCertificate() - { -#pragma warning disable 618 - var settings = new MongoClientSettings(); - Assert.Equal(true, settings.VerifySslCertificate); - - var verifySslCertificate = false; - settings.VerifySslCertificate = verifySslCertificate; - Assert.Equal(verifySslCertificate, settings.VerifySslCertificate); - settings.SslSettings.CheckCertificateRevocation.Should().BeFalse(); - - settings.Freeze(); - Assert.Equal(verifySslCertificate, settings.VerifySslCertificate); - Assert.Throws(() => { settings.VerifySslCertificate = verifySslCertificate; }); -#pragma warning restore 618 - } - [Fact] public void TestWaitQueueSize() { diff --git a/tests/MongoDB.Driver.Tests/MongoUrlBuilderTests.cs b/tests/MongoDB.Driver.Tests/MongoUrlBuilderTests.cs index 5d42bbadb52..5ede33caf68 100644 --- a/tests/MongoDB.Driver.Tests/MongoUrlBuilderTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoUrlBuilderTests.cs @@ -101,13 +101,7 @@ public void TestAll() Timeout = TimeSpan.FromSeconds(13), #endif Username = "username", -#pragma warning disable 618 - UseSsl = true, -#pragma warning restore 618 UseTls = true, -#pragma warning disable 618 - VerifySslCertificate = false, -#pragma warning restore 618 W = 2, #pragma warning disable 618 WaitQueueSize = 123, @@ -202,13 +196,7 @@ public void TestAll() Assert.Equal(TimeSpan.FromSeconds(13), builder.Timeout); #endif Assert.Equal("username", builder.Username); -#pragma warning disable 618 - Assert.Equal(true, builder.UseSsl); -#pragma warning restore 618 Assert.Equal(true, builder.UseTls); -#pragma warning disable 618 - Assert.Equal(false, builder.VerifySslCertificate); -#pragma warning restore 618 Assert.Equal(2, ((WriteConcern.WCount)builder.W).Value); #pragma warning disable 618 Assert.Equal(0.0, builder.WaitQueueMultiple); @@ -462,13 +450,7 @@ public void TestDefaults() Assert.Equal(null, builder.Timeout); Assert.Equal(MongoInternalDefaults.MongoClientSettings.SrvServiceName, builder.SrvServiceName); Assert.Equal(null, builder.Username); -#pragma warning disable 618 - Assert.Equal(false, builder.UseSsl); -#pragma warning restore 618 Assert.Equal(false, builder.UseTls); -#pragma warning disable 618 - Assert.Equal(true, builder.VerifySslCertificate); -#pragma warning restore 618 Assert.Equal(null, builder.W); #pragma warning disable 618 Assert.Equal(MongoDefaults.WaitQueueMultiple, builder.WaitQueueMultiple); diff --git a/tests/MongoDB.Driver.Tests/MongoUrlTests.cs b/tests/MongoDB.Driver.Tests/MongoUrlTests.cs index a6200efb790..55dfef98e7c 100644 --- a/tests/MongoDB.Driver.Tests/MongoUrlTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoUrlTests.cs @@ -292,13 +292,7 @@ public void TestAll() #endif Assert.Equal(true, url.TlsDisableCertificateRevocationCheck); Assert.Equal("username", url.Username); -#pragma warning disable 618 - Assert.Equal(true, url.UseSsl); -#pragma warning restore 618 Assert.Equal(true, url.UseTls); -#pragma warning disable 618 - Assert.Equal(false, url.VerifySslCertificate); -#pragma warning restore 618 Assert.Equal(2, ((WriteConcern.WCount)url.W).Value); #pragma warning disable 618 Assert.Equal(0.0, url.WaitQueueMultiple); diff --git a/tests/MongoDB.Driver.Tests/Specifications/connection-string/ConnectionStringTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/connection-string/ConnectionStringTestRunner.cs index 77b10f6ee35..8e064c3f2e6 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/connection-string/ConnectionStringTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/connection-string/ConnectionStringTestRunner.cs @@ -176,9 +176,8 @@ private void AssertOptions(ConnectionString connectionString, BsonDocument defin connectionString.SrvServiceName.Should().Be(expectedOption.Value.AsString); break; case "ssl": -#pragma warning disable 618 - AssertBoolean(connectionString.Ssl, expectedOption.Value); -#pragma warning restore 618 + // the ssl option is an alias for tls and is surfaced through Tls + AssertBoolean(connectionString.Tls, expectedOption.Value); break; case "timeoutms": //Ignored for now as this is used by CSOT. break; From 69277c6f508f4c686b4ce8c636cfe841fc38e558 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Thu, 30 Jul 2026 13:20:50 -0400 Subject: [PATCH 07/13] CSHARP-5996: Remove obsolete Csfle2QEv2TextPreviewAlgorithm Csfle2QEv2StringPreviewAlgorithm replaced it in CSHARP-5984 and both name the same wire version, so the two were interchangeable. The earlier "Remove unreferenced obsolete APIs" commit left this one in place because the replacement had only shipped in 3.10.0. That reasoning applies to a minor release; this work targets 4.0, where the deprecation window is not a constraint. Nothing in src/ or tests/ referenced it. Csfle2QEv2StringPreviewAlgorithm and Csfle2QEv2StringAlgorithm are unaffected. Removing public API is a breaking change and targets the 4.0 major release. --- src/MongoDB.Driver/Core/Misc/Feature.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/MongoDB.Driver/Core/Misc/Feature.cs b/src/MongoDB.Driver/Core/Misc/Feature.cs index c29550940ff..bd3c8ab2e52 100644 --- a/src/MongoDB.Driver/Core/Misc/Feature.cs +++ b/src/MongoDB.Driver/Core/Misc/Feature.cs @@ -172,12 +172,6 @@ public class Feature /// public static Feature Csfle2QEv2StringPreviewAlgorithm { get; } = new("csfle2Qev2StringPreviewAlgorithm", WireVersion.Server82); - /// - /// Gets the csfle2 textPreview algorithm feature. - /// - [Obsolete("Use Csfle2QEv2StringPreviewAlgorithm instead.")] - public static Feature Csfle2QEv2TextPreviewAlgorithm { get; } = new("csfle2Qev2TextPreviewAlgorithm", WireVersion.Server82); - /// /// Gets the date operators added in 5.0 feature. /// From c18da6734718287c1d4ded671424afab4a7eee5e Mon Sep 17 00:00:00 2001 From: adelinowona Date: Thu, 30 Jul 2026 16:56:51 -0400 Subject: [PATCH 08/13] CSHARP-5996: Remove obsolete TextOptions and TextPreview algorithm StringOptions and EncryptionAlgorithm.String replaced these in CSHARP-5984. Both removed members were pure aliases: - TextOptions carried no behaviour of its own. Its CreateDocument extension and StringOptions' both delegated to CreateStringOptionsDocument with the same five fields, so the two produced identical BSON, and EnsureThatOptionsAreValid applied the same rules to each. - EncryptionAlgorithm.TextPreview was translated to "String" by ConvertEnumAlgorithmToString before reaching the wire. Preview versus GA is expressed by the query type, not by the options type or the algorithm: ValidStringQueryTypes accepts prefix, prefixPreview, substring, substringPreview, suffix and suffixPreview, and StringOptions has always accepted all six. A caller on server 8.2 pairs StringOptions with a *Preview query type and gets the bytes TextOptions produced, so removing the aliases costs nothing while the GA query types wait on server 9.0. Removed: the TextOptions class, EncryptOptions.TextOptions, the two constructors and the With overload taking TextOptions, the _textOptions field, the CreateDocument extension for it, and EncryptionAlgorithm.TextPreview. The validation and marshalling paths that had to consider both options types collapse to StringOptions alone, which retires six CS0618 suppressions. Behaviour change beyond the removals: the string "TextPreview" passed to the string-algorithm constructor no longer translates to "String". Enum.TryParse now fails for it, so it is forwarded verbatim and the server rejects it. The two InlineData cases asserting the old translation are removed along with With_textOptions_should_create_new_instance_with_updated_textOptions, whose StringOptions twin already covers the same ground. The enum's remaining ordinals are deliberately left unpinned. The integers are not part of the wire contract, the enum never declared explicit values, and anything still holding a stale ordinal is already broken by the rest of this branch. AGENTS.md is corrected: it claimed the enum integer was part of the on-the-wire contract, which is what argued for pinning. Removing public API is a breaking change and targets the 4.0 major release. --- src/MongoDB.Driver.Encryption/AGENTS.md | 8 +- .../EncryptOptions.cs | 191 +----------------- .../EncryptionAlgorithm.cs | 9 - .../EncryptionOptionsExtensions.cs | 5 - .../Encryption/EncryptOptionsTests.cs | 22 -- 5 files changed, 12 insertions(+), 223 deletions(-) diff --git a/src/MongoDB.Driver.Encryption/AGENTS.md b/src/MongoDB.Driver.Encryption/AGENTS.md index 1ed677fb9ad..458734348eb 100644 --- a/src/MongoDB.Driver.Encryption/AGENTS.md +++ b/src/MongoDB.Driver.Encryption/AGENTS.md @@ -13,7 +13,7 @@ This project wraps **libmongocrypt** (the C library that implements CSFLE and Qu - `ClientEncryption` — explicit encryption surface, incl. `CreateDataKey`, `RewrapManyDataKey`, `Encrypt`, `EncryptExpression`, `Decrypt`, `GetKey`, `GetKeyByAlternateKeyName`, `AddAlternateKeyName`, `RemoveAlternateKeyName`, `DeleteKey`, `GetKeys`, `CreateEncryptedCollection`. All have sync + async pairs and accept a `CancellationToken`. Backed by `ExplicitEncryptionLibMongoCryptController`. - `ClientEncryptionOptions` — `KeyVaultClient` (typically a separate `IMongoClient` for the key vault), `KeyVaultNamespace`, `KmsProviders` (per-provider credentials), `TlsOptions`, `KeyExpiration` (DEK cache TTL; the C# property defaults to `null`, which causes libmongocrypt to apply its 60-second default; `Zero` = never expire). Validates KMS option values are `byte[]` or `string`; rejects per-provider TLS settings that supply a `ServerCertificateValidationCallback` (insecure-by-construction). Other `SslSettings` knobs (custom CAs via the standard validation callback chain, client certificates, etc.) are not blocked. -- `EncryptOptions`, `EncryptionAlgorithm`, `DataKeyOptions`, `RewrapManyDataKeyOptions`, `RangeOptions`, `TextOptions` (with `PrefixOptions`, `SubstringOptions`, `SuffixOptions` for the QE TextPreview surface), `CsfleSchemaBuilder` (a fluent builder for **CSFLE** `$jsonSchema`-style schemas suitable for `AutoEncryptionOptions.SchemaMap`; QE encrypted-field schemas are configured via `AutoEncryptionOptions.EncryptedFieldsMap`, not via this builder). +- `EncryptOptions`, `EncryptionAlgorithm`, `DataKeyOptions`, `RewrapManyDataKeyOptions`, `RangeOptions`, `StringOptions` (with `PrefixOptions`, `SubstringOptions`, `SuffixOptions` for the QE String surface), `CsfleSchemaBuilder` (a fluent builder for **CSFLE** `$jsonSchema`-style schemas suitable for `AutoEncryptionOptions.SchemaMap`; QE encrypted-field schemas are configured via `AutoEncryptionOptions.EncryptedFieldsMap`, not via this builder). ## Controllers @@ -49,13 +49,13 @@ libmongocrypt asks managed code for AES / HMAC / random / RSA signing via callba - `CsfleSchemaBuilder` — fluent builder for **CSFLE** `$jsonSchema`-style schemas (composes with `AutoEncryptionOptions.SchemaMap`). The duplicate-namespace check is incidental: `Encrypt(CollectionNamespace, Action>)` calls `_schemas.Add(...)` on the underlying `Dictionary`, which throws `ArgumentException` on a duplicate key — there is no explicit validation step beyond that. **Not** the entry point for Queryable Encryption — QE schemas are configured via `AutoEncryptionOptions.EncryptedFieldsMap`. - `EncryptionAlgorithm` is a single flat enum — values are not partitioned in the type system, only by usage convention: - **CSFLE-only by convention** — `AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic` (equality-queryable, same plaintext → same ciphertext), `AEAD_AES_256_CBC_HMAC_SHA_512_Random` (no queries possible). - - **QE-only by convention** — `Indexed` (equality with contention), `Range` (range queries), `TextPreview` (preview), `Unindexed`. Server-version availability (preview vs GA) for each algorithm is a server-side concern; consult the MongoDB server release notes rather than relying on driver-side enum metadata. The "Preview" suffix on `TextPreview` reflects the server's preview status — but `EncryptionAlgorithm` is a **public enum**, so the value itself is SemVer-covered: renaming or removing `TextPreview` (e.g. once the server feature GAs as `Text`) requires an `[Obsolete]` deprecation cycle, not an in-place rename. The migration shape is additive-then-deprecate: introduce a new `Text` enum member alongside `TextPreview`, mark `TextPreview` `[Obsolete]`, and only remove it in a later major version — never reuse the existing enum value, since the integer is part of the on-the-wire contract for any caller that has it baked in. + - **QE-only by convention** — `Indexed` (equality with contention), `Range` (range queries), `String` (prefix / substring / suffix queries, including the `*Preview` query types), `Unindexed`. Server-version availability (preview vs GA) for each algorithm is a server-side concern; consult the MongoDB server release notes rather than relying on driver-side enum metadata. Preview versus GA is expressed by the **query type** (`prefixPreview` vs `prefix`, and so on — see `ValidStringQueryTypes` in `EncryptOptions.cs`), not by a separate algorithm or options type. `EncryptionAlgorithm` is a **public enum**, so renaming or removing a member is SemVer-covered and needs an `[Obsolete]` cycle followed by removal in a major version, not an in-place rename. The integers are **not** part of the wire contract — `ConvertEnumAlgorithmToString` sends the member name as a string, and the enum declares no explicit values — so members may be removed without pinning the remaining ordinals. Contrast `CompressorType` in `Core/Compression/ICompressor.cs`, which does pin its values because there the integers are wire protocol IDs. Server-side enforcement decides which value is valid in a given context. Confusing CSFLE and QE algorithms is a recurring bug. ## Mongocryptd vs crypt_shared - `MongocryptdFactory` — spawns a local `mongocryptd` process if needed. Default URI `mongodb://localhost:27020`. Controlled by `extraOptions["mongocryptdURI"]`, `mongocryptdSpawnArgs`. Skipped if `BypassQueryAnalysis = true`. -- **`crypt_shared` is preferred** — it's a shared library loaded by libmongocrypt, no separate process. Set `CRYPT_SHARED_LIB_PATH` to point libmongocrypt at it. **QE (Indexed/Range/TextPreview) requires `crypt_shared`**; mongocryptd does not implement QE. +- **`crypt_shared` is preferred** — it's a shared library loaded by libmongocrypt, no separate process. Set `CRYPT_SHARED_LIB_PATH` to point libmongocrypt at it. **QE (Indexed/Range/String) requires `crypt_shared`**; mongocryptd does not implement QE. ## Threading & lifecycle @@ -70,7 +70,7 @@ libmongocrypt asks managed code for AES / HMAC / random / RSA signing via callba - **State-machine misuse.** Calling `Encrypt` / `Decrypt` before `InitContext`, or feeding wrong-shape input, corrupts the context. Always drive contexts to `DONE`. - **KMS credential expiry mid-operation.** AWS STS, Azure IMDS, GCP service tokens can expire. libmongocrypt asks for fresh credentials via `NEED_KMS_CREDENTIALS`; the controller must refetch and resupply. Failing to handle this looks like sporadic "auth failed" errors under load. - **DEK cache staleness.** `KeyExpiration` is the cache-pruning lever — TTL expiry evicts a DEK on next lookup, not in the background. Long-lived processes that never re-encrypt may accumulate cache entries. `RewrapManyDataKey` is **key rotation** (re-encrypts each DEK with a new KEK in the key vault); it does not itself prune the local DEK cache, but is the canonical way to rotate keys on a schedule — entries then expire normally per `KeyExpiration`. -- **CSFLE vs QE algorithms confused.** `Deterministic`/`Random` are CSFLE-only; `Indexed`/`Range`/`TextPreview`/`Unindexed` are QE-only. The wrong combination on the server side fails with cryptic schema errors. +- **CSFLE vs QE algorithms confused.** `Deterministic`/`Random` are CSFLE-only; `Indexed`/`Range`/`String`/`Unindexed` are QE-only. The wrong combination on the server side fails with cryptic schema errors. - **SafeHandle ordering bug.** A `ContextSafeHandle` outliving its parent `MongoCryptSafeHandle` dereferences a destroyed pointer. Don't rearrange disposal order without checking `GC.KeepAlive` calls. - **TLS callback security.** `ClientEncryptionOptions` rejects insecure TLS callbacks at construction. Don't add a "for testing" bypass that disables this — tests should use the mock KMS instead. diff --git a/src/MongoDB.Driver.Encryption/EncryptOptions.cs b/src/MongoDB.Driver.Encryption/EncryptOptions.cs index e9705b1d2bd..534bb4bf3f1 100644 --- a/src/MongoDB.Driver.Encryption/EncryptOptions.cs +++ b/src/MongoDB.Driver.Encryption/EncryptOptions.cs @@ -96,7 +96,7 @@ public RangeOptions( /// Prefix options. /// /// - /// PrefixOptions is used with StringOptions (or the deprecated TextOptions) and provides further options to support "prefix" and "prefixPreview" queries. + /// PrefixOptions is used with StringOptions and provides further options to support "prefix" and "prefixPreview" queries. /// public sealed class PrefixOptions { @@ -140,7 +140,7 @@ public PrefixOptions(int strMaxQueryLength, int strMinQueryLength) /// Substring options. /// /// - /// SubstringOptions is used with StringOptions (or the deprecated TextOptions) and provides further options to support "substring" and "substringPreview" queries. + /// SubstringOptions is used with StringOptions and provides further options to support "substring" and "substringPreview" queries. /// public sealed class SubstringOptions { @@ -199,7 +199,7 @@ public SubstringOptions(int strMaxLength, int strMaxQueryLength, int strMinQuery /// Suffix options. /// /// - /// SuffixOptions is used with StringOptions (or the deprecated TextOptions) and provides further options to support "suffix" and "suffixPreview" queries. + /// SuffixOptions is used with StringOptions and provides further options to support "suffix" and "suffixPreview" queries. /// public sealed class SuffixOptions { @@ -302,69 +302,6 @@ public StringOptions( public SuffixOptions SuffixOptions => _suffixOptions; } - /// - /// Text options. - /// - /// - /// This is a deprecated alias for . Use instead. - /// - [Obsolete("Use StringOptions instead.")] - public sealed class TextOptions - { - private readonly bool _caseSensitive; - private readonly bool _diacriticSensitive; - private readonly PrefixOptions _prefixOptions; - private readonly SubstringOptions _substringOptions; - private readonly SuffixOptions _suffixOptions; - - /// - /// Initializes a new instance of the class. - /// - /// The indicator of whether text indexes for this field are case-sensitive. - /// The indicator of whether text indexes for this field are diacritic sensitive. - /// The prefix options. - /// The substring options. - /// The suffix options. - public TextOptions( - bool caseSensitive, - bool diacriticSensitive, - Optional prefixOptions = default, - Optional substringOptions = default, - Optional suffixOptions = default) - { - _caseSensitive = caseSensitive; - _diacriticSensitive = diacriticSensitive; - _prefixOptions = prefixOptions.WithDefault(null); - _substringOptions = substringOptions.WithDefault(null); - _suffixOptions = suffixOptions.WithDefault(null); - } - - /// - /// Gets whether text indexes for this field are case-sensitive. - /// - public bool CaseSensitive => _caseSensitive; - - /// - /// Gets whether text indexes for this field are diacritic sensitive. - /// - public bool DiacriticSensitive => _diacriticSensitive; - - /// - /// Gets the prefix options. - /// - public PrefixOptions PrefixOptions => _prefixOptions; - - /// - /// Gets the substring options. - /// - public SubstringOptions SubstringOptions => _substringOptions; - - /// - /// Gets the suffix options. - /// - public SuffixOptions SuffixOptions => _suffixOptions; - } - /// /// Encryption options for explicit encryption. /// @@ -376,9 +313,6 @@ private static string ConvertEnumAlgorithmToString(EncryptionAlgorithm encryptio { EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic => "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random => "AEAD_AES_256_CBC_HMAC_SHA_512-Random", -#pragma warning disable CS0618 // TextPreview is a deprecated alias for String and is translated to "String". - EncryptionAlgorithm.TextPreview => EncryptionAlgorithm.String.ToString(), -#pragma warning restore CS0618 _ => encryptionAlgorithm.ToString(), }; @@ -392,9 +326,6 @@ private static string ConvertEnumAlgorithmToString(EncryptionAlgorithm encryptio private readonly Guid? _keyId; private readonly RangeOptions _rangeOptions; private readonly StringOptions _stringOptions; -#pragma warning disable CS0618 // _textOptions is the deprecated alias for _stringOptions. - private readonly TextOptions _textOptions; -#pragma warning restore CS0618 private readonly string _queryType; // constructors @@ -469,43 +400,6 @@ public EncryptOptions( EnsureThatOptionsAreValid(); } - /// - /// Initializes a new instance of the class. - /// - /// The encryption algorithm. - /// The text options. - /// The alternate key name. - /// The contention factor. - /// The key Id. - /// The query type. - [Obsolete("Use the StringOptions overload instead.")] - public EncryptOptions( - string algorithm, - TextOptions textOptions, - Optional alternateKeyName = default, - Optional contentionFactor = default, - Optional keyId = default, - Optional queryType = default) - { - Ensure.IsNotNull(algorithm, nameof(algorithm)); - Ensure.IsNotNull(textOptions, nameof(textOptions)); - if (Enum.TryParse(algorithm, out var @enum)) - { - _algorithm = ConvertEnumAlgorithmToString(@enum); - } - else - { - _algorithm = algorithm; - } - - _alternateKeyName = alternateKeyName.WithDefault(null); - _contentionFactor = contentionFactor.WithDefault(null); - _keyId = keyId.WithDefault(null); - _textOptions = textOptions; - _queryType = queryType.WithDefault(null); - EnsureThatOptionsAreValid(); - } - /// /// Initializes a new instance of the class. /// @@ -558,33 +452,6 @@ public EncryptOptions( { } - /// - /// Initializes a new instance of the class. - /// - /// The encryption algorithm. - /// The text options. - /// The alternate key name. - /// The key Id. - /// The contention factor. - /// The query type. - [Obsolete("Use the StringOptions overload instead.")] - public EncryptOptions( - EncryptionAlgorithm algorithm, - TextOptions textOptions, - Optional alternateKeyName = default, - Optional keyId = default, - Optional contentionFactor = default, - Optional queryType = default) - : this( - algorithm: ConvertEnumAlgorithmToString(algorithm), - textOptions, - alternateKeyName, - contentionFactor, - keyId, - queryType) - { - } - // public properties /// /// Gets the algorithm. @@ -651,15 +518,6 @@ public EncryptOptions( /// public StringOptions StringOptions => _stringOptions; - /// - /// Gets the text options. - /// - /// - /// This is a deprecated alias for . Use instead. - /// - [Obsolete("Use StringOptions instead.")] - public TextOptions TextOptions => _textOptions; - /// /// Returns a new EncryptOptions instance with some settings changed. /// @@ -714,38 +572,8 @@ public EncryptOptions With( stringOptions: stringOptions); } - /// - /// Returns a new EncryptOptions instance with some settings changed. - /// - /// The text options. - /// The encryption algorithm. - /// The alternate key name. - /// The keyId. - /// The contention factor. - /// The query type. - /// A new EncryptOptions instance. - [Obsolete("Use the StringOptions overload instead.")] - public EncryptOptions With( - TextOptions textOptions, - Optional algorithm = default, - Optional alternateKeyName = default, - Optional keyId = default, - Optional contentionFactor = default, - Optional queryType = default) - { - return new EncryptOptions( - algorithm: algorithm.WithDefault(_algorithm), - alternateKeyName: alternateKeyName.WithDefault(_alternateKeyName), - contentionFactor: contentionFactor.WithDefault(_contentionFactor), - keyId: keyId.WithDefault(_keyId), - queryType: queryType.WithDefault(_queryType), - textOptions: textOptions); - } - // internal methods -#pragma warning disable CS0618 // marshal whichever of StringOptions/TextOptions was set. - internal BsonDocument GetStringOptionsDocument() => _stringOptions?.CreateDocument() ?? _textOptions?.CreateDocument(); -#pragma warning restore CS0618 + internal BsonDocument GetStringOptionsDocument() => _stringOptions?.CreateDocument(); // private methods private void EnsureThatOptionsAreValid() @@ -756,8 +584,6 @@ private void EnsureThatOptionsAreValid() Ensure.That(!(_queryType != null && (_algorithm != EncryptionAlgorithm.Indexed.ToString() && _algorithm != EncryptionAlgorithm.Range.ToString() && _algorithm != EncryptionAlgorithm.String.ToString())), "QueryType only applies for Indexed, Range, or String algorithm."); Ensure.That(!(_rangeOptions != null && _algorithm != EncryptionAlgorithm.Range.ToString()), "RangeOptions only applies for Range algorithm."); Ensure.That(!(_stringOptions != null && _algorithm != EncryptionAlgorithm.String.ToString()), "StringOptions only applies for String algorithm."); -#pragma warning disable CS0618 // _textOptions is the deprecated alias; validate whichever options were set. - Ensure.That(!(_textOptions != null && _algorithm != EncryptionAlgorithm.String.ToString()), "TextOptions only applies for String algorithm."); if (_algorithm == EncryptionAlgorithm.String.ToString() && _queryType != null) { @@ -766,19 +592,18 @@ private void EnsureThatOptionsAreValid() $"QueryType '{_queryType}' is not valid for String algorithm. Use: {string.Join(", ", ValidStringQueryTypes)}."); } - if ((_stringOptions != null || _textOptions != null) && _queryType != null) + if (_stringOptions != null && _queryType != null) { Ensure.That( - !((_queryType == "prefix" || _queryType == "prefixPreview") && (_stringOptions?.PrefixOptions ?? _textOptions?.PrefixOptions) == null), + !((_queryType == "prefix" || _queryType == "prefixPreview") && _stringOptions.PrefixOptions == null), "PrefixOptions must be set when queryType is 'prefix' or 'prefixPreview'"); Ensure.That( - !((_queryType == "substring" || _queryType == "substringPreview") && (_stringOptions?.SubstringOptions ?? _textOptions?.SubstringOptions) == null), + !((_queryType == "substring" || _queryType == "substringPreview") && _stringOptions.SubstringOptions == null), "SubstringOptions must be set when queryType is 'substring' or 'substringPreview'"); Ensure.That( - !((_queryType == "suffix" || _queryType == "suffixPreview") && (_stringOptions?.SuffixOptions ?? _textOptions?.SuffixOptions) == null), + !((_queryType == "suffix" || _queryType == "suffixPreview") && _stringOptions.SuffixOptions == null), "SuffixOptions must be set when queryType is 'suffix' or 'suffixPreview'"); } -#pragma warning restore CS0618 } } } diff --git a/src/MongoDB.Driver.Encryption/EncryptionAlgorithm.cs b/src/MongoDB.Driver.Encryption/EncryptionAlgorithm.cs index 97a7f36c903..c0b48a1d2a4 100644 --- a/src/MongoDB.Driver.Encryption/EncryptionAlgorithm.cs +++ b/src/MongoDB.Driver.Encryption/EncryptionAlgorithm.cs @@ -57,15 +57,6 @@ public enum EncryptionAlgorithm /// Range, - /// - /// TextPreview algorithm. - /// - /// - /// This is a deprecated alias for and is translated to "String". Use instead. - /// - [Obsolete("Use String instead.")] - TextPreview, - /// /// String algorithm. /// diff --git a/src/MongoDB.Driver.Encryption/EncryptionOptionsExtensions.cs b/src/MongoDB.Driver.Encryption/EncryptionOptionsExtensions.cs index 9beb844cbfe..f79462164f0 100644 --- a/src/MongoDB.Driver.Encryption/EncryptionOptionsExtensions.cs +++ b/src/MongoDB.Driver.Encryption/EncryptionOptionsExtensions.cs @@ -32,11 +32,6 @@ public static BsonDocument CreateDocument(this RangeOptions rangeOptions) => public static BsonDocument CreateDocument(this StringOptions stringOptions) => CreateStringOptionsDocument(stringOptions.CaseSensitive, stringOptions.DiacriticSensitive, stringOptions.PrefixOptions, stringOptions.SubstringOptions, stringOptions.SuffixOptions); -#pragma warning disable CS0618 // TextOptions is the deprecated alias for StringOptions. - public static BsonDocument CreateDocument(this TextOptions textOptions) => - CreateStringOptionsDocument(textOptions.CaseSensitive, textOptions.DiacriticSensitive, textOptions.PrefixOptions, textOptions.SubstringOptions, textOptions.SuffixOptions); -#pragma warning restore CS0618 - private static BsonDocument CreateStringOptionsDocument( bool caseSensitive, bool diacriticSensitive, diff --git a/tests/MongoDB.Driver.Tests/Encryption/EncryptOptionsTests.cs b/tests/MongoDB.Driver.Tests/Encryption/EncryptOptionsTests.cs index f720e3f42e8..f0d87590e39 100644 --- a/tests/MongoDB.Driver.Tests/Encryption/EncryptOptionsTests.cs +++ b/tests/MongoDB.Driver.Tests/Encryption/EncryptOptionsTests.cs @@ -176,11 +176,6 @@ public void Constructor_should_fail_when_suffix_queryType_without_suffixOptions( // String algorithm [InlineData(EncryptionAlgorithm.String, "String")] [InlineData("String", "String")] - // TextPreview is a deprecated alias and is translated to the String algorithm -#pragma warning disable CS0618 - [InlineData(EncryptionAlgorithm.TextPreview, "String")] -#pragma warning restore CS0618 - [InlineData("TextPreview", "String")] public void Constructor_should_support_different_algorithm_representations(object algorithm, string expectedAlgorithmRepresentation) { var alternateKeyName = "test"; @@ -215,23 +210,6 @@ public void With_stringOptions_should_create_new_instance_with_updated_stringOpt updated.KeyId.Should().Be(subject.KeyId); } - [Fact] - public void With_textOptions_should_create_new_instance_with_updated_textOptions() - { -#pragma warning disable CS0618 // intentionally exercising the deprecated TextOptions API - var originalTextOptions = new TextOptions(true, true, prefixOptions: new PrefixOptions(10, 2)); - var newTextOptions = new TextOptions(false, false, substringOptions: new SubstringOptions(10, 8, 2)); - - var subject = new EncryptOptions(algorithm: EncryptionAlgorithm.TextPreview, keyId: Guid.NewGuid(), textOptions: originalTextOptions); - - var updated = subject.With(textOptions: newTextOptions); - - updated.TextOptions.Should().BeSameAs(newTextOptions); - updated.Algorithm.Should().Be(subject.Algorithm); - updated.KeyId.Should().Be(subject.KeyId); -#pragma warning restore CS0618 - } - [Fact] public void With_should_set_correct_values() { From 835d1d2127c02f4d6ea86231b473ecd62c1c72f8 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Thu, 30 Jul 2026 17:08:57 -0400 Subject: [PATCH 09/13] CSHARP-5996: Collapse the string options indirection left by TextOptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two layers existed only to serve two options types that rendered identically. With TextOptions gone each has a single caller and can be inlined: - CreateStringOptionsDocument took the five field values positionally so that the StringOptions and TextOptions extensions could share a body. Its logic now lives in CreateDocument(this StringOptions), which reads the properties directly. This matches CreateDocument(this RangeOptions) immediately above it. - EncryptOptions.GetStringOptionsDocument picked between _stringOptions and _textOptions. Both call sites in ExplicitEncryptionLibMongoCryptController now use encryptOptions.StringOptions?.CreateDocument(), which is the shape the neighbouring RangeOptions argument already had. Applied to the sync and async encrypt paths alike. No behaviour change: same element names, same order, same conditional inclusion, and the deferred lambdas still guard the null sub-option cases. Adds the two unit tests that were missing for this rendering. StringOptions' rendered document had no unit coverage at all — only the QE-Text-* spec fixtures exercised it, and those need CRYPT_SHARED_LIB_PATH, so the shape was unverifiable on a plain local run. Both tests were written against the previous implementation first and pass on either, so they pin the rendering rather than this refactor. --- .../EncryptOptions.cs | 3 -- .../EncryptionOptionsExtensions.cs | 32 +++++++----------- ...plicitEncryptionLibMongoCryptController.cs | 4 +-- .../Encryption/EncryptOptionsTests.cs | 33 +++++++++++++++++++ 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/src/MongoDB.Driver.Encryption/EncryptOptions.cs b/src/MongoDB.Driver.Encryption/EncryptOptions.cs index 534bb4bf3f1..145cb8af971 100644 --- a/src/MongoDB.Driver.Encryption/EncryptOptions.cs +++ b/src/MongoDB.Driver.Encryption/EncryptOptions.cs @@ -572,9 +572,6 @@ public EncryptOptions With( stringOptions: stringOptions); } - // internal methods - internal BsonDocument GetStringOptionsDocument() => _stringOptions?.CreateDocument(); - // private methods private void EnsureThatOptionsAreValid() { diff --git a/src/MongoDB.Driver.Encryption/EncryptionOptionsExtensions.cs b/src/MongoDB.Driver.Encryption/EncryptionOptionsExtensions.cs index f79462164f0..529ed2a7824 100644 --- a/src/MongoDB.Driver.Encryption/EncryptionOptionsExtensions.cs +++ b/src/MongoDB.Driver.Encryption/EncryptionOptionsExtensions.cs @@ -30,42 +30,34 @@ public static BsonDocument CreateDocument(this RangeOptions rangeOptions) => }; public static BsonDocument CreateDocument(this StringOptions stringOptions) => - CreateStringOptionsDocument(stringOptions.CaseSensitive, stringOptions.DiacriticSensitive, stringOptions.PrefixOptions, stringOptions.SubstringOptions, stringOptions.SuffixOptions); - - private static BsonDocument CreateStringOptionsDocument( - bool caseSensitive, - bool diacriticSensitive, - PrefixOptions prefixOptions, - SubstringOptions substringOptions, - SuffixOptions suffixOptions) => new() { - { "caseSensitive", caseSensitive }, - { "diacriticSensitive", diacriticSensitive }, + { "caseSensitive", stringOptions.CaseSensitive }, + { "diacriticSensitive", stringOptions.DiacriticSensitive }, { "prefix", () => new BsonDocument { - { "strMaxQueryLength", prefixOptions.StrMaxQueryLength }, - { "strMinQueryLength", prefixOptions.StrMinQueryLength } + { "strMaxQueryLength", stringOptions.PrefixOptions.StrMaxQueryLength }, + { "strMinQueryLength", stringOptions.PrefixOptions.StrMinQueryLength } }, - prefixOptions != null + stringOptions.PrefixOptions != null }, { "substring", () => new BsonDocument { - { "strMaxLength", substringOptions.StrMaxLength }, - { "strMaxQueryLength", substringOptions.StrMaxQueryLength }, - { "strMinQueryLength", substringOptions.StrMinQueryLength } + { "strMaxLength", stringOptions.SubstringOptions.StrMaxLength }, + { "strMaxQueryLength", stringOptions.SubstringOptions.StrMaxQueryLength }, + { "strMinQueryLength", stringOptions.SubstringOptions.StrMinQueryLength } }, - substringOptions != null + stringOptions.SubstringOptions != null }, { "suffix", () => new BsonDocument { - { "strMaxQueryLength", suffixOptions.StrMaxQueryLength }, - { "strMinQueryLength", suffixOptions.StrMinQueryLength } + { "strMaxQueryLength", stringOptions.SuffixOptions.StrMaxQueryLength }, + { "strMinQueryLength", stringOptions.SuffixOptions.StrMinQueryLength } }, - suffixOptions != null + stringOptions.SuffixOptions != null } }; } diff --git a/src/MongoDB.Driver.Encryption/ExplicitEncryptionLibMongoCryptController.cs b/src/MongoDB.Driver.Encryption/ExplicitEncryptionLibMongoCryptController.cs index ef1127936e9..be0361b53ca 100644 --- a/src/MongoDB.Driver.Encryption/ExplicitEncryptionLibMongoCryptController.cs +++ b/src/MongoDB.Driver.Encryption/ExplicitEncryptionLibMongoCryptController.cs @@ -232,7 +232,7 @@ public BsonValue EncryptField( encryptOptions.Algorithm, wrappedValueBytes, ToBsonIfNotNull(encryptOptions.RangeOptions?.CreateDocument()), - ToBsonIfNotNull(encryptOptions.GetStringOptionsDocument()), + ToBsonIfNotNull(encryptOptions.StringOptions?.CreateDocument()), isExpressionMode); using (context) @@ -268,7 +268,7 @@ public async Task EncryptFieldAsync( encryptOptions.Algorithm, wrappedValueBytes, ToBsonIfNotNull(encryptOptions.RangeOptions?.CreateDocument()), - ToBsonIfNotNull(encryptOptions.GetStringOptionsDocument()), + ToBsonIfNotNull(encryptOptions.StringOptions?.CreateDocument()), isExpressionMode); using (context) diff --git a/tests/MongoDB.Driver.Tests/Encryption/EncryptOptionsTests.cs b/tests/MongoDB.Driver.Tests/Encryption/EncryptOptionsTests.cs index f0d87590e39..a234a2bc677 100644 --- a/tests/MongoDB.Driver.Tests/Encryption/EncryptOptionsTests.cs +++ b/tests/MongoDB.Driver.Tests/Encryption/EncryptOptionsTests.cs @@ -15,6 +15,7 @@ using System; using FluentAssertions; +using MongoDB.Bson; using MongoDB.Driver.Encryption; using Xunit; @@ -210,6 +211,38 @@ public void With_stringOptions_should_create_new_instance_with_updated_stringOpt updated.KeyId.Should().Be(subject.KeyId); } + [Fact] + public void StringOptions_should_render_all_query_type_options() + { + var subject = new StringOptions( + caseSensitive: true, + diacriticSensitive: false, + prefixOptions: new PrefixOptions(10, 2), + substringOptions: new SubstringOptions(20, 10, 2), + suffixOptions: new SuffixOptions(8, 3)); + + var result = subject.CreateDocument(); + + result.Should().Be(BsonDocument.Parse(@" + { + caseSensitive : true, + diacriticSensitive : false, + prefix : { strMaxQueryLength : 10, strMinQueryLength : 2 }, + substring : { strMaxLength : 20, strMaxQueryLength : 10, strMinQueryLength : 2 }, + suffix : { strMaxQueryLength : 8, strMinQueryLength : 3 } + }")); + } + + [Fact] + public void StringOptions_should_omit_query_type_options_that_are_not_set() + { + var subject = new StringOptions(caseSensitive: false, diacriticSensitive: true); + + var result = subject.CreateDocument(); + + result.Should().Be(BsonDocument.Parse("{ caseSensitive : false, diacriticSensitive : true }")); + } + [Fact] public void With_should_set_correct_values() { From c06a81cf4b2f84688462438b6f951611c7f7e4c9 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Thu, 30 Jul 2026 21:20:18 -0400 Subject: [PATCH 10/13] CSHARP-5996: Remove obsolete ServerType.ReplicaSetPassive The value was unreachable, not merely deprecated. Nothing in the driver ever assigned it: HelloResult reads the hello reply's "passives" array only to enumerate member endpoints, and classifies those members as ReplicaSetSecondary. The SDAM spec requires exactly that -- see specifications/server-discovery-and-monitoring/tests/rs/discover_passives.json, where a member with "passive": true is expected to have type "RSSecondary". It was also unusable by callers. ServerTypeExtensions.ToClusterType has no case for it, so passing it fell through to the default label and threw ArgumentException; IsWritable would have returned false via its own default. The remaining ordinals are left unpinned, as with the encryption algorithm enum earlier in this branch. ServerType's integers are not a wire value -- the wire carries hello fields, and the driver compares enum members -- and the enum only pins Unknown = 0. Removing public API is a breaking change and targets the 4.0 major release. --- src/MongoDB.Driver/Core/Servers/ServerType.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/MongoDB.Driver/Core/Servers/ServerType.cs b/src/MongoDB.Driver/Core/Servers/ServerType.cs index 9bb18305cba..ecf91d3bf03 100644 --- a/src/MongoDB.Driver/Core/Servers/ServerType.cs +++ b/src/MongoDB.Driver/Core/Servers/ServerType.cs @@ -48,12 +48,6 @@ public enum ServerType /// ReplicaSetSecondary, - /// - /// Use ReplicaSetSecondary instead. - /// - [Obsolete("Passives are treated the same as secondaries.")] - ReplicaSetPassive, - /// /// The server is a replica set arbiter. /// From 4012fb6368dd51c02de909743a8b09fa0078a543 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Thu, 13 Aug 2026 16:34:45 -0400 Subject: [PATCH 11/13] address pr comments --- src/MongoDB.Driver/AggregateFluent.cs | 5 ---- src/MongoDB.Driver/AggregateFluentBase.cs | 3 --- src/MongoDB.Driver/Core/Servers/ServerType.cs | 6 +++++ .../JsonDrivenTests/JsonDrivenTestFactory.cs | 2 -- .../Specifications/UnifiedTestSpecRunner.cs | 27 +++++++++++++++++-- .../ClientSideEncryptionTestRunner.cs | 6 +++++ .../UnifiedTestOperationFactory.cs | 3 --- 7 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/MongoDB.Driver/AggregateFluent.cs b/src/MongoDB.Driver/AggregateFluent.cs index 90bd9823ce1..fcd8b874e2f 100644 --- a/src/MongoDB.Driver/AggregateFluent.cs +++ b/src/MongoDB.Driver/AggregateFluent.cs @@ -369,11 +369,6 @@ public override IAggregateFluent UnionWith( return WithPipeline(_pipeline.UnionWith(withCollection, withPipeline)); } - public override IAggregateFluent Unwind(FieldDefinition field, IBsonSerializer newResultSerializer) - { - return WithPipeline(_pipeline.Unwind(field, new AggregateUnwindOptions { ResultSerializer = newResultSerializer })); - } - public override IAggregateFluent Unwind(FieldDefinition field, AggregateUnwindOptions options) { return WithPipeline(_pipeline.Unwind(field, options)); diff --git a/src/MongoDB.Driver/AggregateFluentBase.cs b/src/MongoDB.Driver/AggregateFluentBase.cs index 4aad9f8a97e..ff23efa44e8 100644 --- a/src/MongoDB.Driver/AggregateFluentBase.cs +++ b/src/MongoDB.Driver/AggregateFluentBase.cs @@ -316,9 +316,6 @@ public virtual IAggregateFluent UnionWith( throw new NotImplementedException(); } - /// - public abstract IAggregateFluent Unwind(FieldDefinition field, IBsonSerializer newResultSerializer); - /// public virtual IAggregateFluent Unwind(FieldDefinition field, AggregateUnwindOptions options) { diff --git a/src/MongoDB.Driver/Core/Servers/ServerType.cs b/src/MongoDB.Driver/Core/Servers/ServerType.cs index ecf91d3bf03..9bb18305cba 100644 --- a/src/MongoDB.Driver/Core/Servers/ServerType.cs +++ b/src/MongoDB.Driver/Core/Servers/ServerType.cs @@ -48,6 +48,12 @@ public enum ServerType /// ReplicaSetSecondary, + /// + /// Use ReplicaSetSecondary instead. + /// + [Obsolete("Passives are treated the same as secondaries.")] + ReplicaSetPassive, + /// /// The server is a replica set arbiter. /// diff --git a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs index 6dc31185715..0c54e9db790 100644 --- a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs +++ b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs @@ -153,7 +153,6 @@ public JsonDrivenTest CreateTest(string receiver, string name) { case "aggregate": return new JsonDrivenAggregateTest(collection, _objectMap); case "bulkWrite": return new JsonDrivenBulkWriteTest(collection, _objectMap); - case "count": throw new SkipException(".NET/C# driver does not implement a Count helper; use CountDocuments or EstimatedDocumentCount."); case "countDocuments": return new JsonDrivenCountDocumentsTest(collection, _objectMap); case "createIndex": return new JsonDrivenCreateIndexTest(collection, _objectMap); case "deleteMany": return new JsonDrivenDeleteManyTest(collection, _objectMap); @@ -172,7 +171,6 @@ public JsonDrivenTest CreateTest(string receiver, string name) case "insertOne": return new JsonDrivenInsertOneTest(collection, _objectMap); case "listIndexes": return new JsonDrivenListIndexesTest(collection, _objectMap); case "listIndexNames": throw new SkipException(".NET/C# driver does not implement a ListIndexNames helper."); - case "mapReduce": throw new SkipException(".NET/C# driver does not implement a MapReduce helper; use an aggregation pipeline."); case "replaceOne": return new JsonDrivenReplaceOneTest(collection, _objectMap); case "updateMany": return new JsonDrivenUpdateManyTest(collection, _objectMap); case "updateOne": return new JsonDrivenUpdateOneTest(collection, _objectMap); diff --git a/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs index 5da19ac0723..de715d6a080 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs @@ -55,6 +55,7 @@ public UnifiedTestSpecRunner(ITestOutputHelper testOutputHelper) [UnifiedTestsTheory("client_side_operations_timeout.tests")] public void ClientSideOperationsTimeout(JsonDrivenTestCase testCase) { + SkipNotSupportedTestCases(testCase, "count on collection"); // .NET/C# driver does not implement a Count helper SkipNotSupportedTestCases(testCase, "dropIndexes"); SkipNotSupportedTestCases(testCase, "findOne"); SkipNotSupportedTestCases(testCase, "listIndexNames"); @@ -385,6 +386,21 @@ private static void SkipNotSupportedTestCases(JsonDrivenTestCase testCase, strin "legacy hello with speculative authenticate", "legacy hello without speculative authenticate is not redacted", + // crud + // .NET/C# driver does not implement a Count helper. These files also cover CountDocuments and + // EstimatedDocumentCount, so only the Count test cases are excluded. + "Deprecated count without a filter", + "Deprecated count with a filter", + "Deprecated count with skip and limit", + "Deprecated count with empty collection", + "Deprecated count with collation", + "Deprecated count with rawData option", + "Deprecated count with rawData option on less than 8.2.0 - ignore argument", + + // readWriteConcern + // .NET/C# driver does not implement a MapReduce helper + "MapReduce omits default write concern", + // retryableReads "collection.findOne succeeds after retryable handshake network error", "collection.findOne succeeds after retryable handshake server error (ShutdownInProgress)", @@ -418,12 +434,19 @@ private static void SkipNotSupportedTestCases(JsonDrivenTestCase testCase, strin "findOne.json", "findOne-serverErrors.json", "listCollectionObjects.json", - "listCollectionObjects.json", "listCollectionObjects-serverErrors.json", "listDatabaseObjects.json", "listDatabaseObjects-serverErrors.json", "listIndexNames.json", - "listIndexNames-serverErrors.json" + "listIndexNames-serverErrors.json", + + // .NET/C# driver does not implement Count or MapReduce helpers. + // Qualified by resource namespace because other specs have files of the same name that must keep running. + "retryable_reads.tests.unified.count.json", + "retryable_reads.tests.unified.count-serverErrors.json", + "retryable_reads.tests.unified.mapReduce.json", + "transactions.tests.unified.count.json", + "open_telemetry.operation.map_reduce.json" ]); #region CMAP helpers diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs index bfd46017416..886186d18b6 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs @@ -24,6 +24,7 @@ using MongoDB.TestHelpers.XunitExtensions; using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; namespace MongoDB.Driver.Tests.Specifications.client_side_encryption { @@ -59,6 +60,11 @@ public void Run(JsonDrivenTestCase testCase) RequireEnvironment.Check().EnvironmentVariable("KMS_MOCK_SERVERS_ENABLED"); } + if (testCase.Name.Contains("legacy.count.json") || testCase.Name.Contains("legacy.unsupportedCommand.json")) + { + throw new SkipException(".NET/C# driver does not implement Count or MapReduce helpers."); + } + RequirePlatform .Check() .SkipWhen( diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs index 14df0d1a7fc..21778838336 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; using MongoDB.Bson; -using Xunit.Sdk; namespace MongoDB.Driver.Tests.UnifiedTestOperations { @@ -91,7 +90,6 @@ public IUnifiedTestOperation CreateOperation(string operationName, string target { "aggregate" => new UnifiedAggregateOperationBuilder(_entityMap).BuildCollectionOperation(targetEntityId, operationArguments), "bulkWrite" => new UnifiedBulkWriteOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), - "count" => throw new SkipException(".NET/C# driver does not implement a Count helper; use CountDocuments or EstimatedDocumentCount."), "countDocuments" => new UnifiedCountDocumentsOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "createChangeStream" => new UnifiedCreateChangeStreamOnCollectionOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "createFindCursor" => new UnifiedCreateFindCursorOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), @@ -113,7 +111,6 @@ public IUnifiedTestOperation CreateOperation(string operationName, string target "insertOne" => new UnifiedInsertOneOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "listIndexes" => new UnifiedListIndexesOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "listSearchIndexes" => new UnifiedListSearchIndexesOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), - "mapReduce" => throw new SkipException(".NET/C# driver does not implement a MapReduce helper; use an aggregation pipeline."), "rename" => new UnifiedRenameCollectionOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "replaceOne" => new UnifiedReplaceOneOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "updateMany" => new UnifiedUpdateManyOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), From 03c7474d62629edac935a8661873c15d7d592861 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Fri, 14 Aug 2026 15:25:56 -0400 Subject: [PATCH 12/13] CSHARP-5996: Remove the ServerDescription version parameter The Version property went with the other obsolete members, but the value feeding it stayed: callers could still pass a version in through the constructor and With, where it took part in Equals, GetHashCode and ToString with no way to read it back. The value was never the server's version. Since CSHARP-3480 the driver stopped calling buildInfo and derived it from maxWireVersion instead, so it only ever carried a major and minor number. MaxWireVersion and WireVersionRange are what callers need to ask what a server supports, and both are already compared in Equals and printed in ToString. Removing public API is a breaking change and targets the 4.0 major release. --- .../Core/Servers/ServerDescription.cs | 11 ---------- .../Core/Servers/ServerMonitor.cs | 1 - .../Bindings/CoreServerSessionPoolTests.cs | 1 - .../Core/Bindings/CoreSessionTests.cs | 5 ++--- .../Core/Clusters/MultiServerClusterTests.cs | 1 - .../Core/Helpers/ServerDescriptionHelper.cs | 1 - .../Core/Servers/ServerDescriptionTests.cs | 20 ------------------- .../ServerSelectionTestHelpers.cs | 2 -- 8 files changed, 2 insertions(+), 40 deletions(-) diff --git a/src/MongoDB.Driver/Core/Servers/ServerDescription.cs b/src/MongoDB.Driver/Core/Servers/ServerDescription.cs index d8131a2560a..954bc268438 100644 --- a/src/MongoDB.Driver/Core/Servers/ServerDescription.cs +++ b/src/MongoDB.Driver/Core/Servers/ServerDescription.cs @@ -100,7 +100,6 @@ public int GetHashCode(ServerDescription obj) => private readonly TagSet _tags; private readonly TopologyVersion _topologyVersion; private readonly ServerType _type; - private readonly SemanticVersion _version; private readonly Range _wireVersionRange; // constructors @@ -129,7 +128,6 @@ public int GetHashCode(ServerDescription obj) => /// The replica set tags. /// The topology version. /// The server type. - /// The server version. /// The wire version range. /// EndPoint and ServerId.EndPoint must match. public ServerDescription( @@ -155,7 +153,6 @@ public ServerDescription( Optional tags = default(Optional), Optional topologyVersion = default(Optional), Optional type = default(Optional), - Optional version = default(Optional), Optional> wireVersionRange = default(Optional>)) { Ensure.IsNotNull(endPoint, nameof(endPoint)); @@ -187,7 +184,6 @@ public ServerDescription( _tags = tags.WithDefault(null); _topologyVersion = topologyVersion.WithDefault(null); _type = type.WithDefault(ServerType.Unknown); - _version = version.WithDefault(null); _wireVersionRange = wireVersionRange.WithDefault(null); } @@ -528,7 +524,6 @@ public bool Equals(ServerDescription other) _state == other._state && object.Equals(_tags, other._tags) && _type == other._type && - object.Equals(_version, other._version) && object.Equals(_wireVersionRange, other._wireVersionRange); } @@ -559,7 +554,6 @@ public override int GetHashCode() .Hash(_tags) .Hash(_topologyVersion) .Hash(_type) - .Hash(_version) .Hash(_wireVersionRange) .GetHashCode(); } @@ -595,7 +589,6 @@ public override string ToString() .AppendFormat(", EndPoint: \"{0}\"", _endPoint) .AppendFormat(", ReasonChanged: \"{0}\"", _reasonChanged) .AppendFormat(", State: \"{0}\"", _state) - .Append($", ServerVersion: {_version}") .Append($", TopologyVersion: {_topologyVersion}") .AppendFormat(", Type: \"{0}\"", _type) .AppendFormatIf(_tags != null && !_tags.IsEmpty, ", Tags: \"{0}\"", _tags) @@ -631,7 +624,6 @@ public override string ToString() /// The replica set tags. /// The topology version. /// The server type. - /// The server version. /// The wire version range. /// /// A new instance of ServerDescription. @@ -657,7 +649,6 @@ public ServerDescription With( Optional tags = default(Optional), Optional topologyVersion = default(Optional), Optional type = default(Optional), - Optional version = default(Optional), Optional> wireVersionRange = default(Optional>)) { return new ServerDescription( @@ -683,7 +674,6 @@ public ServerDescription With( tags: tags.WithDefault(_tags), topologyVersion: topologyVersion.WithDefault(_topologyVersion), type: type.WithDefault(_type), - version: version.WithDefault(_version), wireVersionRange: wireVersionRange.WithDefault(_wireVersionRange)); } @@ -719,7 +709,6 @@ public ServerDescription WithHeartbeatException(Exception heartbeatException) tags: _tags, topologyVersion: _topologyVersion, type: _type, - version: _version, wireVersionRange: _wireVersionRange); } } diff --git a/src/MongoDB.Driver/Core/Servers/ServerMonitor.cs b/src/MongoDB.Driver/Core/Servers/ServerMonitor.cs index 3d7948af746..4e6214d9913 100644 --- a/src/MongoDB.Driver/Core/Servers/ServerMonitor.cs +++ b/src/MongoDB.Driver/Core/Servers/ServerMonitor.cs @@ -415,7 +415,6 @@ private void Heartbeat(CancellationToken cancellationToken) tags: heartbeatHelloResult.Tags, topologyVersion: heartbeatHelloResult.TopologyVersion, type: heartbeatHelloResult.ServerType, - version: WireVersion.ToServerVersion(heartbeatHelloResult.MaxWireVersion), wireVersionRange: new Range(heartbeatHelloResult.MinWireVersion, heartbeatHelloResult.MaxWireVersion)); } else diff --git a/tests/MongoDB.Driver.Tests/Core/Bindings/CoreServerSessionPoolTests.cs b/tests/MongoDB.Driver.Tests/Core/Bindings/CoreServerSessionPoolTests.cs index 86f17bdc563..0206939f555 100644 --- a/tests/MongoDB.Driver.Tests/Core/Bindings/CoreServerSessionPoolTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Bindings/CoreServerSessionPoolTests.cs @@ -191,7 +191,6 @@ private CoreServerSessionPool CreateSubject(ClusterType clusterType = ClusterTyp logicalSessionTimeout: TimeSpan.FromMinutes(30), state: ServerState.Connected, type: ServerType.ShardRouter, - version: new SemanticVersion(3, 6, 0), wireVersionRange: new Range(6, 14)); var clusterDescription = new ClusterDescription(clusterId, false, null, clusterType, [serverDescription]); diff --git a/tests/MongoDB.Driver.Tests/Core/Bindings/CoreSessionTests.cs b/tests/MongoDB.Driver.Tests/Core/Bindings/CoreSessionTests.cs index 03c33383b39..e406ff0da0b 100644 --- a/tests/MongoDB.Driver.Tests/Core/Bindings/CoreSessionTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Bindings/CoreSessionTests.cs @@ -386,7 +386,7 @@ private IClusterInternal CreateMockReplicaSetCluster() var endPoint = new DnsEndPoint("localhost", 27017); var serverId = new ServerId(clusterId, endPoint); var maxWireVersion = WireVersion.SupportedWireVersionRange.Min; - var servers = new[] { new ServerDescription(serverId, endPoint, state: ServerState.Connected, type: ServerType.ReplicaSetPrimary, version: WireVersion.ToServerVersion(maxWireVersion), wireVersionRange: new Range(0, maxWireVersion)) }; + var servers = new[] { new ServerDescription(serverId, endPoint, state: ServerState.Connected, type: ServerType.ReplicaSetPrimary, wireVersionRange: new Range(0, maxWireVersion)) }; var clusterDescription = new ClusterDescription(clusterId, false, null, ClusterType.ReplicaSet, servers); var mockCluster = new Mock(); mockCluster.SetupGet(m => m.Description).Returns(clusterDescription); @@ -404,8 +404,7 @@ private ServerDescription CreateServerDescription( serverId = serverId ?? new ServerId(new ClusterId(1), endPoint); maxWireVersion = maxWireVersion ?? WireVersion.Server40; - var approximateServerVersion = WireVersion.ToServerVersion(maxWireVersion.Value); - return new ServerDescription(serverId, endPoint, state: state, type: type, version: approximateServerVersion, wireVersionRange: new Optional>(new Range(0, maxWireVersion.Value))); + return new ServerDescription(serverId, endPoint, state: state, type: type, wireVersionRange: new Optional>(new Range(0, maxWireVersion.Value))); } private CoreSession CreateSubject( diff --git a/tests/MongoDB.Driver.Tests/Core/Clusters/MultiServerClusterTests.cs b/tests/MongoDB.Driver.Tests/Core/Clusters/MultiServerClusterTests.cs index bf08a207179..619795f5d66 100644 --- a/tests/MongoDB.Driver.Tests/Core/Clusters/MultiServerClusterTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Clusters/MultiServerClusterTests.cs @@ -1231,7 +1231,6 @@ private void PublishDescription(IClusterInternal cluster, EndPoint endPoint, Ser state: ServerState.Connected, tags: null, type: serverType, - version: new SemanticVersion(3, 6, 0), wireVersionRange: new Range(0, int.MaxValue)); var currentClusterDescription = cluster.Description; diff --git a/tests/MongoDB.Driver.Tests/Core/Helpers/ServerDescriptionHelper.cs b/tests/MongoDB.Driver.Tests/Core/Helpers/ServerDescriptionHelper.cs index 1725495723f..4b1bd12748e 100644 --- a/tests/MongoDB.Driver.Tests/Core/Helpers/ServerDescriptionHelper.cs +++ b/tests/MongoDB.Driver.Tests/Core/Helpers/ServerDescriptionHelper.cs @@ -37,7 +37,6 @@ public static ServerDescription Connected(ClusterId clusterId, EndPoint endPoint state: ServerState.Connected, tags: tags, type: serverType, - version: new SemanticVersion(3, 6, 0), wireVersionRange: wireVersionRange ?? Cluster.SupportedWireVersionRange); } } diff --git a/tests/MongoDB.Driver.Tests/Core/Servers/ServerDescriptionTests.cs b/tests/MongoDB.Driver.Tests/Core/Servers/ServerDescriptionTests.cs index 6ecae16d216..771c98e1a18 100644 --- a/tests/MongoDB.Driver.Tests/Core/Servers/ServerDescriptionTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Servers/ServerDescriptionTests.cs @@ -76,7 +76,6 @@ public void Constructor_with_multiple_parameters_should_return_properly_initiali var state = ServerState.Connected; var tags = new TagSet(new[] { new Tag("x", "a") }); var type = ServerType.ReplicaSetPrimary; - var version = new SemanticVersion(3, 6, 0); var wireVersionRange = new Range(6, 14); var subject = new ServerDescription( @@ -91,7 +90,6 @@ public void Constructor_with_multiple_parameters_should_return_properly_initiali logicalSessionTimeout: logicalSessionTimeout, replicaSetConfig: replicaSetConfig, tags: tags, - version: version, wireVersionRange: wireVersionRange); subject.AverageRoundTripTime.Should().Be(TimeSpan.FromSeconds(1)); @@ -159,7 +157,6 @@ public static IEnumerable Exception_equals_test_cases() [InlineData("State")] [InlineData("Tags")] [InlineData("Type")] - [InlineData("Version")] [InlineData("WireVersionRange")] public void Equals_should_return_false_when_any_field_is_not_equal(string notEqualField) { @@ -179,7 +176,6 @@ public void Equals_should_return_false_when_any_field_is_not_equal(string notEqu var state = ServerState.Connected; var tags = new TagSet(new[] { new Tag("x", "a") }); var type = ServerType.ReplicaSetPrimary; - var version = new SemanticVersion(3, 6, 0); var wireVersionRange = new Range(6, 14); var subject = new ServerDescription( @@ -195,7 +191,6 @@ public void Equals_should_return_false_when_any_field_is_not_equal(string notEqu logicalSessionTimeout: logicalSessionTimeout, replicaSetConfig: replicaSetConfig, tags: tags, - version: version, wireVersionRange: wireVersionRange); switch (notEqualField) @@ -211,7 +206,6 @@ public void Equals_should_return_false_when_any_field_is_not_equal(string notEqu case "ServerId": serverId = new ServerId(new ClusterId(), endPoint); break; case "Tags": tags = new TagSet(new[] { new Tag("x", "b") }); break; case "Type": type = ServerType.ReplicaSetSecondary; break; - case "Version": version = new SemanticVersion(version.Major, version.Minor, version.Patch + 1); break; case "WireVersionRange": wireVersionRange = new Range(0, 0); break; } @@ -228,7 +222,6 @@ public void Equals_should_return_false_when_any_field_is_not_equal(string notEqu logicalSessionTimeout: logicalSessionTimeout, replicaSetConfig: replicaSetConfig, tags: tags, - version: version, wireVersionRange: wireVersionRange); subject.Equals(serverDescription2).Should().BeFalse(); @@ -332,7 +325,6 @@ public void SdamEquals_should_return_false_when_any_sdam_field_is_not_equal(stri [InlineData("AverageRoundTripTime")] [InlineData("LastUpdateTimestamp")] [InlineData("State")] - [InlineData("Version")] [InlineData("MaxBatchCount")] [InlineData("MaxDocumentSize")] [InlineData("MaxMessageSize")] @@ -348,7 +340,6 @@ public void SdamEquals_should_return_true_when_any_non_sdam_field_is_not_equal(s var averageRoundTripTime = TimeSpan.FromSeconds(1); var lastUpdateTimestamp = DateTime.UtcNow; var state = ServerState.Connected; - var version = new SemanticVersion(3, 6, 0); var maxBatchCount = 1000; var maxDocumentSize = 16000000; var maxMessageSize = 48000000; @@ -363,7 +354,6 @@ public void SdamEquals_should_return_true_when_any_non_sdam_field_is_not_equal(s averageRoundTripTime: averageRoundTripTime, lastUpdateTimestamp: lastUpdateTimestamp, state: state, - version: version, maxBatchCount: maxBatchCount, maxDocumentSize: maxDocumentSize, maxMessageSize: maxMessageSize, @@ -377,7 +367,6 @@ public void SdamEquals_should_return_true_when_any_non_sdam_field_is_not_equal(s case "AverageRoundTripTime": averageRoundTripTime = averageRoundTripTime.Add(TimeSpan.FromSeconds(1)); break; case "LastUpdateTimestamp": lastUpdateTimestamp = lastUpdateTimestamp.Add(TimeSpan.FromSeconds(1)); break; case "State": state = ServerState.Disconnected; break; - case "Version": version = new SemanticVersion(version.Major, version.Minor, version.Patch + 1); break; case "MaxBatchCount": maxBatchCount += 1; break; case "MaxDocumentSize": maxDocumentSize += 1; break; case "MaxMessageSize": maxMessageSize += 1; break; @@ -394,7 +383,6 @@ public void SdamEquals_should_return_true_when_any_non_sdam_field_is_not_equal(s averageRoundTripTime: averageRoundTripTime, lastUpdateTimestamp: lastUpdateTimestamp, state: state, - version: version, maxBatchCount: maxBatchCount, maxDocumentSize: maxDocumentSize, maxMessageSize: maxMessageSize, @@ -573,7 +561,6 @@ public void MaxWireVersion_should_be_the_same_as_wireVersionRange_max() [InlineData("State")] [InlineData("Tags")] [InlineData("Type")] - [InlineData("Version")] [InlineData("WireVersionRange")] public void With_should_return_new_instance_when_a_field_is_not_equal(string notEqualField) { @@ -598,7 +585,6 @@ public void With_should_return_new_instance_when_a_field_is_not_equal(string not var state = ServerState.Connected; var tags = new TagSet(new[] { new Tag("x", "a") }); var type = ServerType.ReplicaSetPrimary; - var version = new SemanticVersion(3, 6, 0); var wireVersionRange = new Range(6, 14); var subject = new ServerDescription( @@ -621,7 +607,6 @@ public void With_should_return_new_instance_when_a_field_is_not_equal(string not state: state, tags: tags, type: type, - version: version, wireVersionRange: wireVersionRange); switch (notEqualField) @@ -643,7 +628,6 @@ public void With_should_return_new_instance_when_a_field_is_not_equal(string not case "State": state = ServerState.Disconnected; break; case "Tags": tags = new TagSet(new[] { new Tag("x", "b") }); break; case "Type": type = ServerType.ReplicaSetSecondary; break; - case "Version": version = new SemanticVersion(version.Major, version.Minor, version.Patch + 1); break; case "WireVersionRange": wireVersionRange = new Range(0, 0); break; } @@ -665,7 +649,6 @@ public void With_should_return_new_instance_when_a_field_is_not_equal(string not state: state, tags: tags, type: type, - version: version, wireVersionRange: wireVersionRange); result.Should().NotBeSameAs(subject); @@ -688,7 +671,6 @@ public void With_should_return_same_instance_when_all_fields_are_equal() var state = ServerState.Connected; var tags = new TagSet(new[] { new Tag("x", "a") }); var type = ServerType.ReplicaSetPrimary; - var version = new SemanticVersion(3, 6, 0); var wireVersionRange = new Range(6, 14); var subject = new ServerDescription( @@ -701,7 +683,6 @@ public void With_should_return_same_instance_when_all_fields_are_equal() state: state, tags: tags, type: type, - version: version, wireVersionRange: wireVersionRange); var result = subject.With( @@ -712,7 +693,6 @@ public void With_should_return_same_instance_when_all_fields_are_equal() state: ServerState.Connected, tags: tags, type: type, - version: version, wireVersionRange: wireVersionRange); result.ShouldBeEquivalentTo(subject); diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-selection/ServerSelectionTestHelpers.cs b/tests/MongoDB.Driver.Tests/Specifications/server-selection/ServerSelectionTestHelpers.cs index 7d099e02e8d..c7275e42eb4 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/server-selection/ServerSelectionTestHelpers.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/server-selection/ServerSelectionTestHelpers.cs @@ -129,7 +129,6 @@ private static ServerDescription BuildServerDescription( var maxWireVersion = serverData.maxWireVersion ?? 9; var wireVersionRange = new Range(0, maxWireVersion); - var serverVersion = new SemanticVersion(4, 4, 0); var serverId = new ServerId(clusterId, endPoint); return new ServerDescription( @@ -141,7 +140,6 @@ private static ServerDescription BuildServerDescription( lastWriteTimestamp: lastWriteTimestamp, heartbeatInterval: heartbeatInterval, wireVersionRange: wireVersionRange, - version: serverVersion, tags: tagSet, state: ServerState.Connected); } From 0bb3d4122306b1c6dd8ab1bfae05b80893642a62 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Fri, 14 Aug 2026 16:33:00 -0400 Subject: [PATCH 13/13] CSHARP-5996: Restore MapReduce support Reverts 37c74dbbb8. The server deprecated mapReduce in 5.0 but has not removed it, and the driver supports servers from 4.4 up (WireVersion.SupportedWireVersionRange starts at Server44), so users on supported servers still need the helper. Removing it belongs to whichever release raises the minimum supported server version past 5.0. The spec skips that the removal required come out with it: the mapReduce entries in the UnifiedTestSpecRunner ignore lists, and the legacy.unsupportedCommand.json skip in ClientSideEncryptionTestRunner, whose only case is a mapReduce one. The Count entries stay, as Count remains removed. --- .../Core/Operations/MapReduceOperation.cs | 149 ++++ .../Core/Operations/MapReduceOperationBase.cs | 249 +++++++ .../Core/Operations/MapReduceOutputMode.cs | 27 + .../MapReduceOutputToCollectionOperation.cs | 287 ++++++++ .../FilteredMongoCollectionBase.cs | 32 + src/MongoDB.Driver/IMongoCollection.cs | 54 ++ src/MongoDB.Driver/MapReduceOptions.cs | 317 +++++++++ src/MongoDB.Driver/MongoCollectionBase.cs | 21 + src/MongoDB.Driver/MongoCollectionImpl.cs | 185 +++++ .../CommandStartedEventAsserter.cs | 15 + .../Operations/MapReduceOperationBaseTests.cs | 598 ++++++++++++++++ .../Operations/MapReduceOperationTests.cs | 593 ++++++++++++++++ ...pReduceOutputToCollectionOperationTests.cs | 669 ++++++++++++++++++ .../JsonDrivenMapReduceTest.cs | 123 ++++ .../JsonDrivenTests/JsonDrivenTestFactory.cs | 1 + .../MongoCollectionImplTests.cs | 194 +++++ .../OfTypeMongoCollectionTests.cs | 74 ++ .../Specifications/UnifiedTestSpecRunner.cs | 10 +- .../ClientSideEncryptionTestRunner.cs | 4 +- .../UnifiedMapReduceOperation.cs | 120 ++++ .../UnifiedTestOperationFactory.cs | 1 + 21 files changed, 3713 insertions(+), 10 deletions(-) create mode 100644 src/MongoDB.Driver/Core/Operations/MapReduceOperation.cs create mode 100644 src/MongoDB.Driver/Core/Operations/MapReduceOperationBase.cs create mode 100644 src/MongoDB.Driver/Core/Operations/MapReduceOutputMode.cs create mode 100644 src/MongoDB.Driver/Core/Operations/MapReduceOutputToCollectionOperation.cs create mode 100644 src/MongoDB.Driver/MapReduceOptions.cs create mode 100644 tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOperationBaseTests.cs create mode 100644 tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOperationTests.cs create mode 100644 tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOutputToCollectionOperationTests.cs create mode 100644 tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenMapReduceTest.cs create mode 100644 tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedMapReduceOperation.cs diff --git a/src/MongoDB.Driver/Core/Operations/MapReduceOperation.cs b/src/MongoDB.Driver/Core/Operations/MapReduceOperation.cs new file mode 100644 index 00000000000..a6b013f8924 --- /dev/null +++ b/src/MongoDB.Driver/Core/Operations/MapReduceOperation.cs @@ -0,0 +1,149 @@ +/* Copyright 2010-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Threading.Tasks; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Driver.Core.Bindings; +using MongoDB.Driver.Core.Connections; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; + +namespace MongoDB.Driver.Core.Operations +{ + /// + /// Represents a map-reduce operation. + /// + /// The type of the result. + [Obsolete("Use Aggregation pipeline instead.")] + internal sealed class MapReduceOperation : MapReduceOperationBase, IReadOperation> + { + // fields + private ReadConcern _readConcern = ReadConcern.Default; + private readonly IBsonSerializer _resultSerializer; + + // constructors + /// + /// Initializes a new instance of the class. + /// + /// The collection namespace. + /// The map function. + /// The reduce function. + /// The result serializer. + /// The message encoder settings. + public MapReduceOperation(CollectionNamespace collectionNamespace, BsonJavaScript mapFunction, BsonJavaScript reduceFunction, IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings) + : base( + collectionNamespace, + mapFunction, + reduceFunction, + messageEncoderSettings) + { + _resultSerializer = Ensure.IsNotNull(resultSerializer, nameof(resultSerializer)); + } + + // properties + /// + /// Gets or sets the read concern. + /// + /// + /// The read concern. + /// + public ReadConcern ReadConcern + { + get { return _readConcern; } + set { _readConcern = Ensure.IsNotNull(value, nameof(value)); } + } + + /// + /// Gets the result serializer. + /// + /// + /// The result serializer. + /// + public IBsonSerializer ResultSerializer + { + get { return _resultSerializer; } + } + + /// + /// Gets the name of the operation. + /// + public string OperationName => "mapReduce"; + + // methods + /// + protected override BsonDocument CreateOutputOptions() + { + return new BsonDocument("inline", 1); + } + + /// + public IAsyncCursor Execute(OperationContext operationContext, IReadBinding binding) + { + Ensure.IsNotNull(binding, nameof(binding)); + + using (var channelSource = binding.GetReadChannelSource(operationContext)) + using (var channel = channelSource.GetChannel(operationContext)) + using (var channelBinding = new ChannelReadBinding(channelSource.Server, channel, binding.ReadPreference)) + { + var operation = CreateOperation(operationContext, channel.ConnectionDescription); + var result = operation.Execute(operationContext, channelBinding); + return new SingleBatchAsyncCursor(result); + } + } + + /// + public async Task> ExecuteAsync(OperationContext operationContext, IReadBinding binding) + { + Ensure.IsNotNull(binding, nameof(binding)); + + using (var channelSource = await binding.GetReadChannelSourceAsync(operationContext).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) + using (var channelBinding = new ChannelReadBinding(channelSource.Server, channel, binding.ReadPreference)) + { + var operation = CreateOperation(operationContext, channel.ConnectionDescription); + var result = await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); + return new SingleBatchAsyncCursor(result); + } + } + + /// + protected internal override BsonDocument CreateCommand(OperationContext operationContext, ConnectionDescription connectionDescription, long? transactionNumber = null) + { + var command = base.CreateCommand(operationContext, connectionDescription); + + var readConcern = ReadConcernHelper.GetReadConcernForCommand(operationContext.Session, connectionDescription, _readConcern); + if (readConcern != null) + { + command.Add("readConcern", readConcern); + } + + return command; + } + + private ReadCommandOperation CreateOperation(OperationContext operationContext, ConnectionDescription connectionDescription) + { + var command = CreateCommand(operationContext, connectionDescription); + var resultArraySerializer = new ArraySerializer(_resultSerializer); + var resultSerializer = new ElementDeserializer("results", resultArraySerializer); + return new ReadCommandOperation(CollectionNamespace.DatabaseNamespace, command, resultSerializer, MessageEncoderSettings, OperationName) + { + RetryRequested = false, + }; + } + } +} diff --git a/src/MongoDB.Driver/Core/Operations/MapReduceOperationBase.cs b/src/MongoDB.Driver/Core/Operations/MapReduceOperationBase.cs new file mode 100644 index 00000000000..5fcdb9d9826 --- /dev/null +++ b/src/MongoDB.Driver/Core/Operations/MapReduceOperationBase.cs @@ -0,0 +1,249 @@ +/* Copyright 2010-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using MongoDB.Bson; +using MongoDB.Driver.Core.Connections; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; + +namespace MongoDB.Driver.Core.Operations +{ + /// + /// Represents a base class for map-reduce operations. + /// + [Obsolete("Use Aggregation pipeline instead.")] + internal abstract class MapReduceOperationBase + { + // fields + private Collation _collation; + private readonly CollectionNamespace _collectionNamespace; + private BsonDocument _filter; + private BsonJavaScript _finalizeFunction; + private bool? _javaScriptMode; + private long? _limit; + private readonly BsonJavaScript _mapFunction; + private TimeSpan? _maxTime; + private readonly MessageEncoderSettings _messageEncoderSettings; + private readonly BsonJavaScript _reduceFunction; + private BsonDocument _scope; + private BsonDocument _sort; + private bool? _verbose; + + // constructors + /// + /// Initializes a new instance of the class. + /// + /// The collection namespace. + /// The map function. + /// The reduce function. + /// The message encoder settings. + protected MapReduceOperationBase(CollectionNamespace collectionNamespace, BsonJavaScript mapFunction, BsonJavaScript reduceFunction, MessageEncoderSettings messageEncoderSettings) + { + _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); + _mapFunction = Ensure.IsNotNull(mapFunction, nameof(mapFunction)); + _reduceFunction = Ensure.IsNotNull(reduceFunction, nameof(reduceFunction)); + _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); + } + + // properties + /// + /// Gets or sets the collation. + /// + /// + /// The collation. + /// + public Collation Collation + { + get { return _collation; } + set { _collation = value; } + } + + /// + /// Gets the collection namespace. + /// + /// + /// The collection namespace. + /// + public CollectionNamespace CollectionNamespace + { + get { return _collectionNamespace; } + } + + /// + /// Gets or sets the filter. + /// + /// + /// The filter. + /// + public BsonDocument Filter + { + get { return _filter; } + set { _filter = value; } + } + + /// + /// Gets or sets the finalize function. + /// + /// + /// The finalize function. + /// + public BsonJavaScript FinalizeFunction + { + get { return _finalizeFunction; } + set { _finalizeFunction = value; } + } + + /// + /// Gets or sets a value indicating whether objects emitted by the map function remain as JavaScript objects. + /// + /// + /// + /// Setting this value to true can result in faster execution, but requires more memory on the server, and if + /// there are too many emitted objects the map-reduce operation may fail. + /// + /// true if objects emitted by the map function remain as JavaScript objects; otherwise, false. + /// + [Obsolete("JavaScriptMode is ignored by server versions 4.4.0 and newer.")] + public bool? JavaScriptMode + { + get { return _javaScriptMode; } + set { _javaScriptMode = value; } + } + + /// + /// Gets or sets the maximum number of documents to pass to the map function. + /// + /// + /// The maximum number of documents to pass to the map function. + /// + public long? Limit + { + get { return _limit; } + set { _limit = value; } + } + + /// + /// Gets the map function. + /// + /// + /// The map function. + /// + public BsonJavaScript MapFunction + { + get { return _mapFunction; } + } + + /// + /// Gets or sets the maximum time the server should spend on this operation. + /// + /// + /// The maximum time the server should spend on this operation. + /// + public TimeSpan? MaxTime + { + get { return _maxTime; } + set { _maxTime = Ensure.IsNullOrInfiniteOrGreaterThanOrEqualToZero(value, nameof(value)); } + } + + /// + /// Gets the message encoder settings. + /// + /// + /// The message encoder settings. + /// + public MessageEncoderSettings MessageEncoderSettings + { + get { return _messageEncoderSettings; } + } + + /// + /// Gets the reduce function. + /// + /// + /// The reduce function. + /// + public BsonJavaScript ReduceFunction + { + get { return _reduceFunction; } + } + + /// + /// Gets or sets the scope document. + /// + /// + /// The scode document defines global variables that are accessible from the map, reduce and finalize functions. + /// + /// + /// The scope document. + /// + public BsonDocument Scope + { + get { return _scope; } + set { _scope = value; } + } + + /// + /// Gets or sets the sort specification. + /// + /// + /// The sort specification. + /// + public BsonDocument Sort + { + get { return _sort; } + set { _sort = value; } + } + + /// + /// Gets or sets a value indicating whether to include extra information, such as timing, in the result. + /// + /// + /// true if extra information, such as timing, should be included in the result; otherwise, false. + /// + public bool? Verbose + { + get { return _verbose; } + set { _verbose = value; } + } + + // methods + protected internal virtual BsonDocument CreateCommand(OperationContext operationContext, ConnectionDescription connectionDescription, long? transactionNumber = null) + { + return new BsonDocument + { + { "mapReduce", _collectionNamespace.CollectionName }, + { "map", _mapFunction }, + { "reduce", _reduceFunction }, + { "out" , CreateOutputOptions() }, + { "query", _filter, _filter != null }, + { "sort", _sort, _sort != null }, + { "limit", () => _limit.Value, _limit.HasValue }, + { "finalize", _finalizeFunction, _finalizeFunction != null }, + { "scope", _scope, _scope != null }, + { "jsMode", () => _javaScriptMode.Value, _javaScriptMode.HasValue }, + { "verbose", () => _verbose.Value, _verbose.HasValue }, + { "maxTimeMS", () => MaxTimeHelper.ToMaxTimeMS(_maxTime.Value), _maxTime.HasValue && !operationContext.IsRootContextTimeoutConfigured() }, + { "collation", () => _collation.ToBsonDocument(), _collation != null } + }; + } + + /// + /// Creates the output options. + /// + /// The output options. + protected abstract BsonDocument CreateOutputOptions(); + } +} diff --git a/src/MongoDB.Driver/Core/Operations/MapReduceOutputMode.cs b/src/MongoDB.Driver/Core/Operations/MapReduceOutputMode.cs new file mode 100644 index 00000000000..d41781aeeb5 --- /dev/null +++ b/src/MongoDB.Driver/Core/Operations/MapReduceOutputMode.cs @@ -0,0 +1,27 @@ +/* Copyright 2010-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; + +namespace MongoDB.Driver.Core.Operations +{ + [Obsolete("Use Aggregation pipeline instead.")] + internal enum MapReduceOutputMode + { + Replace = 0, + Merge, + Reduce + } +} diff --git a/src/MongoDB.Driver/Core/Operations/MapReduceOutputToCollectionOperation.cs b/src/MongoDB.Driver/Core/Operations/MapReduceOutputToCollectionOperation.cs new file mode 100644 index 00000000000..92654b46412 --- /dev/null +++ b/src/MongoDB.Driver/Core/Operations/MapReduceOutputToCollectionOperation.cs @@ -0,0 +1,287 @@ +/* Copyright 2010-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Threading.Tasks; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Driver.Core.Bindings; +using MongoDB.Driver.Core.Connections; +using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; + +namespace MongoDB.Driver.Core.Operations +{ + /// + /// Represents a map-reduce operation that outputs its results to a collection. + /// + [Obsolete("Use Aggregation pipeline instead.")] + internal sealed class MapReduceOutputToCollectionOperation : MapReduceOperationBase, IWriteOperation, IRetryableWriteOperation + { + // fields + private bool? _bypassDocumentValidation; + private bool _enableOverloadRetargeting; + private int _maxAdaptiveRetries; + private bool _retryRequested; + private bool? _nonAtomicOutput; + private readonly CollectionNamespace _outputCollectionNamespace; + private MapReduceOutputMode _outputMode; + private bool? _shardedOutput; + private WriteConcern _writeConcern; + + // constructors + /// + /// Initializes a new instance of the class. + /// + /// The collection namespace. + /// The output collection namespace. + /// The map function. + /// The reduce function. + /// The message encoder settings. + public MapReduceOutputToCollectionOperation( + CollectionNamespace collectionNamespace, + CollectionNamespace outputCollectionNamespace, + BsonJavaScript mapFunction, + BsonJavaScript reduceFunction, + MessageEncoderSettings messageEncoderSettings) + : base( + collectionNamespace, + mapFunction, + reduceFunction, + messageEncoderSettings) + { + _outputCollectionNamespace = Ensure.IsNotNull(outputCollectionNamespace, nameof(outputCollectionNamespace)); + _outputMode = MapReduceOutputMode.Replace; + } + + // properties + /// + /// Gets or sets a value indicating whether to bypass document validation. + /// + /// + /// A value indicating whether to bypass document validation. + /// + public bool? BypassDocumentValidation + { + get { return _bypassDocumentValidation; } + set { _bypassDocumentValidation = value; } + } + + /// + /// Gets or sets a value indicating whether overload retargeting is enabled. + /// + public bool EnableOverloadRetargeting + { + get { return _enableOverloadRetargeting; } + set { _enableOverloadRetargeting = value; } + } + + /// + /// Gets a value indicating whether the operation is retryable. + /// + public bool IsOperationRetryable => false; + + /// + /// Gets or sets the maximum number of adaptive retries. + /// + public int MaxAdaptiveRetries + { + get { return _maxAdaptiveRetries; } + set { _maxAdaptiveRetries = value; } + } + + /// + /// Gets or sets a value indicating whether a retry was requested. + /// + /// + /// A value indicating whether a retry was requested. + /// + public bool RetryRequested + { + get { return _retryRequested; } + set { _retryRequested = value; } + } + + /// + /// Gets or sets a value indicating whether the server should not lock the database for merge and reduce output modes. + /// + /// + /// true if the server should not lock the database for merge and reduce output modes; otherwise, false. + /// + [Obsolete("NonAtomicOutput is rejected by server versions 4.4.0 and newer.")] + public bool? NonAtomicOutput + { + get { return _nonAtomicOutput; } + set { _nonAtomicOutput = value; } + } + + /// + /// Gets the name of the operation. + /// + public string OperationName => "mapReduce"; + + /// + /// Gets the output collection namespace. + /// + /// + /// The output collection namespace. + /// + public CollectionNamespace OutputCollectionNamespace + { + get { return _outputCollectionNamespace; } + } + + /// + /// Gets or sets the output mode. + /// + /// + /// The output mode. + /// + public MapReduceOutputMode OutputMode + { + get { return _outputMode; } + set { _outputMode = value; } + } + + /// + /// Gets or sets a value indicating whether the output collection should be sharded. + /// + /// + /// true if the output collection should be sharded; otherwise, false. + /// + [Obsolete("ShardedOutput is rejected by server versions 4.4.0 and newer.")] + public bool? ShardedOutput + { + get { return _shardedOutput; } + set { _shardedOutput = value; } + } + + /// + /// Gets or sets the write concern. + /// + /// + /// The write concern. + /// + public WriteConcern WriteConcern + { + get { return _writeConcern; } + set { _writeConcern = value; } + } + + // methods + /// + protected internal override BsonDocument CreateCommand(OperationContext operationContext, ConnectionDescription connectionDescription, long? transactionNumber = null) + { + var command = base.CreateCommand(operationContext, connectionDescription, transactionNumber); + + if (_bypassDocumentValidation.HasValue) + { + command.Add("bypassDocumentValidation", _bypassDocumentValidation.Value); + } + var writeConcern = WriteConcernHelper.GetEffectiveWriteConcern(operationContext, _writeConcern); + if (writeConcern != null) + { + command.Add("writeConcern", writeConcern.ToBsonDocument()); + } + return command; + } + + /// + protected override BsonDocument CreateOutputOptions() + { + var action = _outputMode.ToString().ToLowerInvariant(); + return new BsonDocument + { + { action, _outputCollectionNamespace.CollectionName }, + { "db", _outputCollectionNamespace.DatabaseNamespace.DatabaseName }, + { "sharded", () => _shardedOutput.Value, _shardedOutput.HasValue }, + { "nonAtomic", () => _nonAtomicOutput.Value, _nonAtomicOutput.HasValue } + }; + } + + /// + public BsonDocument Execute(OperationContext operationContext, IWriteBinding binding) + { + using (BeginOperation()) + { + return RetryableWriteOperationExecutor.Execute(operationContext, this, binding, retryRequested: RetryRequested, _maxAdaptiveRetries, _enableOverloadRetargeting); + } + } + + /// + public Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) + { + using (BeginOperation()) + { + return RetryableWriteOperationExecutor.ExecuteAsync(operationContext, this, binding, retryRequested: RetryRequested, _maxAdaptiveRetries, _enableOverloadRetargeting); + } + } + + /// + public BsonDocument Execute(OperationContext operationContext, RetryableWriteContext context) + { + using (BeginOperation()) + { + return RetryableWriteOperationExecutor.Execute(operationContext, this, context); + } + } + + /// + public Task ExecuteAsync(OperationContext operationContext, RetryableWriteContext context) + { + using (BeginOperation()) + { + return RetryableWriteOperationExecutor.ExecuteAsync(operationContext, this, context); + } + } + + /// + public BsonDocument ExecuteAttempt(OperationContext operationContext, RetryableWriteContext context, int attempt, long? transactionNumber) + { + var binding = context.Binding; + var channelSource = context.ChannelSource; + var channel = context.Channel; + + using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel)) + { + var operation = CreateOperation(operationContext, channel.ConnectionDescription, transactionNumber); + return operation.Execute(operationContext, channelBinding); + } + } + + /// + public async Task ExecuteAttemptAsync(OperationContext operationContext, RetryableWriteContext context, int attempt, long? transactionNumber) + { + var binding = context.Binding; + var channelSource = context.ChannelSource; + var channel = context.Channel; + + using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel)) + { + var operation = CreateOperation(operationContext, channel.ConnectionDescription, transactionNumber); + return await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); + } + } + + private IDisposable BeginOperation() => EventContext.BeginOperation("mapReduce"); + + private WriteCommandOperation CreateOperation(OperationContext operationContext, ConnectionDescription connectionDescription, long? transactionNumber) + { + var command = CreateCommand(operationContext, connectionDescription, transactionNumber); + return new WriteCommandOperation(CollectionNamespace.DatabaseNamespace, command, BsonDocumentSerializer.Instance, MessageEncoderSettings, OperationName); + } + } +} diff --git a/src/MongoDB.Driver/FilteredMongoCollectionBase.cs b/src/MongoDB.Driver/FilteredMongoCollectionBase.cs index 48c049971dd..04f8d1aa546 100644 --- a/src/MongoDB.Driver/FilteredMongoCollectionBase.cs +++ b/src/MongoDB.Driver/FilteredMongoCollectionBase.cs @@ -298,6 +298,38 @@ public override Task> DistinctManyAsync(IClientSessio return _wrappedCollection.FindOneAndUpdateAsync(session, CombineFilters(filter), AdjustUpdateDefinition(update, options?.IsUpsert ?? false), options, cancellationToken); } + [Obsolete("Use Aggregation pipeline instead.")] + public override IAsyncCursor MapReduce(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + { + options = options ?? new MapReduceOptions(); + options.Filter = CombineFilters(options.Filter); + return _wrappedCollection.MapReduce(map, reduce, options, cancellationToken); + } + + [Obsolete("Use Aggregation pipeline instead.")] + public override IAsyncCursor MapReduce(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + { + options = options ?? new MapReduceOptions(); + options.Filter = CombineFilters(options.Filter); + return _wrappedCollection.MapReduce(session, map, reduce, options, cancellationToken); + } + + [Obsolete("Use Aggregation pipeline instead.")] + public override Task> MapReduceAsync(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + { + options = options ?? new MapReduceOptions(); + options.Filter = CombineFilters(options.Filter); + return _wrappedCollection.MapReduceAsync(map, reduce, options, cancellationToken); + } + + [Obsolete("Use Aggregation pipeline instead.")] + public override Task> MapReduceAsync(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + { + options = options ?? new MapReduceOptions(); + options.Filter = CombineFilters(options.Filter); + return _wrappedCollection.MapReduceAsync(session, map, reduce, options, cancellationToken); + } + // private methods private FilterDefinition CombineFilters(FilterDefinition filter) { diff --git a/src/MongoDB.Driver/IMongoCollection.cs b/src/MongoDB.Driver/IMongoCollection.cs index 6446ed3d68b..74340ea9bbc 100644 --- a/src/MongoDB.Driver/IMongoCollection.cs +++ b/src/MongoDB.Driver/IMongoCollection.cs @@ -858,6 +858,60 @@ public interface IMongoCollection // TODO: derive from IMongoCollecti /// Task InsertManyAsync(IClientSessionHandle session, IEnumerable documents, InsertManyOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); + /// + /// Executes a map-reduce command. + /// + /// The type of the result. + /// The map function. + /// The reduce function. + /// The options. + /// The cancellation token. + /// A cursor. + [Obsolete("Use Aggregation pipeline instead.")] + IAsyncCursor MapReduce(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Executes a map-reduce command. + /// + /// The type of the result. + /// The session. + /// The map function. + /// The reduce function. + /// The options. + /// The cancellation token. + /// + /// A cursor. + /// + [Obsolete("Use Aggregation pipeline instead.")] + IAsyncCursor MapReduce(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Executes a map-reduce command. + /// + /// The type of the result. + /// The map function. + /// The reduce function. + /// The options. + /// The cancellation token. + /// A Task whose result is a cursor. + [Obsolete("Use Aggregation pipeline instead.")] + Task> MapReduceAsync(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Executes a map-reduce command. + /// + /// The type of the result. + /// The session. + /// The map function. + /// The reduce function. + /// The options. + /// The cancellation token. + /// + /// A Task whose result is a cursor. + /// + [Obsolete("Use Aggregation pipeline instead.")] + Task> MapReduceAsync(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); + /// /// Returns a filtered collection that appears to contain only documents of the derived type. /// All operations using this filtered collection will automatically use discriminators as necessary. diff --git a/src/MongoDB.Driver/MapReduceOptions.cs b/src/MongoDB.Driver/MapReduceOptions.cs new file mode 100644 index 00000000000..5b268609eea --- /dev/null +++ b/src/MongoDB.Driver/MapReduceOptions.cs @@ -0,0 +1,317 @@ +/* Copyright 2010-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver +{ + /// + /// Represents the options for a map-reduce operation. + /// + /// The type of the document. + /// The type of the result. + [Obsolete("Use Aggregation pipeline instead.")] + public sealed class MapReduceOptions + { + // fields + private bool? _bypassDocumentValidation; + private Collation _collation; + private FilterDefinition _filter; + private BsonJavaScript _finalize; + private bool? _javaScriptMode; + private long? _limit; + private TimeSpan? _maxTime; + private MapReduceOutputOptions _outputOptions; + private IBsonSerializer _resultSerializer; + private BsonDocument _scope; + private SortDefinition _sort; + private TimeSpan? _timeout; + private bool? _verbose; + + // properties + /// + /// Gets or sets a value indicating whether to bypass document validation. + /// + public bool? BypassDocumentValidation + { + get { return _bypassDocumentValidation; } + set { _bypassDocumentValidation = value; } + } + + /// + /// Gets or sets the collation. + /// + public Collation Collation + { + get { return _collation; } + set { _collation = value; } + } + + /// + /// Gets or sets the filter. + /// + public FilterDefinition Filter + { + get { return _filter; } + set { _filter = value; } + } + + /// + /// Gets or sets the finalize function. + /// + public BsonJavaScript Finalize + { + get { return _finalize; } + set { _finalize = value; } + } + + /// + /// Gets or sets the java script mode. + /// + [Obsolete("JavaScriptMode is ignored by server versions 4.4.0 and newer.")] + public bool? JavaScriptMode + { + get { return _javaScriptMode; } + set { _javaScriptMode = value; } + } + + /// + /// Gets or sets the limit. + /// + public long? Limit + { + get { return _limit; } + set { _limit = value; } + } + + /// + /// Gets or sets the maximum time. + /// + public TimeSpan? MaxTime + { + get { return _maxTime; } + set { _maxTime = Ensure.IsNullOrInfiniteOrGreaterThanOrEqualToZero(value, nameof(value)); } + } + + /// + /// Gets or sets the output options. + /// + public MapReduceOutputOptions OutputOptions + { + get { return _outputOptions; } + set { _outputOptions = value; } + } + + /// + /// Gets or sets the result serializer. + /// + public IBsonSerializer ResultSerializer + { + get { return _resultSerializer; } + set { _resultSerializer = value; } + } + + /// + /// Gets or sets the scope. + /// + public BsonDocument Scope + { + get { return _scope; } + set { _scope = value; } + } + + /// + /// Gets or sets the sort. + /// + public SortDefinition Sort + { + get { return _sort; } + set { _sort = value; } + } + + /// + /// Gets or sets the operation timeout. + /// + // TODO: CSOT: Make it public when CSOT will be ready for GA + internal TimeSpan? Timeout + { + get => _timeout; + set => _timeout = Ensure.IsNullOrValidTimeout(value, nameof(Timeout)); + } + + /// + /// Gets or sets whether to include timing information. + /// + public bool? Verbose + { + get { return _verbose; } + set { _verbose = value; } + } + } + + /// + /// Represents the output options for a map-reduce operation. + /// + [Obsolete("Use Aggregation pipeline instead.")] + public abstract class MapReduceOutputOptions + { + private static MapReduceOutputOptions __inline = new InlineOutput(); + + private MapReduceOutputOptions() + { } + + /// + /// An inline map-reduce output options. + /// + public static MapReduceOutputOptions Inline + { + get { return __inline; } + } + + /// + /// A merge map-reduce output options. + /// + /// The name of the collection. + /// The name of the database. + /// Whether the output collection should be sharded. + /// Whether the server should not lock the database for the duration of the merge. + /// A merge map-reduce output options. + [Obsolete("Use an overload of Merge that does not have sharded and nonAtomic parameters instead.")] + public static MapReduceOutputOptions Merge(string collectionName, string databaseName = null, bool? sharded = null, bool? nonAtomic = null) + { + Ensure.IsNotNull(collectionName, nameof(collectionName)); + return new CollectionOutput(collectionName, Core.Operations.MapReduceOutputMode.Merge, databaseName, sharded, nonAtomic); + } + + /// + /// A merge map-reduce output options. + /// + /// The name of the collection. + /// The name of the database. + /// A merge map-reduce output options. + public static MapReduceOutputOptions Merge(string collectionName, string databaseName) + { + Ensure.IsNotNull(collectionName, nameof(collectionName)); + return new CollectionOutput(collectionName, Core.Operations.MapReduceOutputMode.Merge, databaseName); + } + + /// + /// A reduce map-reduce output options. + /// + /// The name of the collection. + /// The name of the database. + /// Whether the output collection should be sharded. + /// Whether the server should not lock the database for the duration of the reduce. + /// A reduce map-reduce output options. + [Obsolete("Use an overload of Reduce that does not have sharded and nonAtomic parameters instead.")] + public static MapReduceOutputOptions Reduce(string collectionName, string databaseName = null, bool? sharded = null, bool? nonAtomic = null) + { + Ensure.IsNotNull(collectionName, nameof(collectionName)); + return new CollectionOutput(collectionName, Core.Operations.MapReduceOutputMode.Reduce, databaseName, sharded, nonAtomic); + } + + /// + /// A reduce map-reduce output options. + /// + /// The name of the collection. + /// The name of the database. + /// A reduce map-reduce output options. + public static MapReduceOutputOptions Reduce(string collectionName, string databaseName) + { + Ensure.IsNotNull(collectionName, nameof(collectionName)); + return new CollectionOutput(collectionName, Core.Operations.MapReduceOutputMode.Reduce, databaseName); + } + + /// + /// A replace map-reduce output options. + /// + /// The name of the collection. + /// Name of the database. + /// Whether the output collection should be sharded. + /// A replace map-reduce output options. + [Obsolete("Use an overload of Replace that does not have a sharded parameter instead.")] + public static MapReduceOutputOptions Replace(string collectionName, string databaseName = null, bool? sharded = null) + { + Ensure.IsNotNull(collectionName, nameof(collectionName)); + return new CollectionOutput(collectionName, Core.Operations.MapReduceOutputMode.Replace, databaseName, sharded, null); + } + + /// + /// A replace map-reduce output options. + /// + /// The name of the collection. + /// Name of the database. + /// A replace map-reduce output options. + public static MapReduceOutputOptions Replace(string collectionName, string databaseName) + { + Ensure.IsNotNull(collectionName, nameof(collectionName)); + return new CollectionOutput(collectionName, Core.Operations.MapReduceOutputMode.Replace, databaseName); + } + + internal sealed class InlineOutput : MapReduceOutputOptions + { + internal InlineOutput() + { } + } + + internal sealed class CollectionOutput : MapReduceOutputOptions + { + private readonly string _collectionName; + private readonly string _databaseName; + private readonly bool? _nonAtomic; + private readonly Core.Operations.MapReduceOutputMode _outputMode; + private readonly bool? _sharded; + + internal CollectionOutput(string collectionName, Core.Operations.MapReduceOutputMode outputMode, string databaseName = null, bool? sharded = null, bool? nonAtomic = null) + { + _collectionName = collectionName; + _outputMode = outputMode; + _databaseName = databaseName; + _sharded = sharded; + _nonAtomic = nonAtomic; + } + + public string CollectionName + { + get { return _collectionName; } + } + + public string DatabaseName + { + get { return _databaseName; } + } + + [Obsolete("NonAtomic is rejected by server versions 4.4.0 and newer.")] + public bool? NonAtomic + { + get { return _nonAtomic; } + } + + public Core.Operations.MapReduceOutputMode OutputMode + { + get { return _outputMode; } + } + + [Obsolete("Sharded is rejected by server versions 4.4.0 and newer.")] + public bool? Sharded + { + get { return _sharded; } + } + } + } +} diff --git a/src/MongoDB.Driver/MongoCollectionBase.cs b/src/MongoDB.Driver/MongoCollectionBase.cs index 536b139a3a5..a2d5314d451 100644 --- a/src/MongoDB.Driver/MongoCollectionBase.cs +++ b/src/MongoDB.Driver/MongoCollectionBase.cs @@ -514,6 +514,27 @@ private async Task InsertManyAsync(IEnumerable docu return InsertManyResult.FromBulkWriteResult(bulkWriteResult, DocumentSerializer); } + [Obsolete("Use Aggregation pipeline instead.")] + public virtual IAsyncCursor MapReduce(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + { + throw new NotImplementedException(); + } + + [Obsolete("Use Aggregation pipeline instead.")] + public virtual IAsyncCursor MapReduce(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + { + throw new NotImplementedException(); + } + + [Obsolete("Use Aggregation pipeline instead.")] + public abstract Task> MapReduceAsync(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)); + + [Obsolete("Use Aggregation pipeline instead.")] + public virtual Task> MapReduceAsync(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + { + throw new NotImplementedException(); + } + public abstract IFilteredMongoCollection OfType() where TDerivedDocument : TDocument; public virtual ReplaceOneResult ReplaceOne(FilterDefinition filter, TDocument replacement, ReplaceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) diff --git a/src/MongoDB.Driver/MongoCollectionImpl.cs b/src/MongoDB.Driver/MongoCollectionImpl.cs index 4dca0a41124..39158302676 100644 --- a/src/MongoDB.Driver/MongoCollectionImpl.cs +++ b/src/MongoDB.Driver/MongoCollectionImpl.cs @@ -491,6 +491,70 @@ public override Task FindOneAndUpdateAsync(IClientSess return ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken); } + [Obsolete("Use Aggregation pipeline instead.")] + public override IAsyncCursor MapReduce(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default) + { + using var session = _operationExecutor.StartImplicitSession(); + return MapReduce(session, map, reduce, options, cancellationToken: cancellationToken); + } + + [Obsolete("Use Aggregation pipeline instead.")] + public override IAsyncCursor MapReduce(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default) + { + Ensure.IsNotNull(session, nameof(session)); + Ensure.IsNotNull(map, nameof(map)); + Ensure.IsNotNull(reduce, nameof(reduce)); + options ??= new MapReduceOptions(); + + var outputOptions = options.OutputOptions ?? MapReduceOutputOptions.Inline; + var resultSerializer = ResolveResultSerializer(options.ResultSerializer); + + var renderArgs = GetRenderArgs(); + if (outputOptions == MapReduceOutputOptions.Inline) + { + var operation = CreateMapReduceOperation(map, reduce, options, resultSerializer, renderArgs); + return ExecuteReadOperation(session, operation, options.Timeout, cancellationToken); + } + else + { + var mapReduceOperation = CreateMapReduceOutputToCollectionOperation(map, reduce, options, outputOptions, renderArgs); + ExecuteWriteOperation(session, mapReduceOperation, options.Timeout, cancellationToken); + return CreateMapReduceOutputToCollectionResultCursor(session, options, mapReduceOperation.OutputCollectionNamespace, resultSerializer); + } + } + + [Obsolete("Use Aggregation pipeline instead.")] + public override async Task> MapReduceAsync(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default) + { + using var session = _operationExecutor.StartImplicitSession(); + return await MapReduceAsync(session, map, reduce, options, cancellationToken).ConfigureAwait(false); + } + + [Obsolete("Use Aggregation pipeline instead.")] + public override async Task> MapReduceAsync(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default) + { + Ensure.IsNotNull(session, nameof(session)); + Ensure.IsNotNull(map, nameof(map)); + Ensure.IsNotNull(reduce, nameof(reduce)); + options ??= new MapReduceOptions(); + + var outputOptions = options.OutputOptions ?? MapReduceOutputOptions.Inline; + var resultSerializer = ResolveResultSerializer(options.ResultSerializer); + + var renderArgs = GetRenderArgs(); + if (outputOptions == MapReduceOutputOptions.Inline) + { + var operation = CreateMapReduceOperation(map, reduce, options, resultSerializer, renderArgs); + return await ExecuteReadOperationAsync(session, operation, options.Timeout, cancellationToken).ConfigureAwait(false); + } + else + { + var mapReduceOperation = CreateMapReduceOutputToCollectionOperation(map, reduce, options, outputOptions, renderArgs); + await ExecuteWriteOperationAsync(session, mapReduceOperation, options.Timeout, cancellationToken).ConfigureAwait(false); + return CreateMapReduceOutputToCollectionResultCursor(session, options, mapReduceOperation.OutputCollectionNamespace, resultSerializer); + } + } + public override IFilteredMongoCollection OfType() { var derivedDocumentSerializer = _settings.SerializationDomain.LookupSerializer(); @@ -1028,6 +1092,112 @@ private FindOperation CreateFindOperation( }; } +#pragma warning disable CS0618 // Type or member is obsolete + private MapReduceOperation CreateMapReduceOperation( + BsonJavaScript map, + BsonJavaScript reduce, + MapReduceOptions options, + IBsonSerializer resultSerializer, + RenderArgs renderArgs) + { + return new MapReduceOperation( +#pragma warning restore CS0618 // Type or member is obsolete + _collectionNamespace, + map, + reduce, + resultSerializer, + _messageEncoderSettings) + { + Collation = options.Collation, + Filter = options.Filter?.Render(renderArgs), + FinalizeFunction = options.Finalize, +#pragma warning disable 618 + JavaScriptMode = options.JavaScriptMode, +#pragma warning restore 618 + Limit = options.Limit, + MaxTime = options.MaxTime, + ReadConcern = _settings.ReadConcern, + Scope = options.Scope, + Sort = options.Sort?.Render(renderArgs), + Verbose = options.Verbose + }; + } + +#pragma warning disable CS0618 // Type or member is obsolete + private MapReduceOutputToCollectionOperation CreateMapReduceOutputToCollectionOperation( + BsonJavaScript map, + BsonJavaScript reduce, + MapReduceOptions options, + MapReduceOutputOptions outputOptions, + RenderArgs renderArgs) + { + var collectionOutputOptions = (MapReduceOutputOptions.CollectionOutput)outputOptions; + var databaseNamespace = collectionOutputOptions.DatabaseName == null ? + _collectionNamespace.DatabaseNamespace : + new DatabaseNamespace(collectionOutputOptions.DatabaseName); + var outputCollectionNamespace = new CollectionNamespace(databaseNamespace, collectionOutputOptions.CollectionName); + + return new MapReduceOutputToCollectionOperation( +#pragma warning restore CS0618 // Type or member is obsolete + _collectionNamespace, + outputCollectionNamespace, + map, + reduce, + _messageEncoderSettings) + { + BypassDocumentValidation = options.BypassDocumentValidation, + Collation = options.Collation, + EnableOverloadRetargeting = _database.Client.Settings.EnableOverloadRetargeting, + Filter = options.Filter?.Render(renderArgs), + FinalizeFunction = options.Finalize, +#pragma warning disable 618 + JavaScriptMode = options.JavaScriptMode, +#pragma warning restore 618 + Limit = options.Limit, + MaxAdaptiveRetries = _database.Client.Settings.MaxAdaptiveRetries, + MaxTime = options.MaxTime, +#pragma warning disable 618 + NonAtomicOutput = collectionOutputOptions.NonAtomic, +#pragma warning restore 618 + OutputMode = collectionOutputOptions.OutputMode, + RetryRequested = _database.Client.Settings.RetryWrites, + Scope = options.Scope, +#pragma warning disable 618 + ShardedOutput = collectionOutputOptions.Sharded, +#pragma warning restore 618 + Sort = options.Sort?.Render(renderArgs), + Verbose = options.Verbose, + WriteConcern = _settings.WriteConcern + }; + } + +#pragma warning disable CS0618 // Type or member is obsolete + private IAsyncCursor CreateMapReduceOutputToCollectionResultCursor(IClientSessionHandle session, MapReduceOptions options, CollectionNamespace outputCollectionNamespace, IBsonSerializer resultSerializer) +#pragma warning restore CS0618 // Type or member is obsolete + { + var findOperation = new FindOperation( + outputCollectionNamespace, + resultSerializer, + _messageEncoderSettings) + { + Collation = options.Collation, + EnableOverloadRetargeting = _database.Client.Settings.EnableOverloadRetargeting, + MaxAdaptiveRetries = _database.Client.Settings.MaxAdaptiveRetries, + MaxTime = options.MaxTime, + ReadConcern = _settings.ReadConcern, + RetryRequested = _database.Client.Settings.RetryReads + }; + + // we want to delay execution of the find because the user may + // not want to iterate the results at all... + var forkedSession = session.Fork(); + var deferredCursor = new DeferredAsyncCursor( + () => forkedSession.Dispose(), + ct => ExecuteReadOperation(forkedSession, findOperation, ReadPreference.Primary, options?.Timeout, ct), + ct => ExecuteReadOperationAsync(forkedSession, findOperation, ReadPreference.Primary, options?.Timeout, ct)); + return deferredCursor; + } + private OperationContext CreateOperationContext(IClientSessionHandle session, TimeSpan? timeout, string operationName, CancellationToken cancellationToken) { var operationContext = session.WrappedCoreSession.CurrentTransaction?.OperationContext; @@ -1182,6 +1352,21 @@ private IEnumerable RenderArrayFilters(IEnumerable ResolveResultSerializer(IBsonSerializer resultSerializer) + { + if (resultSerializer != null) + { + return resultSerializer; + } + + if (typeof(TResult) == typeof(TDocument) && _documentSerializer != null) + { + return (IBsonSerializer)_documentSerializer; + } + + return _settings.SerializationDomain.LookupSerializer(); + } + // nested types private class MongoIndexManager : MongoIndexManagerBase { diff --git a/tests/MongoDB.Driver.TestHelpers/Core/JsonDrivenTests/CommandStartedEventAsserter.cs b/tests/MongoDB.Driver.TestHelpers/Core/JsonDrivenTests/CommandStartedEventAsserter.cs index 4be510543a5..4db40dbe90b 100644 --- a/tests/MongoDB.Driver.TestHelpers/Core/JsonDrivenTests/CommandStartedEventAsserter.cs +++ b/tests/MongoDB.Driver.TestHelpers/Core/JsonDrivenTests/CommandStartedEventAsserter.cs @@ -157,6 +157,21 @@ private void AssertCommandAspect(BsonDocument actualCommand, string name, BsonVa { switch (name) { + case "out": + if (commandName == "mapReduce") + { + if (expectedValue is BsonString && + actualValue.IsBsonDocument && + actualValue.AsBsonDocument.Contains("replace") && + actualValue["replace"] == expectedValue.AsString) + { + // allow short form for "out" to be equivalent to the long form + // Assumes that the driver is correctly generating the following + // fields: db, sharded, nonAtomic + return; + } + } + break; case "encryptedFields": if (commandName == "create") // create encrypted collection { diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOperationBaseTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOperationBaseTests.cs new file mode 100644 index 00000000000..2b99708eee4 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOperationBaseTests.cs @@ -0,0 +1,598 @@ +/* Copyright 2010-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; +using MongoDB.TestHelpers.XunitExtensions; +using Xunit; + +namespace MongoDB.Driver.Core.Operations +{ + public class MapReduceOperationBaseTests : OperationTestBase + { + // fields + private readonly BsonJavaScript _mapFunction = "map"; + private readonly BsonJavaScript _reduceFunction = "reduce"; + + // test methods + [Theory] + [ParameterAttributeData] + public void Collation_should_get_and_set_value( + [Values(null, "en_US", "fr_CA")] + string locale) + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); + var value = locale == null ? null : new Collation(locale); + + subject.Collation = value; + var result = subject.Collation; + + result.Should().BeSameAs(value); + } + + [Theory] + [ParameterAttributeData] + public void CollectionNamespace_should_get_value( + [Values("a", "b")] + string collectionName) + { + var collectionNamespace = new CollectionNamespace(_collectionNamespace.DatabaseNamespace, collectionName); + var subject = new FakeMapReduceOperation(collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); + + var result = subject.CollectionNamespace; + + result.Should().BeSameAs(collectionNamespace); + } + + [Fact] + public void constructor_should_initialize_instance() + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); + + subject.CollectionNamespace.Should().BeSameAs(_collectionNamespace); + subject.MapFunction.Should().BeSameAs(_mapFunction); + subject.ReduceFunction.Should().BeSameAs(_reduceFunction); + subject.MessageEncoderSettings.Should().BeSameAs(_messageEncoderSettings); + + subject.Collation.Should().BeNull(); + subject.Filter.Should().BeNull(); + subject.FinalizeFunction.Should().BeNull(); +#pragma warning disable 618 + subject.JavaScriptMode.Should().NotHaveValue(); +#pragma warning restore 618 + subject.Limit.Should().NotHaveValue(); + subject.MaxTime.Should().NotHaveValue(); + subject.Scope.Should().BeNull(); + subject.Sort.Should().BeNull(); + subject.Verbose.Should().NotHaveValue(); + } + + [Fact] + public void constructor_should_throw_when_collectionNamespace_is_null() + { + var exception = Record.Exception(() => new FakeMapReduceOperation(null, _mapFunction, _reduceFunction, _messageEncoderSettings)); + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("collectionNamespace"); + } + + [Fact] + public void constructor_should_throw_when_mapFunction_is_null() + { + var exception = Record.Exception(() => new FakeMapReduceOperation(_collectionNamespace, null, _reduceFunction, _messageEncoderSettings)); + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("mapFunction"); + } + + [Fact] + public void constructor_should_throw_when_messageEncoderSettings_is_null() + { + var exception = Record.Exception(() => new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, null)); + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("messageEncoderSettings"); + } + + [Fact] + public void constructor_should_throw_when_reduceFunction_is_null() + { + var exception = Record.Exception(() => new FakeMapReduceOperation(_collectionNamespace, _mapFunction, null, _messageEncoderSettings)); + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("reduceFunction"); + } + + [Fact] + public void CreateCommand_should_return_the_expected_result() + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); + var connectionDescription = OperationTestHelper.CreateConnectionDescription(); + using var session = OperationTestHelper.CreateSession(); + using var operationContext = new OperationContext(session); + + var result = subject.CreateCommand(operationContext, connectionDescription); + + var expectedResult = new BsonDocument + { + { "mapReduce", _collectionNamespace.CollectionName }, + { "map", _mapFunction }, + { "reduce", _reduceFunction }, + { "out", new BsonDocument("fake", 1) } + }; + result.Should().Be(expectedResult); + } + + [Theory] + [ParameterAttributeData] + public void CreateCommand_should_return_the_expected_result_when_Collation_is_provided( + [Values(null, "en_US", "fr_CA")] + string locale) + { + var collation = locale == null ? null : new Collation(locale); + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) + { + Collation = collation + }; + var connectionDescription = OperationTestHelper.CreateConnectionDescription(); + using var session = OperationTestHelper.CreateSession(); + using var operationContext = new OperationContext(session); + + var result = subject.CreateCommand(operationContext, connectionDescription); + + var expectedResult = new BsonDocument + { + { "mapReduce", _collectionNamespace.CollectionName }, + { "map", _mapFunction }, + { "reduce", _reduceFunction }, + { "out", new BsonDocument("fake", 1) }, + { "collation", () => collation.ToBsonDocument(), collation != null } + }; + result.Should().Be(expectedResult); + } + + [Theory] + [ParameterAttributeData] + public void CreateCommand_should_return_the_expected_result_when_Filter_is_provided( + [Values(null, "{ x : 1 }", "{ x : 2 }")] + string filterString) + { + var filter = filterString == null ? null : BsonDocument.Parse(filterString); + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) + { + Filter = filter + }; + var connectionDescription = OperationTestHelper.CreateConnectionDescription(); + using var session = OperationTestHelper.CreateSession(); + using var operationContext = new OperationContext(session); + + var result = subject.CreateCommand(operationContext, connectionDescription); + + var expectedResult = new BsonDocument + { + { "mapReduce", _collectionNamespace.CollectionName }, + { "map", _mapFunction }, + { "reduce", _reduceFunction }, + { "out", new BsonDocument("fake", 1) }, + { "query", filter, filter != null } + }; + result.Should().Be(expectedResult); + } + + [Theory] + [ParameterAttributeData] + public void CreateCommand_should_return_the_expected_result_when_FinalizeFunction_is_provided( + [Values(null, "a", "b")] + string code) + { + var finalizeFunction = code == null ? null : new BsonJavaScript(code); + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) + { + FinalizeFunction = finalizeFunction + }; + var connectionDescription = OperationTestHelper.CreateConnectionDescription(); + using var session = OperationTestHelper.CreateSession(); + using var operationContext = new OperationContext(session); + + var result = subject.CreateCommand(operationContext, connectionDescription); + + var expectedResult = new BsonDocument + { + { "mapReduce", _collectionNamespace.CollectionName }, + { "map", _mapFunction }, + { "reduce", _reduceFunction }, + { "out", new BsonDocument("fake", 1) }, + { "finalize", finalizeFunction, finalizeFunction != null } + }; + result.Should().Be(expectedResult); + } + + [Theory] + [ParameterAttributeData] + public void CreateCommand_should_return_the_expected_result_when_JavaScriptMode_is_provided( + [Values(null, false, true)] + bool? javaScriptMode) + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) + { +#pragma warning disable 618 + JavaScriptMode = javaScriptMode +#pragma warning restore 618 + }; + var connectionDescription = OperationTestHelper.CreateConnectionDescription(); + using var session = OperationTestHelper.CreateSession(); + using var operationContext = new OperationContext(session); + + var result = subject.CreateCommand(operationContext, connectionDescription); + + var expectedResult = new BsonDocument + { + { "mapReduce", _collectionNamespace.CollectionName }, + { "map", _mapFunction }, + { "reduce", _reduceFunction }, + { "out", new BsonDocument("fake", 1) }, + { "jsMode", () => javaScriptMode.Value, javaScriptMode.HasValue } + }; + result.Should().Be(expectedResult); + } + + [Theory] + [ParameterAttributeData] + public void CreateCommand_should_return_the_expected_result_when_Limit_is_provided( + [Values(null, 1L, 2L)] + long? limit) + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) + { + Limit = limit + }; + var connectionDescription = OperationTestHelper.CreateConnectionDescription(); + using var session = OperationTestHelper.CreateSession(); + using var operationContext = new OperationContext(session); + + var result = subject.CreateCommand(operationContext, connectionDescription); + + var expectedResult = new BsonDocument + { + { "mapReduce", _collectionNamespace.CollectionName }, + { "map", _mapFunction }, + { "reduce", _reduceFunction }, + { "out", new BsonDocument("fake", 1) }, + { "limit", () => limit.Value, limit.HasValue } + }; + result.Should().Be(expectedResult); + } + + [Theory] + [InlineData(-10000, 0)] + [InlineData(0, 0)] + [InlineData(1, 1)] + [InlineData(9999, 1)] + [InlineData(10000, 1)] + [InlineData(10001, 2)] + public void CreateCommand_should_return_expected_result_when_MaxTime_is_set(long maxTimeTicks, int expectedMaxTimeMS) + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) + { + MaxTime = TimeSpan.FromTicks(maxTimeTicks) + }; + var connectionDescription = OperationTestHelper.CreateConnectionDescription(); + using var session = OperationTestHelper.CreateSession(); + using var operationContext = new OperationContext(session); + + var result = subject.CreateCommand(operationContext, connectionDescription); + + var expectedResult = new BsonDocument + { + { "mapReduce", _collectionNamespace.CollectionName }, + { "map", _mapFunction }, + { "reduce", _reduceFunction }, + { "out", new BsonDocument("fake", 1) }, + { "maxTimeMS", expectedMaxTimeMS } + }; + result.Should().Be(expectedResult); + result["maxTimeMS"].BsonType.Should().Be(BsonType.Int32); + } + + [Theory] + [InlineData(42)] + [InlineData(-1)] + public void CreateCommand_should_ignore_maxtime_if_timeout_specified(int timeoutMs) + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) + { + MaxTime = TimeSpan.FromTicks(10) + }; + var connectionDescription = OperationTestHelper.CreateConnectionDescription(); + using var operationContext = new OperationContext(OperationTestHelper.CreateSession(), timeout: TimeSpan.FromMilliseconds(timeoutMs)); + + var result = subject.CreateCommand(operationContext, connectionDescription); + + result.Should().NotContain("maxTimeMS"); + } + + [Theory] + [ParameterAttributeData] + public void CreateCommand_should_return_the_expected_result_when_Scope_is_provided( + [Values(null, "{ x : 1 }", "{ x : 2 }")] + string scopeString) + { + var scope = scopeString == null ? null : BsonDocument.Parse(scopeString); + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) + { + Scope = scope + }; + var connectionDescription = OperationTestHelper.CreateConnectionDescription(); + using var session = OperationTestHelper.CreateSession(); + using var operationContext = new OperationContext(session); + + var result = subject.CreateCommand(operationContext, connectionDescription); + + var expectedResult = new BsonDocument + { + { "mapReduce", _collectionNamespace.CollectionName }, + { "map", _mapFunction }, + { "reduce", _reduceFunction }, + { "out", new BsonDocument("fake", 1) }, + { "scope", scope, scope != null } + }; + result.Should().Be(expectedResult); + } + + [Theory] + [ParameterAttributeData] + public void CreateCommand_should_return_the_expected_result_when_Sort_is_provided( + [Values(null, "{ x : 1 }", "{ x : -1 }")] + string sortString) + { + var sort = sortString == null ? null : BsonDocument.Parse(sortString); + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) + { + Sort = sort + }; + var connectionDescription = OperationTestHelper.CreateConnectionDescription(); + using var session = OperationTestHelper.CreateSession(); + using var operationContext = new OperationContext(session); + + var result = subject.CreateCommand(operationContext, connectionDescription); + + var expectedResult = new BsonDocument + { + { "mapReduce", _collectionNamespace.CollectionName }, + { "map", _mapFunction }, + { "reduce", _reduceFunction }, + { "out", new BsonDocument("fake", 1) }, + { "sort", sort, sort != null } + }; + result.Should().Be(expectedResult); + } + + [Theory] + [ParameterAttributeData] + public void CreateCommand_should_return_the_expected_result_when_Verbose_is_provided( + [Values(null, false, true)] + bool? verbose) + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) + { + Verbose = verbose + }; + var connectionDescription = OperationTestHelper.CreateConnectionDescription(); + using var session = OperationTestHelper.CreateSession(); + using var operationContext = new OperationContext(session); + + var result = subject.CreateCommand(operationContext, connectionDescription); + + var expectedResult = new BsonDocument + { + { "mapReduce", _collectionNamespace.CollectionName }, + { "map", _mapFunction }, + { "reduce", _reduceFunction }, + { "out", new BsonDocument("fake", 1) }, + { "verbose", () => verbose.Value, verbose.HasValue } + }; + result.Should().Be(expectedResult); + } + + [Theory] + [ParameterAttributeData] + public void Filter_should_get_and_set_value( + [Values(null, "{ x : 1 }", "{ x : 2 }")] + string valueString) + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); + var value = valueString == null ? null : BsonDocument.Parse(valueString); + + subject.Filter = value; + var result = subject.Filter; + + result.Should().BeSameAs(value); + } + + [Theory] + [ParameterAttributeData] + public void FinalizeFunction_should_get_and_set_value( + [Values(null, "a", "b")] + string code) + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); + var value = code == null ? null : new BsonJavaScript(code); + + subject.FinalizeFunction = value; + var result = subject.FinalizeFunction; + + result.Should().BeSameAs(value); + } + + [Theory] + [ParameterAttributeData] + public void JavaScriptMode_should_get_and_set_value( + [Values(null, false, true)] + bool? value) + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); + +#pragma warning disable 618 + subject.JavaScriptMode = value; + var result = subject.JavaScriptMode; +#pragma warning restore 618 + + result.Should().Be(value); + } + + [Theory] + [ParameterAttributeData] + public void Limit_should_get_and_set_value( + [Values(null, 0L, 1L)] + long? value) + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); + + subject.Limit = value; + var result = subject.Limit; + + result.Should().Be(value); + } + + [Theory] + [ParameterAttributeData] + public void MapFunction_should_get_value( + [Values("a", "b")] + string code) + { + var mapFunction = new BsonJavaScript(code); + var subject = new FakeMapReduceOperation(_collectionNamespace, mapFunction, _reduceFunction, _messageEncoderSettings); + + var result = subject.MapFunction; + + result.Should().BeSameAs(mapFunction); + } + + [Theory] + [ParameterAttributeData] + public void MaxTime_get_and_set_should_work( + [Values(-10000, 0, 1, 10000, 99999)] long maxTimeTicks) + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); + var value = TimeSpan.FromTicks(maxTimeTicks); + + subject.MaxTime = value; + var result = subject.MaxTime; + + result.Should().Be(value); + } + + [Theory] + [ParameterAttributeData] + public void MaxTime_set_should_throw_when_value_is_invalid( + [Values(-10001, -9999, -1)] long maxTimeTicks) + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); + var value = TimeSpan.FromTicks(maxTimeTicks); + + var exception = Record.Exception(() => subject.MaxTime = value); + + var e = exception.Should().BeOfType().Subject; + e.ParamName.Should().Be("value"); + } + + [Fact] + public void MessageEncoderSettings_should_get_value() + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); + + var result = subject.MessageEncoderSettings; + + result.Should().BeSameAs(_messageEncoderSettings); + } + + [Fact] + public void ReduceFunction_should_get_value() + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); + + var result = subject.ReduceFunction; + + result.Should().BeSameAs(_reduceFunction); + } + + [Theory] + [ParameterAttributeData] + public void Scope_should_get_and_set_value( + [Values(null, "{ x : 1 }", "{ x : 2 }")] + string valueString) + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); + var value = valueString == null ? null : BsonDocument.Parse(valueString); + + subject.Scope = value; + var result = subject.Scope; + + result.Should().Be(value); + } + + [Theory] + [ParameterAttributeData] + public void Sort_should_get_and_set_value( + [Values(null, "{ x : 1 }", "{ x : -1 }")] + string valueString) + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); + var value = valueString == null ? null : BsonDocument.Parse(valueString); + + subject.Sort = value; + var result = subject.Sort; + + result.Should().Be(value); + } + + [Theory] + [ParameterAttributeData] + public void Verbose_should_get_and_set_value( + [Values(null, false, true)] + bool? value) + { + var subject = new FakeMapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); + + subject.Verbose = value; + var result = subject.Verbose; + + result.Should().Be(value); + } + + // nested types +#pragma warning disable CS0618 // Type or member is obsolete + private class FakeMapReduceOperation : MapReduceOperationBase +#pragma warning restore CS0618 // Type or member is obsolete + { + public FakeMapReduceOperation( + CollectionNamespace collectionNamespace, + BsonJavaScript mapFunction, + BsonJavaScript reduceFunction, + MessageEncoderSettings messageEncoderSettings + ) + : base(collectionNamespace, mapFunction, reduceFunction, messageEncoderSettings) + { + } + + protected override BsonDocument CreateOutputOptions() + { + return new BsonDocument("fake", 1); + } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOperationTests.cs new file mode 100644 index 00000000000..361507786a7 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOperationTests.cs @@ -0,0 +1,593 @@ +/* Copyright 2010-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Reflection; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Driver.Core.Bindings; +using MongoDB.Driver.Core.Clusters; +using MongoDB.Driver.Core.TestHelpers; +using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; +using Xunit; + +namespace MongoDB.Driver.Core.Operations +{ + public class MapReduceOperationTests : OperationTestBase + { + // fields + private readonly BsonJavaScript _mapFunction; + private readonly BsonJavaScript _reduceFunction; + private readonly IBsonSerializer _resultSerializer; + + // constructors + public MapReduceOperationTests() + { + _mapFunction = "function() { emit(this.x, this.v); }"; + _reduceFunction = "function(key, values) { var sum = 0; for (var i = 0; i < values.length; i++) { sum += values[i]; }; return sum; }"; + _resultSerializer = BsonDocumentSerializer.Instance; + } + + // test methods + [Fact] + public void constructor_should_initialize_instance() + { +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + + subject.CollectionNamespace.Should().BeSameAs(_collectionNamespace); + subject.MapFunction.Should().BeSameAs(_mapFunction); + subject.ReduceFunction.Should().BeSameAs(_reduceFunction); + subject.ResultSerializer.Should().BeSameAs(_resultSerializer); + subject.MessageEncoderSettings.Should().BeSameAs(_messageEncoderSettings); + + subject.Collation.Should().BeNull(); + subject.Filter.Should().BeNull(); + subject.FinalizeFunction.Should().BeNull(); +#pragma warning disable 618 + subject.JavaScriptMode.Should().NotHaveValue(); +#pragma warning restore 618 + subject.Limit.Should().NotHaveValue(); + subject.MaxTime.Should().NotHaveValue(); + subject.ReadConcern.Should().BeSameAs(ReadConcern.Default); + subject.Scope.Should().BeNull(); + subject.Sort.Should().BeNull(); + subject.Verbose.Should().NotHaveValue(); + } + + [Fact] + public void constructor_should_throw_when_resultSerializer_is_null() + { +#pragma warning disable CS0618 // Type or member is obsolete + var exception = Record.Exception(() => new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, null, _messageEncoderSettings)); +#pragma warning restore CS0618 // Type or member is obsolete + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("resultSerializer"); + } + + [Theory] + [ParameterAttributeData] + public void ReadConcern_get_and_set_should_work( + [Values(ReadConcernLevel.Linearizable, ReadConcernLevel.Local)] + ReadConcernLevel level) + { +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + var value = new ReadConcern(level); + + subject.ReadConcern = value; + var result = subject.ReadConcern; + + result.Should().Be(value); + } + + [Fact] + public void ReadConcern_set_should_throw_when_value_is_null() + { +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + + var exception = Record.Exception(() => subject.ReadConcern = null); + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("value"); + } + + [Fact] + public void ResultSerializer_should_get_value() + { +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + + var result = subject.ResultSerializer; + + result.Should().BeSameAs(_resultSerializer); + } + + [Fact] + public void CreateOutputOptions_should_return_expected_result() + { +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + var subjectReflector = new Reflector(subject); + + var result = subjectReflector.CreateOutputOptions(); + + result.Should().Be("{ inline : 1 }"); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_results( + [Values(false, true)] + bool async) + { + RequireServer.Check(); + EnsureTestData(); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + + var cursor = ExecuteOperation(subject, async); + var results = ReadCursorToEnd(cursor, async); + + results.Should().BeEquivalentTo( + BsonDocument.Parse("{ _id : 1, value : 3 }"), + BsonDocument.Parse("{ _id : 2, value : 4 }")); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_results_when_Collation_is_set( + [Values(false, true)] + bool caseSensitive, + [Values(false, true)] + bool async) + { + RequireServer.Check(); + EnsureTestData(); + var collation = new Collation("en_US", caseLevel: caseSensitive, strength: CollationStrength.Primary); + var filter = BsonDocument.Parse("{ y : 'a' }"); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + Collation = collation, + Filter = filter + }; + + var cursor = ExecuteOperation(subject, async); + var results = ReadCursorToEnd(cursor, async); + + BsonDocument[] expectedResults; + if (caseSensitive) + { + expectedResults = new[] + { + BsonDocument.Parse("{ _id : 1, value : 1 }"), + BsonDocument.Parse("{ _id : 2, value : 4 }") + }; + } + else + { + expectedResults = new[] + { + BsonDocument.Parse("{ _id : 1, value : 3 }"), + BsonDocument.Parse("{ _id : 2, value : 4 }") + }; + } + results.Should().BeEquivalentTo(expectedResults); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_results_when_Filter_is_set( + [Values(false, true)] + bool async) + { + RequireServer.Check(); + EnsureTestData(); + var filter = BsonDocument.Parse("{ y : 'a' }"); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + Filter = filter + }; + + var cursor = ExecuteOperation(subject, async); + var results = ReadCursorToEnd(cursor, async); + + results.Should().BeEquivalentTo( + BsonDocument.Parse("{ _id : 1, value : 1 }"), + BsonDocument.Parse("{ _id : 2, value : 4 }")); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_results_when_FinalizeFunction_is_set( + [Values(false, true)] + bool async) + { + RequireServer.Check(); + EnsureTestData(); + var finalizeFunction = new BsonJavaScript("function(key, reduced) { return -reduced; }"); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + FinalizeFunction = finalizeFunction + }; + + var cursor = ExecuteOperation(subject, async); + var results = ReadCursorToEnd(cursor, async); + + results.Should().BeEquivalentTo( + BsonDocument.Parse("{ _id : 1, value : -3 }"), + BsonDocument.Parse("{ _id : 2, value : -4 }")); + } + + // TODO: figure out why test fails when JavaScriptMode = true (server bug?) + + //[Theory] + //[ParameterAttributeData] + //public void Execute_should_return_expected_results_when_JavaScriptMode_is_set( + // [Values(null, false, true)] + // bool? javaScriptMode, + // [Values(false, true)] + // bool async) + //{ + // RequireServer.Check(); + // EnsureTestData(); + // var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) + // { + // JavaScriptMode = javaScriptMode + // }; + + // var cursor = ExecuteOperation(subject, async); + // var results = ReadCursorToEnd(cursor, async); + + // // the results are the same either way, but at least we're smoke testing JavaScriptMode + // results.Should().Equal( + // BsonDocument.Parse("{ _id : 1, value : 3 }"), + // BsonDocument.Parse("{ _id : 2, value : 4 }")); + //} + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_results_when_Limit_is_set( + [Values(1, 2)] + long limit, + [Values(false, true)] + bool async) + { + RequireServer.Check(); + EnsureTestData(); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + Limit = limit + }; + + var cursor = ExecuteOperation(subject, async); + var results = ReadCursorToEnd(cursor, async); + + var expectedResults = new[] + { + new BsonDocument { { "_id", 1 }, { "value", limit == 1 ? 1 : 3 } } + }; + results.Should().Equal(expectedResults); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_results_when_MaxTime_is_set( + [Values(null, 1000)] + int? seconds, + [Values(false, true)] + bool async) + { + RequireServer.Check(); + EnsureTestData(); + var maxTime = seconds.HasValue ? TimeSpan.FromSeconds(seconds.Value) : (TimeSpan?)null; +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + MaxTime = maxTime + }; + + var cursor = ExecuteOperation(subject, async); + var results = ReadCursorToEnd(cursor, async); + + // results should be the same whether MaxTime was used or not + results.Should().BeEquivalentTo( + BsonDocument.Parse("{ _id : 1, value : 3 }"), + BsonDocument.Parse("{ _id : 2, value : 4 }")); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_results_when_ReadConcern_is_set( + [Values(null, ReadConcernLevel.Local)] // only use values that are valid on StandAlone servers + ReadConcernLevel? readConcernLevel, + [Values(false, true)] + bool async) + { + RequireServer.Check(); + EnsureTestData(); + var readConcern = new ReadConcern(readConcernLevel); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + ReadConcern = readConcern + }; + + // results should be the same whether ReadConcern was used or not + var cursor = ExecuteOperation(subject, async); + var results = ReadCursorToEnd(cursor, async); + + results.Should().BeEquivalentTo( + BsonDocument.Parse("{ _id : 1, value : 3 }"), + BsonDocument.Parse("{ _id : 2, value : 4 }")); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_results_when_ResultSerializer_is_set( + [Values(false, true)] + bool async) + { + RequireServer.Check(); + EnsureTestData(); + var resultSerializer = new ElementDeserializer("value", new DoubleSerializer()); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, resultSerializer, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + + var cursor = ExecuteOperation(subject, async); + var results = ReadCursorToEnd(cursor, async); + + results.Sort(); + results.Should().Equal(3, 4); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_results_when_Scope_is_set( + [Values(false, true)] + bool async) + { + RequireServer.Check(); + EnsureTestData(); + var finalizeFunction = new BsonJavaScript("function(key, reduced) { return reduced + zeroFromScope; }"); + var scope = new BsonDocument("zeroFromScope", 0); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + FinalizeFunction = finalizeFunction, + Scope = scope + }; + + var cursor = ExecuteOperation(subject, async); + var results = ReadCursorToEnd(cursor, async); + + results.Should().BeEquivalentTo( + BsonDocument.Parse("{ _id : 1, value : 3 }"), + BsonDocument.Parse("{ _id : 2, value : 4 }")); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_results_when_Sort_is_set( + [Values(1, -1)] + int direction, + [Values(false, true)] + bool async) + { + RequireServer.Check(); + EnsureTestData(); + var sort = new BsonDocument("_id", direction); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + Limit = 2, + Sort = sort + }; + + var cursor = ExecuteOperation(subject, async); + var results = ReadCursorToEnd(cursor, async); + + BsonDocument[] expectedResults; + if (direction == 1) + { + expectedResults = new[] + { + BsonDocument.Parse("{ _id : 1, value : 3 }") + }; + } + else + { + expectedResults = new[] + { + BsonDocument.Parse("{ _id : 1, value : 2 }"), + BsonDocument.Parse("{ _id : 2, value : 4 }") + }; + } + results.Should().BeEquivalentTo(expectedResults); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_throw_when_binding_is_null( + [Values(false, true)] + bool async) + { +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + using var session = OperationTestHelper.CreateSession(); + using var operationContext = new OperationContext(session); + + var exception = Record.Exception(() => ExecuteOperation(operationContext, subject, (IReadBinding)null, async)); + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("binding"); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_throw_when_maxTime_is_exceeded( + [Values(false, true)] bool async) + { + RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + subject.MaxTime = TimeSpan.FromSeconds(9001); + using var session = OperationTestHelper.CreateSession(); + using var operationContext = new OperationContext(session); + + using (var failPoint = FailPoint.ConfigureAlwaysOn(FailPointName.MaxTimeAlwaysTimeout)) + { + var exception = Record.Exception(() => ExecuteOperation(operationContext, subject, failPoint.Binding, async)); + + exception.Should().BeOfType(); + } + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_send_session_id_when_supported( + [Values(false, true)] bool async) + { + RequireServer.Check(); + EnsureTestData(); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + + VerifySessionIdWasSentWhenSupported(subject, "mapReduce", async); + } + + [Theory] + [ParameterAttributeData] + public void CreateCommand_should_return_expected_result_when_ReadConcern_is_set( + [Values(null, ReadConcernLevel.Linearizable, ReadConcernLevel.Local)] + ReadConcernLevel? level) + { + var readConcern = level.HasValue ? new ReadConcern(level.Value) : ReadConcern.Default; +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + ReadConcern = readConcern + }; + var connectionDescription = OperationTestHelper.CreateConnectionDescription(); + using var session = OperationTestHelper.CreateSession(); + using var operationContext = new OperationContext(session); + + var result = subject.CreateCommand(operationContext, connectionDescription); + + var expectedResult = new BsonDocument + { + { "mapReduce", _collectionNamespace.CollectionName }, + { "map", _mapFunction }, + { "reduce", _reduceFunction }, + { "out", new BsonDocument("inline", 1) }, + { "readConcern", () => readConcern.ToBsonDocument(), !readConcern.IsServerDefault } + }; + result.Should().Be(expectedResult); + } + + [Theory] + [ParameterAttributeData] + public void CreateCommand_should_return_the_expected_result_when_using_causal_consistency( + [Values(null, ReadConcernLevel.Linearizable, ReadConcernLevel.Local)] + ReadConcernLevel? level) + { + var readConcern = level.HasValue ? new ReadConcern(level.Value) : ReadConcern.Default; +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOperation(_collectionNamespace, _mapFunction, _reduceFunction, _resultSerializer, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + ReadConcern = readConcern + }; + var connectionDescription = OperationTestHelper.CreateConnectionDescription(supportsSessions: true); + using var session = OperationTestHelper.CreateSession(isCausallyConsistent: true, operationTime: new BsonTimestamp(100)); + using var operationContext = new OperationContext(session); + + var result = subject.CreateCommand(operationContext, connectionDescription); + + var expectedReadConcernDocument = readConcern.ToBsonDocument(); + expectedReadConcernDocument["afterClusterTime"] = new BsonTimestamp(100); + + var expectedResult = new BsonDocument + { + { "mapReduce", _collectionNamespace.CollectionName }, + { "map", _mapFunction }, + { "reduce", _reduceFunction }, + { "out", new BsonDocument("inline", 1) }, + { "readConcern", expectedReadConcernDocument } + }; + result.Should().Be(expectedResult); + } + + // helper methods + private void EnsureTestData() + { + DropCollection(); + Insert( + new BsonDocument { { "_id", 1 }, { "x", 1 }, { "v", 1 }, { "y", "a" } }, + new BsonDocument { { "_id", 2 }, { "x", 1 }, { "v", 2 }, { "y", "A" } }, + new BsonDocument { { "_id", 3 }, { "x", 2 }, { "v", 4 }, { "y", "a" } }); + } + + // nested types + private class Reflector + { + // fields +#pragma warning disable CS0618 // Type or member is obsolete + private readonly MapReduceOperation _instance; + + // constructor + public Reflector(MapReduceOperation instance) + { + _instance = instance; + } + + // methods + public BsonDocument CreateOutputOptions() + { + var method = typeof(MapReduceOperation).GetMethod("CreateOutputOptions", BindingFlags.NonPublic | BindingFlags.Instance); + return (BsonDocument)method.Invoke(_instance, new object[0]); + } +#pragma warning restore CS0618 // Type or member is obsolete + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOutputToCollectionOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOutputToCollectionOperationTests.cs new file mode 100644 index 00000000000..0ee6df9c2c1 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Core/Operations/MapReduceOutputToCollectionOperationTests.cs @@ -0,0 +1,669 @@ +/* Copyright 2010-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Reflection; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Driver.Core.Clusters; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; +using Xunit; + +namespace MongoDB.Driver.Core.Operations +{ + public class MapReduceOutputToCollectionOperationTests : OperationTestBase + { + // fields + private readonly BsonJavaScript _mapFunction; + private CollectionNamespace _outputCollectionNamespace; + private readonly BsonJavaScript _reduceFunction; + + // constructors + public MapReduceOutputToCollectionOperationTests() + { + _mapFunction = "function() { emit(this.x, this.v); }"; + _outputCollectionNamespace = new CollectionNamespace(_databaseNamespace, _collectionNamespace + "Output"); + _reduceFunction = "function(key, values) { var sum = 0; for (var i = 0; i < values.length; i++) { sum += values[i]; }; return sum; }"; + } + + // test methods + [Fact] + public void constructor_should_initialize_instance() + { +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + + subject.CollectionNamespace.Should().BeSameAs(_collectionNamespace); + subject.OutputCollectionNamespace.Should().BeSameAs(_outputCollectionNamespace); + subject.MapFunction.Should().BeSameAs(_mapFunction); + subject.ReduceFunction.Should().BeSameAs(_reduceFunction); + subject.MessageEncoderSettings.Should().BeSameAs(_messageEncoderSettings); + + subject.BypassDocumentValidation.Should().NotHaveValue(); + subject.Collation.Should().BeNull(); + subject.Filter.Should().BeNull(); + subject.FinalizeFunction.Should().BeNull(); +#pragma warning disable 618 + subject.JavaScriptMode.Should().NotHaveValue(); +#pragma warning restore 618 + subject.Limit.Should().NotHaveValue(); + subject.MaxTime.Should().NotHaveValue(); +#pragma warning disable 618 + subject.NonAtomicOutput.Should().NotHaveValue(); + subject.OutputMode.Should().Be(MapReduceOutputMode.Replace); +#pragma warning restore 618 + subject.Scope.Should().BeNull(); + subject.Sort.Should().BeNull(); + subject.Verbose.Should().NotHaveValue(); + } + + [Fact] + public void constructor_should_throw_when_outputCollectionNamespace_is_null() + { +#pragma warning disable CS0618 // Type or member is obsolete + var exception = Record.Exception(() => new MapReduceOutputToCollectionOperation(_collectionNamespace, null, _mapFunction, _reduceFunction, _messageEncoderSettings)); +#pragma warning restore CS0618 // Type or member is obsolete + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("outputCollectionNamespace"); + } + + [Theory] + [ParameterAttributeData] + public void BypassDocumentValidation_get_and_set_should_work( + [Values(null, false, true)] + bool? value) + { +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + + subject.BypassDocumentValidation = value; + var result = subject.BypassDocumentValidation; + + result.Should().Be(value); + } + + [Theory] + [ParameterAttributeData] + public void Filter_should_get_and_set_value( + [Values(null, "{ x : 1 }", "{ x : 2 }")] + string valueString) + { +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + var value = valueString == null ? null : BsonDocument.Parse(valueString); + + subject.Filter = value; + var result = subject.Filter; + + result.Should().Be(value); + } + + [Theory] + [ParameterAttributeData] + public void NonAtomicOutput_get_and_set_should_work( + [Values(null, false, true)] + bool? value) + { +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + +#pragma warning disable 618 + subject.NonAtomicOutput = value; + var result = subject.NonAtomicOutput; +#pragma warning restore 618 + + result.Should().Be(value); + } + + [Theory] + [ParameterAttributeData] + public void OutputCollectionNamespace_get_and_set_should_work( + [Values("a", "b")] + string collectionName) + { + var outputCollectionNamespace = new CollectionNamespace(_databaseNamespace, collectionName); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + + var result = subject.OutputCollectionNamespace; + + result.Should().BeSameAs(outputCollectionNamespace); + } + + [Theory] + [ParameterAttributeData] + public void OutputMode_get_and_set_should_work( +#pragma warning disable CS0618 // Type or member is obsolete + [Values((int)MapReduceOutputMode.Merge, (int)MapReduceOutputMode.Reduce)] + int valueInt) + { + var value = (MapReduceOutputMode)valueInt; + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); + + subject.OutputMode = value; + var result = subject.OutputMode; +#pragma warning restore CS0618 // Type or member is obsolete + + result.Should().Be(value); + } + + [Theory] + [ParameterAttributeData] + public void ShardedOutput_get_and_set_should_work( + [Values(null, false, true)] + bool? value) + { +#pragma warning disable 618 + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); + + subject.ShardedOutput = value; + var result = subject.ShardedOutput; +#pragma warning restore 618 + + result.Should().Be(value); + } + + [Theory] + [ParameterAttributeData] + public void WriteConcern_get_and_set_should_work( + [Values(null, 1, 2)] + int? w) + { +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + var value = w.HasValue ? new WriteConcern(w.Value) : null; + + subject.WriteConcern = value; + var result = subject.WriteConcern; + + result.Should().BeSameAs(value); + } + + [Theory] + [ParameterAttributeData] + public void CreateCommand_should_return_expected_result_when_BypassDocumentValidation_is_set( + [Values(null, false, true)] + bool? bypassDocumentValidation) + { +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + BypassDocumentValidation = bypassDocumentValidation + }; + var connectionDescription = OperationTestHelper.CreateConnectionDescription(); + using var session = OperationTestHelper.CreateSession(); + using var operationContext = new OperationContext(session); + + var result = subject.CreateCommand(operationContext, connectionDescription); + + var expectedResult = new BsonDocument + { + { "mapReduce", _collectionNamespace.CollectionName }, + { "map", _mapFunction }, + { "reduce", _reduceFunction }, + { "out", new BsonDocument { {"replace", _outputCollectionNamespace.CollectionName }, { "db", _databaseNamespace.DatabaseName } } }, + { "bypassDocumentValidation", () => bypassDocumentValidation.Value, bypassDocumentValidation.HasValue } + }; + result.Should().Be(expectedResult); + } + + [Theory] + [ParameterAttributeData] + public void CreateCommand_should_return_expected_result_when_WriteConcern_is_set( + [Values(null, 1, 2)] + int? w) + { + var writeConcern = w.HasValue ? new WriteConcern(w.Value) : null; +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + WriteConcern = writeConcern + }; + var connectionDescription = OperationTestHelper.CreateConnectionDescription(); + using var session = OperationTestHelper.CreateSession(); + using var operationContext = new OperationContext(session); + + var result = subject.CreateCommand(operationContext, connectionDescription); + + var expectedResult = new BsonDocument + { + { "mapReduce", _collectionNamespace.CollectionName }, + { "map", _mapFunction }, + { "reduce", _reduceFunction }, + { "out", new BsonDocument { {"replace", _outputCollectionNamespace.CollectionName }, { "db", _databaseNamespace.DatabaseName } } }, + { "writeConcern", () => writeConcern.ToBsonDocument(), writeConcern != null } + }; + result.Should().Be(expectedResult); + } + + [Fact] + public void CreateOutputOptions_should_return_expected_result() + { +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + var subjectReflector = new Reflector(subject); + + var result = subjectReflector.CreateOutputOptions(); + + var expectedResult = new BsonDocument + { + { "replace", _outputCollectionNamespace.CollectionName }, + { "db", _outputCollectionNamespace.DatabaseNamespace.DatabaseName } + }; + result.Should().Be(expectedResult); + } + + [Theory] + [ParameterAttributeData] + public void CreateOutputOptions_should_return_expected_result_when_ShardedOutput_is_set( + [Values(null, false, true)] + bool? shardedOutput) + { +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { +#pragma warning disable 618 + ShardedOutput = shardedOutput +#pragma warning restore 618 + }; + var subjectReflector = new Reflector(subject); + + var result = subjectReflector.CreateOutputOptions(); + + var expectedResult = new BsonDocument + { + { "replace", _outputCollectionNamespace.CollectionName }, + { "db", _outputCollectionNamespace.DatabaseNamespace.DatabaseName }, + { "sharded", () => shardedOutput.Value, shardedOutput.HasValue } + }; + result.Should().Be(expectedResult); + } + + [Theory] + [ParameterAttributeData] + public void CreateOutputOptions_should_return_expected_result_when_NonAtomicOutput_is_provided( + [Values(null, false, true)] + bool? nonAtomicOutput) + { +#pragma warning disable 618 + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) + { + NonAtomicOutput = nonAtomicOutput +#pragma warning restore 618 + }; + var subjectReflector = new Reflector(subject); + var expectedResult = new BsonDocument + { + { "replace", _outputCollectionNamespace.CollectionName }, + { "db", _outputCollectionNamespace.DatabaseNamespace.DatabaseName }, + { "nonAtomic", () => nonAtomicOutput.Value, nonAtomicOutput.HasValue } + }; + + var result = subjectReflector.CreateOutputOptions(); + + result.Should().Be(expectedResult); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_result( + [Values(false, true)] + bool async) + { + RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); + EnsureTestData(); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + + ExecuteOperation(subject, async); + + ReadAllFromCollection(_outputCollectionNamespace).Should().BeEquivalentTo( + BsonDocument.Parse("{ _id : 1, value : 3 }"), + BsonDocument.Parse("{ _id : 2, value : 4 }")); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_results_when_Collation_is_set( + [Values(false, true)] + bool caseSensitive, + [Values(false, true)] + bool async) + { + RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); + if (CoreTestConfiguration.ServerVersion >= SemanticVersion.Parse("v8.3.0-alpha3-105-g1227af8")) + { + DropCollection(_outputCollectionNamespace); + } + + EnsureTestData(); + var collation = new Collation("en_US", caseLevel: caseSensitive, strength: CollationStrength.Primary); + var filter = BsonDocument.Parse("{ y : 'a' }"); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + Collation = collation, + Filter = filter + }; + + ExecuteOperation(subject, async); + + BsonDocument[] expectedResults; + if (caseSensitive) + { + expectedResults = new[] + { + BsonDocument.Parse("{ _id : 1, value : 1 }"), + BsonDocument.Parse("{ _id : 2, value : 4 }") + }; + } + else + { + expectedResults = new[] + { + BsonDocument.Parse("{ _id : 1, value : 3 }"), + BsonDocument.Parse("{ _id : 2, value : 4 }") + }; + } + ReadAllFromCollection(_outputCollectionNamespace).Should().BeEquivalentTo(expectedResults); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_results_when_Filter_is_set( + [Values(false, true)] + bool async) + { + RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); + EnsureTestData(); + var filter = BsonDocument.Parse("{ y : 'a' }"); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + Filter = filter + }; + + ExecuteOperation(subject, async); + + ReadAllFromCollection(_outputCollectionNamespace).Should().BeEquivalentTo( + BsonDocument.Parse("{ _id : 1, value : 1 }"), + BsonDocument.Parse("{ _id : 2, value : 4 }")); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_results_when_FinalizeFunction_is_set( + [Values(false, true)] + bool async) + { + RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); + EnsureTestData(); + var finalizeFunction = new BsonJavaScript("function(key, reduced) { return -reduced; }"); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + FinalizeFunction = finalizeFunction + }; + + ExecuteOperation(subject, async); + + ReadAllFromCollection(_outputCollectionNamespace).Should().BeEquivalentTo( + BsonDocument.Parse("{ _id : 1, value : -3 }"), + BsonDocument.Parse("{ _id : 2, value : -4 }")); + } + + // TODO: figure out why test fails when JavaScriptMode = true (server bug?) + + //[Theory] + //[ParameterAttributeData] + //public void Execute_should_return_expected_results_when_JavaScriptMode_is_set( + // [Values(null, false, true)] + // bool? javaScriptMode, + // [Values(false, true)] + // bool async) + //{ + // RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); + // EnsureTestData(); + // var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) + // { + // JavaScriptMode = javaScriptMode + // }; + + // ExecuteOperation(subject, async); + + // // the results are the same either way, but at least we're smoke testing JavaScriptMode + // ReadAllFromCollection(_outputCollectionNamespace).Should().Equal( + // BsonDocument.Parse("{ _id : 1, value : 3 }"), + // BsonDocument.Parse("{ _id : 2, value : 4 }")); + //} + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_results_when_Limit_is_set( + [Values(1, 2)] + long limit, + [Values(false, true)] + bool async) + { + RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); + EnsureTestData(); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + Limit = limit + }; + + ExecuteOperation(subject, async); + + var expectedResults = new[] + { + new BsonDocument { { "_id", 1 }, { "value", limit == 1 ? 1 : 3 } } + }; + ReadAllFromCollection(_outputCollectionNamespace).Should().Equal(expectedResults); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_results_when_MaxTime_is_set( + [Values(null, 1000)] + int? seconds, + [Values(false, true)] + bool async) + { + RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); + EnsureTestData(); + var maxTime = seconds.HasValue ? TimeSpan.FromSeconds(seconds.Value) : (TimeSpan?)null; +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + MaxTime = maxTime + }; + + ExecuteOperation(subject, async); + + // results should be the same whether MaxTime was used or not + ReadAllFromCollection(_outputCollectionNamespace).Should().BeEquivalentTo( + BsonDocument.Parse("{ _id : 1, value : 3 }"), + BsonDocument.Parse("{ _id : 2, value : 4 }")); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_results_when_Scope_is_set( + [Values(false, true)] + bool async) + { + RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); + EnsureTestData(); + var finalizeFunction = new BsonJavaScript("function(key, reduced) { return reduced + zeroFromScope; }"); + var scope = new BsonDocument("zeroFromScope", 0); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + FinalizeFunction = finalizeFunction, + Scope = scope + }; + + ExecuteOperation(subject, async); + + ReadAllFromCollection(_outputCollectionNamespace).Should().BeEquivalentTo( + BsonDocument.Parse("{ _id : 1, value : 3 }"), + BsonDocument.Parse("{ _id : 2, value : 4 }")); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_return_expected_results_when_Sort_is_set( + [Values(1, -1)] + int direction, + [Values(false, true)] + bool async) + { + RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); + EnsureTestData(); + var sort = new BsonDocument("_id", direction); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + Limit = 2, + Sort = sort + }; + + ExecuteOperation(subject, async); + + BsonDocument[] expectedResults; + if (direction == 1) + { + expectedResults = new[] + { + BsonDocument.Parse("{ _id : 1, value : 3 }") + }; + } + else + { + expectedResults = new[] + { + BsonDocument.Parse("{ _id : 1, value : 2 }"), + BsonDocument.Parse("{ _id : 2, value : 4 }") + }; + } + ReadAllFromCollection(_outputCollectionNamespace).Should().BeEquivalentTo(expectedResults); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_throw_when_binding_is_null( + [Values(false, true)] + bool async) + { +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + using var session = OperationTestHelper.CreateSession(); + using var operationContext = new OperationContext(session); + + var exception = Record.Exception(() => ExecuteOperation(operationContext, subject, null, async)); + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("binding"); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_throw_when_a_write_concern_error_occurs( + [Values(false, true)] + bool async) + { + RequireServer.Check().ClusterType(ClusterType.ReplicaSet); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings) +#pragma warning restore CS0618 // Type or member is obsolete + { + WriteConcern = new WriteConcern(9) + }; + + var exception = Record.Exception(() => ExecuteOperation(subject, async)); + + exception.Should().BeOfType(); + } + + [Theory] + [ParameterAttributeData] + public void Execute_should_send_session_id_when_supported( + [Values(false, true)] bool async) + { + RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); + EnsureTestData(); +#pragma warning disable CS0618 // Type or member is obsolete + var subject = new MapReduceOutputToCollectionOperation(_collectionNamespace, _outputCollectionNamespace, _mapFunction, _reduceFunction, _messageEncoderSettings); +#pragma warning restore CS0618 // Type or member is obsolete + + VerifySessionIdWasSentWhenSupported(subject, "mapReduce", async); + } + + // helper methods + private void EnsureTestData() + { + DropCollection(); + Insert( + new BsonDocument { { "_id", 1 }, { "x", 1 }, { "v", 1 }, { "y", "a" } }, + new BsonDocument { { "_id", 2 }, { "x", 1 }, { "v", 2 }, { "y", "A" } }, + new BsonDocument { { "_id", 3 }, { "x", 2 }, { "v", 4 }, { "y", "a" } }); + } + + // nested types + private class Reflector + { + // fields +#pragma warning disable CS0618 // Type or member is obsolete + private readonly MapReduceOutputToCollectionOperation _instance; + + // constructor + public Reflector(MapReduceOutputToCollectionOperation instance) + { + _instance = instance; + } + + // methods + public BsonDocument CreateOutputOptions() + { + var method = typeof(MapReduceOutputToCollectionOperation).GetMethod("CreateOutputOptions", BindingFlags.NonPublic | BindingFlags.Instance); + return (BsonDocument)method.Invoke(_instance, new object[0]); + } +#pragma warning restore CS0618 // Type or member is obsolete + } + } +} diff --git a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenMapReduceTest.cs b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenMapReduceTest.cs new file mode 100644 index 00000000000..9a5c63f965d --- /dev/null +++ b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenMapReduceTest.cs @@ -0,0 +1,123 @@ +/* Copyright 2019-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.TestHelpers.JsonDrivenTests; +using MongoDB.Driver.Core.Operations; + +namespace MongoDB.Driver.Tests.JsonDrivenTests +{ + public sealed class JsonDrivenMapReduceTest : JsonDrivenCollectionTest + { + // private fields + private BsonJavaScript _map; +#pragma warning disable CS0618 // Type or member is obsolete + private MapReduceOptions _options = new MapReduceOptions(); +#pragma warning restore CS0618 // Type or member is obsolete + private BsonJavaScript _reduce; + private List _result; + private IClientSessionHandle _session; + + // public constructors + public JsonDrivenMapReduceTest(IMongoCollection collection, Dictionary objectMap) + : base(collection, objectMap) + { + } + + // public methods + public override void Arrange(BsonDocument document) + { + JsonDrivenHelper.EnsureAllFieldsAreValid(document, "name", "object", "collectionOptions", "arguments", "result", "error"); + base.Arrange(document); + } + + // protected methods + protected override void AssertResult() + { + _result.Should().Equal(_expectedResult.AsBsonArray.Cast()); + } + + protected override void CallMethod(CancellationToken cancellationToken) + { + IAsyncCursor cursor; + if (_session == null) + { +#pragma warning disable CS0618 // Type or member is obsolete + cursor = _collection.MapReduce(_map, _reduce, _options, cancellationToken); +#pragma warning restore CS0618 // Type or member is obsolete + } + else + { +#pragma warning disable CS0618 // Type or member is obsolete + cursor = _collection.MapReduce(_session, _map, _reduce, _options, cancellationToken); +#pragma warning restore CS0618 // Type or member is obsolete + } + + _result = cursor.ToList(); + } + + protected override async Task CallMethodAsync(CancellationToken cancellationToken) + { + IAsyncCursor cursor; + if (_session == null) + { +#pragma warning disable CS0618 // Type or member is obsolete + cursor = await _collection.MapReduceAsync(_map, _reduce, _options, cancellationToken).ConfigureAwait(false); +#pragma warning restore CS0618 // Type or member is obsolete + } + else + { +#pragma warning disable CS0618 // Type or member is obsolete + cursor = await _collection.MapReduceAsync(_session, _map, _reduce, _options, cancellationToken).ConfigureAwait(false); +#pragma warning restore CS0618 // Type or member is obsolete + } + + _result = await cursor.ToListAsync(cancellationToken).ConfigureAwait(false); + } + + protected override void SetArgument(string name, BsonValue value) + { + switch (name) + { + case "map": + _map = BsonJavaScript.Create(value); + return; + + case "reduce": + _reduce = BsonJavaScript.Create(value); + return; + + case "out": + _options.OutputOptions = value is BsonString +#pragma warning disable CS0618 // Type or member is obsolete + ? new MapReduceOutputOptions.CollectionOutput(value.AsString, MapReduceOutputMode.Replace) + : MapReduceOutputOptions.Inline; // TODO: Clean this up. +#pragma warning restore CS0618 // Type or member is obsolete + return; + + case "session": + _session = (IClientSessionHandle)_objectMap[value.AsString]; + return; + } + + base.SetArgument(name, value); + } + } +} diff --git a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs index 0c54e9db790..51ac873e5b4 100644 --- a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs +++ b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs @@ -171,6 +171,7 @@ public JsonDrivenTest CreateTest(string receiver, string name) case "insertOne": return new JsonDrivenInsertOneTest(collection, _objectMap); case "listIndexes": return new JsonDrivenListIndexesTest(collection, _objectMap); case "listIndexNames": throw new SkipException(".NET/C# driver does not implement a ListIndexNames helper."); + case "mapReduce": return new JsonDrivenMapReduceTest(collection, _objectMap); case "replaceOne": return new JsonDrivenReplaceOneTest(collection, _objectMap); case "updateMany": return new JsonDrivenUpdateManyTest(collection, _objectMap); case "updateOne": return new JsonDrivenUpdateOneTest(collection, _objectMap); diff --git a/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs b/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs index ad0178fc1dc..2c44ff65f76 100644 --- a/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs @@ -2813,6 +2813,200 @@ public void InsertMany_should_respect_AssignIdOnInsert( document.Contains("_id").Should().Be(assignIdOnInsert); } + [Theory] + [ParameterAttributeData] + public void MapReduce_with_inline_output_mode_should_execute_a_MapReduceOperation( + [Values(false, true)] bool usingSession, + [Values(false, true)] bool async) + { + var subject = CreateSubject(); + var session = CreateSession(usingSession); + var map = new BsonJavaScript("map"); + var reduce = new BsonJavaScript("reduce"); + var filterDocument = new BsonDocument("filter", 1); + var filterDefinition = (FilterDefinition)filterDocument; + var sortDocument = new BsonDocument("sort", 1); + var sortDefinition = (SortDefinition)sortDocument; +#pragma warning disable CS0618 // Type or member is obsolete + var options = new MapReduceOptions +#pragma warning restore CS0618 // Type or member is obsolete + { + Collation = new Collation("en_US"), + Filter = filterDefinition, + Finalize = new BsonJavaScript("finalizer"), +#pragma warning disable 618 + JavaScriptMode = true, +#pragma warning restore 618 + Limit = 10, + MaxTime = TimeSpan.FromMinutes(2), +#pragma warning disable CS0618 // Type or member is obsolete + OutputOptions = MapReduceOutputOptions.Inline, +#pragma warning restore CS0618 // Type or member is obsolete + Scope = new BsonDocument("test", 3), + Sort = sortDefinition, + Verbose = true + }; + using var cancellationTokenSource = new CancellationTokenSource(); + var cancellationToken = cancellationTokenSource.Token; + + if (usingSession) + { + if (async) + { +#pragma warning disable CS0618 // Type or member is obsolete + subject.MapReduceAsync(session, map, reduce, options, cancellationToken).GetAwaiter().GetResult(); +#pragma warning restore CS0618 // Type or member is obsolete + } + else + { +#pragma warning disable CS0618 // Type or member is obsolete + subject.MapReduce(session, map, reduce, options, cancellationToken); +#pragma warning restore CS0618 // Type or member is obsolete + } + } + else + { + if (async) + { +#pragma warning disable CS0618 // Type or member is obsolete + subject.MapReduceAsync(map, reduce, options, cancellationToken).GetAwaiter().GetResult(); +#pragma warning restore CS0618 // Type or member is obsolete + } + else + { +#pragma warning disable CS0618 // Type or member is obsolete + subject.MapReduce(map, reduce, options, cancellationToken); +#pragma warning restore CS0618 // Type or member is obsolete + } + } + + var call = _operationExecutor.GetReadCall>(); + VerifySessionAndCancellationToken(call, session, cancellationToken); + +#pragma warning disable CS0618 // Type or member is obsolete + var operation = call.Operation.Should().BeOfType>().Subject; +#pragma warning restore CS0618 // Type or member is obsolete + operation.Collation.Should().BeSameAs(options.Collation); + operation.CollectionNamespace.Should().Be(subject.CollectionNamespace); + operation.Filter.Should().Be(filterDocument); + operation.FinalizeFunction.Should().Be(options.Finalize); +#pragma warning disable 618 + operation.JavaScriptMode.Should().Be(options.JavaScriptMode); +#pragma warning restore 618 + operation.Limit.Should().Be(options.Limit); + operation.MapFunction.Should().Be(map); + operation.MaxTime.Should().Be(options.MaxTime); + operation.ReadConcern.Should().Be(subject.Settings.ReadConcern); + operation.ReduceFunction.Should().Be(reduce); + operation.ResultSerializer.Should().Be(BsonDocumentSerializer.Instance); + operation.Scope.Should().Be(options.Scope); + operation.Sort.Should().Be(sortDocument); + operation.Verbose.Should().Be(options.Verbose); + } + + [Theory] + [ParameterAttributeData] + public void MapReduce_with_collection_output_mode_should_execute_a_MapReduceOutputToCollectionOperation( + [Values(false, true)] bool usingSession, + [Values(false, true)] bool async) + { + var writeConcern = new WriteConcern(1); + var subject = CreateSubject().WithWriteConcern(writeConcern); + var session = CreateSession(usingSession); + var map = new BsonJavaScript("map"); + var reduce = new BsonJavaScript("reduce"); + var filterDocument = new BsonDocument("filter", 1); + var filterDefinition = (FilterDefinition)filterDocument; + var sortDocument = new BsonDocument("sort", 1); + var sortDefinition = (SortDefinition)sortDocument; +#pragma warning disable CS0618 // Type or member is obsolete + var options = new MapReduceOptions +#pragma warning restore CS0618 // Type or member is obsolete + { + BypassDocumentValidation = true, + Collation = new Collation("en_US"), + Filter = filterDefinition, + Finalize = new BsonJavaScript("finalizer"), +#pragma warning disable 618 + JavaScriptMode = true, +#pragma warning restore 618 + Limit = 10, + MaxTime = TimeSpan.FromMinutes(2), +#pragma warning disable 618 + OutputOptions = MapReduceOutputOptions.Replace("awesome", "otherDB", true), +#pragma warning restore 618 + Scope = new BsonDocument("test", 3), + Sort = sortDefinition, + Verbose = true + }; + using var cancellationTokenSource = new CancellationTokenSource(); + var cancellationToken = cancellationTokenSource.Token; + + if (usingSession) + { + if (async) + { +#pragma warning disable CS0618 // Type or member is obsolete + subject.MapReduceAsync(session, map, reduce, options, cancellationToken).GetAwaiter().GetResult(); +#pragma warning restore CS0618 // Type or member is obsolete + } + else + { +#pragma warning disable CS0618 // Type or member is obsolete + subject.MapReduce(session, map, reduce, options, cancellationToken); +#pragma warning restore CS0618 // Type or member is obsolete + } + } + else + { + if (async) + { +#pragma warning disable CS0618 // Type or member is obsolete + subject.MapReduceAsync(map, reduce, options, cancellationToken).GetAwaiter().GetResult(); +#pragma warning restore CS0618 // Type or member is obsolete + } + else + { +#pragma warning disable CS0618 // Type or member is obsolete + subject.MapReduce(map, reduce, options, cancellationToken); +#pragma warning restore CS0618 // Type or member is obsolete + } + } + + var call = _operationExecutor.GetWriteCall(); + VerifySessionAndCancellationToken(call, session, cancellationToken); + +#pragma warning disable CS0618 // Type or member is obsolete + var operation = call.Operation.Should().BeOfType().Subject; +#pragma warning restore CS0618 // Type or member is obsolete + operation.BypassDocumentValidation.Should().Be(options.BypassDocumentValidation); + operation.Collation.Should().BeSameAs(options.Collation); + operation.CollectionNamespace.Should().Be(subject.CollectionNamespace); + operation.Filter.Should().Be(filterDocument); + operation.FinalizeFunction.Should().Be(options.Finalize); +#pragma warning disable 618 + operation.JavaScriptMode.Should().Be(options.JavaScriptMode); +#pragma warning restore 618 + operation.Limit.Should().Be(options.Limit); + operation.MapFunction.Should().Be(map); + operation.MaxTime.Should().Be(options.MaxTime); +#pragma warning disable 618 + operation.NonAtomicOutput.Should().NotHaveValue(); +#pragma warning restore 618 + operation.OutputCollectionNamespace.Should().Be(CollectionNamespace.FromFullName("otherDB.awesome")); +#pragma warning disable CS0618 // Type or member is obsolete + operation.OutputMode.Should().Be(Core.Operations.MapReduceOutputMode.Replace); +#pragma warning restore CS0618 // Type or member is obsolete + operation.ReduceFunction.Should().Be(reduce); + operation.Scope.Should().Be(options.Scope); +#pragma warning disable 618 + operation.ShardedOutput.Should().Be(true); +#pragma warning restore 618 + operation.Sort.Should().Be(sortDocument); + operation.Verbose.Should().Be(options.Verbose); + operation.WriteConcern.Should().BeSameAs(writeConcern); + } + [Theory] [ParameterAttributeData] public void ReplaceOne_should_execute_a_BulkMixedOperation( diff --git a/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs b/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs index 371b7bad27d..80354d8afdd 100644 --- a/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs +++ b/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs @@ -750,6 +750,80 @@ public void FindOneAndUpdate_with_session_should_not_throw_when_options_is_null( } } + [Theory] + [ParameterAttributeData] + public async Task MapReduce_should_include_the_filter_when_one_was_not_provided( + [Values(false, true)] bool async) + { + var subject = CreateSubject(); + + if (async) + { +#pragma warning disable CS0618 // Type or member is obsolete + await subject.MapReduceAsync("map", "reduce", null, CancellationToken.None); + + _mockDerivedCollection.Verify( + c => c.MapReduceAsync( + "map", + "reduce", + It.Is>(o => RenderFilter(o.Filter).Equals(_ofTypeFilter)), + CancellationToken.None), + Times.Once); + } + else + { + subject.MapReduce("map", "reduce", null, CancellationToken.None); + + _mockDerivedCollection.Verify( + c => c.MapReduce( + "map", + "reduce", + It.Is>(o => RenderFilter(o.Filter).Equals(_ofTypeFilter)), + CancellationToken.None), + Times.Once); +#pragma warning restore CS0618 // Type or member is obsolete + } + } + + [Theory] + [ParameterAttributeData] + public async Task MapReduce_should_include_the_filter( + [Values(false, true)] bool async) + { + var subject = CreateSubject(); +#pragma warning disable CS0618 // Type or member is obsolete + var options = new MapReduceOptions + { + Filter = _providedFilter + }; + + if (async) + { + await subject.MapReduceAsync("map", "reduce", options, CancellationToken.None); + + _mockDerivedCollection.Verify( + c => c.MapReduceAsync( + "map", + "reduce", + It.Is>(o => RenderFilter(o.Filter).Equals(_expectedFilter)), + CancellationToken.None), + Times.Once); + } + else + { + subject.MapReduce("map", "reduce", options, CancellationToken.None); + + _mockDerivedCollection.Verify( + c => c.MapReduce( + "map", + "reduce", + It.Is>(o => RenderFilter(o.Filter).Equals(_expectedFilter)), + CancellationToken.None), + Times.Once); + } +#pragma warning restore CS0618 // Type or member is obsolete + } + [Fact] public void OfType_should_resort_to_root_collections_OfType() { diff --git a/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs index de715d6a080..bbc3a9b1dd5 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs @@ -397,10 +397,6 @@ private static void SkipNotSupportedTestCases(JsonDrivenTestCase testCase, strin "Deprecated count with rawData option", "Deprecated count with rawData option on less than 8.2.0 - ignore argument", - // readWriteConcern - // .NET/C# driver does not implement a MapReduce helper - "MapReduce omits default write concern", - // retryableReads "collection.findOne succeeds after retryable handshake network error", "collection.findOne succeeds after retryable handshake server error (ShutdownInProgress)", @@ -440,13 +436,11 @@ private static void SkipNotSupportedTestCases(JsonDrivenTestCase testCase, strin "listIndexNames.json", "listIndexNames-serverErrors.json", - // .NET/C# driver does not implement Count or MapReduce helpers. + // .NET/C# driver does not implement a Count helper. // Qualified by resource namespace because other specs have files of the same name that must keep running. "retryable_reads.tests.unified.count.json", "retryable_reads.tests.unified.count-serverErrors.json", - "retryable_reads.tests.unified.mapReduce.json", - "transactions.tests.unified.count.json", - "open_telemetry.operation.map_reduce.json" + "transactions.tests.unified.count.json" ]); #region CMAP helpers diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs index 886186d18b6..032e4346f4d 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs @@ -60,9 +60,9 @@ public void Run(JsonDrivenTestCase testCase) RequireEnvironment.Check().EnvironmentVariable("KMS_MOCK_SERVERS_ENABLED"); } - if (testCase.Name.Contains("legacy.count.json") || testCase.Name.Contains("legacy.unsupportedCommand.json")) + if (testCase.Name.Contains("legacy.count.json")) { - throw new SkipException(".NET/C# driver does not implement Count or MapReduce helpers."); + throw new SkipException(".NET/C# driver does not implement a Count helper."); } RequirePlatform diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedMapReduceOperation.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedMapReduceOperation.cs new file mode 100644 index 00000000000..d231630b623 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedMapReduceOperation.cs @@ -0,0 +1,120 @@ +/* Copyright 2010-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Threading; +using System.Threading.Tasks; +using MongoDB.Bson; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Tests.UnifiedTestOperations +{ + public class UnifiedMapReduceOperation : IUnifiedEntityTestOperation + { + private readonly IMongoCollection _collection; + private readonly BsonJavaScript _map; + private readonly BsonJavaScript _reduce; + + public UnifiedMapReduceOperation( + IMongoCollection collection, + BsonJavaScript map, + BsonJavaScript reduce) + { + _collection = collection; + _map = Ensure.IsNotNull(map, nameof(map)); + _reduce = Ensure.IsNotNull(reduce, nameof(reduce)); + } + + /// + /// Executes the specified cancellation token. + /// + /// The cancellation token. + /// + public OperationResult Execute(CancellationToken cancellationToken) + { + try + { +#pragma warning disable CS0618 // Type or member is obsolete + var cursor = _collection.MapReduce(_map, _reduce); +#pragma warning restore CS0618 // Type or member is obsolete + + var result = cursor.ToList(cancellationToken); + return OperationResult.FromResult(new BsonArray(result)); + } + catch (Exception exception) + { + return OperationResult.FromException(exception); + } + } + + public async Task ExecuteAsync(CancellationToken cancellationToken) + { + try + { +#pragma warning disable CS0618 // Type or member is obsolete + var cursor = await _collection.MapReduceAsync(_map, _reduce); +#pragma warning restore CS0618 // Type or member is obsolete + + var result = await cursor.ToListAsync(cancellationToken); + return OperationResult.FromResult(new BsonArray(result)); + } + catch (Exception exception) + { + return OperationResult.FromException(exception); + } + } + } + + public class UnifiedMapReduceOperationBuilder + { + private readonly UnifiedEntityMap _entityMap; + + public UnifiedMapReduceOperationBuilder(UnifiedEntityMap entityMap) + { + _entityMap = entityMap; + } + + public UnifiedMapReduceOperation Build(string targetCollectionId, BsonDocument arguments) + { + var collection = _entityMap.Collections[targetCollectionId]; + + BsonJavaScript map = null, reduce = null; + + foreach (var argument in arguments) + { + switch (argument.Name) + { + case "map": + map = argument.Value.AsBsonJavaScript; + break; + case "reduce": + reduce = argument.Value.AsBsonJavaScript; + break; + case "out": + var outDocument = argument.Value.AsBsonDocument; + if (!outDocument.Equals(new("inline", 1))) + { + throw new FormatException($"Invalid out setting '{argument.Value}'."); + } + break; + default: + throw new FormatException($"Invalid CountOperation argument name: '{argument.Name}'."); + } + } + + return new UnifiedMapReduceOperation(collection, map, reduce); + } + } +} diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs index 21778838336..12d6b4e8955 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs @@ -111,6 +111,7 @@ public IUnifiedTestOperation CreateOperation(string operationName, string target "insertOne" => new UnifiedInsertOneOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "listIndexes" => new UnifiedListIndexesOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "listSearchIndexes" => new UnifiedListSearchIndexesOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), + "mapReduce" => new UnifiedMapReduceOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "rename" => new UnifiedRenameCollectionOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "replaceOne" => new UnifiedReplaceOneOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "updateMany" => new UnifiedUpdateManyOperationBuilder(_entityMap).Build(targetEntityId, operationArguments),