diff --git a/src/MongoDB.Driver/SortDefinition.cs b/src/MongoDB.Driver/SortDefinition.cs index b66bda799a9..a7d6a8bfaed 100644 --- a/src/MongoDB.Driver/SortDefinition.cs +++ b/src/MongoDB.Driver/SortDefinition.cs @@ -47,6 +47,9 @@ public abstract class SortDefinition /// A . public abstract BsonDocument Render(RenderArgs args); + // TODO: remove this and refactor Render to return a BsonValue in 4.0 + internal virtual BsonValue RenderAsBsonValue(RenderArgs args) => Render(args); + /// /// Performs an implicit conversion from to . /// diff --git a/src/MongoDB.Driver/SortDefinitionBuilder.cs b/src/MongoDB.Driver/SortDefinitionBuilder.cs index bec17d70ef3..9f01bd3a25c 100644 --- a/src/MongoDB.Driver/SortDefinitionBuilder.cs +++ b/src/MongoDB.Driver/SortDefinitionBuilder.cs @@ -130,6 +130,15 @@ public static SortDefinition MetaTextScore(this SortDefini /// The type of the document. public sealed class SortDefinitionBuilder { + /// + /// Creates a value ascending sort. + /// + /// A value ascending sort. + public SortDefinition Ascending() + { + return new ValueDirectionalSortDefinition(SortDirection.Ascending); + } + /// /// Creates an ascending sort. /// @@ -170,6 +179,15 @@ public SortDefinition Combine(IEnumerable> return new CombinedSortDefinition(sorts); } + /// + /// Creates a value descending sort. + /// + /// A value descending sort. + public SortDefinition Descending() + { + return new ValueDirectionalSortDefinition(SortDirection.Descending); + } + /// /// Creates a descending sort. /// @@ -232,6 +250,11 @@ internal sealed class CombinedSortDefinition : SortDefinition> sorts) { _sorts = Ensure.IsNotNull(sorts, nameof(sorts)).ToList(); + + if (_sorts.Any(sort => sort is ValueDirectionalSortDefinition)) + { + throw new InvalidOperationException("Value-based sort cannot be combined with other sorts. When sorting by the entire element value, no other sorting criteria can be applied."); + } } public override BsonDocument Render(RenderArgs args) @@ -272,20 +295,25 @@ public override BsonDocument Render(RenderArgs args) { var renderedField = _field.Render(args); - BsonValue value; - switch (_direction) - { - case SortDirection.Ascending: - value = 1; - break; - case SortDirection.Descending: - value = -1; - break; - default: - throw new InvalidOperationException("Unknown value for " + typeof(SortDirection) + "."); - } + return new BsonDocument(renderedField.FieldName, _direction.ToBsonValue()); + } + } - return new BsonDocument(renderedField.FieldName, value); + internal sealed class ValueDirectionalSortDefinition : SortDefinition + { + private readonly SortDirection _direction; + + public ValueDirectionalSortDefinition(SortDirection direction) + { + _direction = direction; + } + + internal override BsonValue RenderAsBsonValue(RenderArgs args) => _direction.ToBsonValue(); + + public override BsonDocument Render(RenderArgs args) + { + throw new InvalidOperationException( + "Value-based sort cannot be rendered as a document. You might be trying to use a value-based sort where a field-based sort is expected."); } } } diff --git a/src/MongoDB.Driver/SortDirectionExtensions.cs b/src/MongoDB.Driver/SortDirectionExtensions.cs new file mode 100644 index 00000000000..d71676e8239 --- /dev/null +++ b/src/MongoDB.Driver/SortDirectionExtensions.cs @@ -0,0 +1,33 @@ +/* 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; + +namespace MongoDB.Driver +{ + internal static class SortDirectionExtensions + { + internal static BsonValue ToBsonValue(this SortDirection direction) + { + return direction switch + { + SortDirection.Ascending => 1, + SortDirection.Descending => -1, + _ => throw new InvalidOperationException($"Invalid sort direction: {direction}.") + }; + } + } +} \ No newline at end of file diff --git a/src/MongoDB.Driver/UpdateDefinitionBuilder.cs b/src/MongoDB.Driver/UpdateDefinitionBuilder.cs index be95f9a306c..9f2bac9d8ff 100644 --- a/src/MongoDB.Driver/UpdateDefinitionBuilder.cs +++ b/src/MongoDB.Driver/UpdateDefinitionBuilder.cs @@ -1685,7 +1685,7 @@ public override BsonValue Render(RenderArgs args) if (_sort != null) { - document["$push"][renderedField.FieldName]["$sort"] = _sort.Render(args.WithNewDocumentType((IBsonSerializer)itemSerializer)); + document["$push"][renderedField.FieldName]["$sort"] = _sort.RenderAsBsonValue(args.WithNewDocumentType((IBsonSerializer)itemSerializer)); } return document; diff --git a/tests/MongoDB.Driver.Tests/SortDefinitionBuilderTests.cs b/tests/MongoDB.Driver.Tests/SortDefinitionBuilderTests.cs index 29b18bdb264..0dbadc9f7a6 100644 --- a/tests/MongoDB.Driver.Tests/SortDefinitionBuilderTests.cs +++ b/tests/MongoDB.Driver.Tests/SortDefinitionBuilderTests.cs @@ -13,6 +13,7 @@ * limitations under the License. */ +using System; using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization; @@ -31,6 +32,14 @@ public void Ascending() Assert(subject.Ascending("a"), "{a: 1}"); } + [Fact] + public void Ascending_value() + { + var subject = CreateSubject(); + + Assert(subject.Ascending(), "{direction: 1}"); + } + [Fact] public void Ascending_Typed() { @@ -76,6 +85,16 @@ public void Combine_with_repeated_fields_using_extension_methods() Assert(sort, "{b: -1, a: -1}"); } + [Fact] + public void Combine_with_value_based_sort_and_additional_sort_should_throw() + { + var subject = CreateSubject(); + + var exception = Record.Exception(() => subject.Ascending().Descending("b")); + + exception.Should().BeOfType(); + } + [Fact] public void Descending() { @@ -84,6 +103,14 @@ public void Descending() Assert(subject.Descending("a"), "{a: -1}"); } + [Fact] + public void Descending_value() + { + var subject = CreateSubject(); + + Assert(subject.Descending(), "{direction: -1}"); + } + [Fact] public void Descending_Typed() {