diff --git a/src/MongoDB.Driver.Core/Core/Misc/Feature.cs b/src/MongoDB.Driver.Core/Core/Misc/Feature.cs index f8117973dc5..3c27d7ab392 100644 --- a/src/MongoDB.Driver.Core/Core/Misc/Feature.cs +++ b/src/MongoDB.Driver.Core/Core/Misc/Feature.cs @@ -74,6 +74,7 @@ public class Feature private static readonly Feature __findAllowDiskUse = new Feature("FindAllowDiskUse", WireVersion.Server44); private static readonly Feature __findAndModifyWriteConcern = new Feature("FindAndModifyWriteConcern", WireVersion.Server32); private static readonly Feature __findCommand = new Feature("FindCommand", WireVersion.Server32); + private static readonly Feature __findProjectionExpressions = new Feature("FindProjectionExpressions", WireVersion.Server44); private static readonly Feature __geoNearCommand = new Feature("GeoNearCommand", WireVersion.Zero, WireVersion.Server42); private static readonly Feature __getField = new Feature("GetField", WireVersion.Server50); private static readonly Feature __getMoreComment = new Feature("GetMoreComment", WireVersion.Server44); @@ -409,6 +410,11 @@ public class Feature [Obsolete("This property will be removed in a later release.")] public static Feature FindCommand => __findCommand; + /// + /// Gets the find projection expressions feature. + /// + public static Feature FindProjectionExpressions => __findProjectionExpressions; + /// /// Gets the geoNear command feature. /// diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/LinqProviderAdapterV3.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/LinqProviderAdapterV3.cs index 72746d808fd..e8257ccfed2 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/LinqProviderAdapterV3.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/LinqProviderAdapterV3.cs @@ -140,8 +140,7 @@ static Expression RemovePossibleConvertToObject(Expression expression) IBsonSerializer sourceSerializer, IBsonSerializerRegistry serializerRegistry) { - // TODO: implement using LINQ3 instead of falling back to LINQ2 - return LinqProviderAdapter.V2.TranslateExpressionToFindProjection(expression, sourceSerializer, serializerRegistry); + return TranslateExpressionToProjection(expression, sourceSerializer, serializerRegistry, translationOptions: null); } internal override RenderedProjectionDefinition TranslateExpressionToGroupProjection( diff --git a/tests/MongoDB.Driver.Tests/FindFluentTests.cs b/tests/MongoDB.Driver.Tests/FindFluentTests.cs index bae0d79750d..af63aa98b85 100644 --- a/tests/MongoDB.Driver.Tests/FindFluentTests.cs +++ b/tests/MongoDB.Driver.Tests/FindFluentTests.cs @@ -24,6 +24,8 @@ using MongoDB.Driver.Linq; using Moq; using Xunit; +using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Tests { @@ -303,10 +305,17 @@ public class FindFluentTests } } - [Fact] - public void ToString_should_return_the_correct_string() + [Theory] + [ParameterAttributeData] + public void ToString_should_return_the_correct_string( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { - var subject = CreateSubject(); + if (linqProvider == LinqProvider.V3) + { + RequireServer.Check().Supports(Feature.FindProjectionExpressions); + } + + var subject = CreateSubject(linqProvider: linqProvider); subject.Filter = new BsonDocument("Age", 20); subject.Options.Collation = new Collation("en_US"); subject.Options.Comment = "awesome"; @@ -333,8 +342,12 @@ public void ToString_should_return_the_correct_string() var str = find.ToString(); + var expectedProjection = linqProvider == LinqProvider.V2 ? + "{ \"FirstName\" : 1, \"LastName\" : 1, \"_id\" : 0 }" : + "{ \"_v\" : { \"$concat\" : [\"$FirstName\", \" \", \"$LastName\"] }, \"_id\" : 0 }"; + str.Should().Be( - "find({ \"Age\" : 20 }, { \"FirstName\" : 1, \"LastName\" : 1, \"_id\" : 0 })" + + "find({ \"Age\" : 20 }, " + expectedProjection + ")" + ".collation({ \"locale\" : \"en_US\" })" + ".sort({ \"LastName\" : 1, \"FirstName\" : -1 })" + ".skip(2)" + @@ -356,9 +369,9 @@ private IClientSessionHandle CreateSession(bool usingSession) return usingSession ? Mock.Of() : null; } - private IFindFluent CreateSubject(IClientSessionHandle session = null, FilterDefinition filter = null, FindOptions options = null) + private IFindFluent CreateSubject(IClientSessionHandle session = null, FilterDefinition filter = null, FindOptions options = null, LinqProvider linqProvider = LinqProvider.V3) { - var clientSettings = new MongoClientSettings { LinqProvider = LinqProvider.V2 }; + var clientSettings = new MongoClientSettings { LinqProvider = linqProvider }; var mockClient = new Mock(); mockClient.SetupGet(c => c.Settings).Returns(clientSettings); diff --git a/tests/MongoDB.Driver.Tests/IFindFluentExtensionsTests.cs b/tests/MongoDB.Driver.Tests/IFindFluentExtensionsTests.cs index 60bc77841cc..afdbd877a87 100644 --- a/tests/MongoDB.Driver.Tests/IFindFluentExtensionsTests.cs +++ b/tests/MongoDB.Driver.Tests/IFindFluentExtensionsTests.cs @@ -20,13 +20,17 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Linq; +using MongoDB.Driver.Tests.Linq.Linq3ImplementationTests; using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; namespace MongoDB.Driver.Tests { - public class IFindFluentExtensionsTests + public class IFindFluentExtensionsTests : Linq3IntegrationTest { // public methods [Theory] @@ -228,15 +232,27 @@ public void Project_should_generate_the_correct_fields_when_a_string_is_used() AssertProjection(subject, expectedProjection); } - [Fact] - public void Project_should_generate_the_correct_fields_and_assign_the_correct_result_serializer() + [Theory] + [ParameterAttributeData] + public void Project_should_generate_the_correct_fields_and_assign_the_correct_result_serializer( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { - var subject = CreateSubject() + if (linqProvider == LinqProvider.V3) + { + RequireServer.Check().Supports(Feature.FindProjectionExpressions); + } + + var subject = CreateSubject(linqProvider) .Project(x => x.FirstName + " " + x.LastName); - var expectedProjection = BsonDocument.Parse("{FirstName: 1, LastName: 1, _id: 0}"); + var expectedProjection = linqProvider == LinqProvider.V2 ? + BsonDocument.Parse("{ FirstName : 1, LastName : 1, _id : 0}") : + BsonDocument.Parse("{ _v : { $concat : ['$FirstName', ' ', '$LastName'] }, _id : 0 }"); - AssertProjection(subject, expectedProjection); + AssertProjection(subject, expectedProjection, linqProvider); + + var results = subject.ToList(); + results.Should().Equal("John Doe"); } [Theory] @@ -435,9 +451,9 @@ public void SortByDescending_ThenByDescending_should_generate_the_correct_sort() AssertSort(subject, expectedSort); } - private static void AssertProjection(IFindFluent subject, BsonDocument expectedProjection) + private static void AssertProjection(IFindFluent subject, BsonDocument expectedProjection, LinqProvider linqProvider = LinqProvider.V3) { - Assert.Equal(expectedProjection, subject.Options.Projection.Render(BsonSerializer.SerializerRegistry.GetSerializer(), BsonSerializer.SerializerRegistry).Document); + Assert.Equal(expectedProjection, subject.Options.Projection.Render(BsonSerializer.SerializerRegistry.GetSerializer(), BsonSerializer.SerializerRegistry, linqProvider).Document); } private static void AssertSort(IFindFluent subject, BsonDocument expectedSort) @@ -445,13 +461,21 @@ private static void AssertSort(IFindFluent subject, BsonDocument Assert.Equal(expectedSort, subject.Options.Sort.Render(BsonSerializer.SerializerRegistry.GetSerializer(), BsonSerializer.SerializerRegistry)); } - private IFindFluent CreateSubject() + private IMongoCollection CreateCollection(LinqProvider linqProvider = LinqProvider.V3) + { + var collection = GetCollection(linqProvider: linqProvider); + + CreateCollection( + collection, + new Person { FirstName = "John", LastName = "Doe", Age = 21 }); + + return collection; + } + + private IFindFluent CreateSubject(LinqProvider linqProvider = LinqProvider.V3) { - var settings = new MongoCollectionSettings(); - var mockCollection = new Mock>(); - mockCollection.SetupGet(c => c.Settings).Returns(settings); - var options = new FindOptions(); - return new FindFluent(session: null, collection: mockCollection.Object, filter: new BsonDocument(), options: options); + var collection = CreateCollection(linqProvider); + return collection.Find("{}"); } public class Person diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/LinqProviderV3Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/LinqProviderV3Tests.cs index de37e67e1f7..ec37f9f9eb5 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/LinqProviderV3Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/LinqProviderV3Tests.cs @@ -139,9 +139,7 @@ public void TranslateExpressionToFindProjection_should_return_expected_result() var result = subject.TranslateExpressionToFindProjection(expression, documentSerializer, serializerRegistry); - var expectedResult = LinqProviderAdapter.V2.TranslateExpressionToFindProjection(expression, documentSerializer, serializerRegistry); - expectedResult.Document.Should().Be("{ X : 1, _id : 0 }"); - result.Document.Should().Be(expectedResult.Document); + result.Document.Should().Be("{ _v : '$X', _id : 0 }"); result.ProjectionSerializer.ValueType.Should().Be(typeof(int)); }