Skip to content

Commit c0c521e

Browse files
committed
CSHARP-4449: Implement Find projections in LINQ3.
1 parent 396830c commit c0c521e

File tree

5 files changed

+65
-25
lines changed

5 files changed

+65
-25
lines changed

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

+6
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public class Feature
7474
private static readonly Feature __findAllowDiskUse = new Feature("FindAllowDiskUse", WireVersion.Server44);
7575
private static readonly Feature __findAndModifyWriteConcern = new Feature("FindAndModifyWriteConcern", WireVersion.Server32);
7676
private static readonly Feature __findCommand = new Feature("FindCommand", WireVersion.Server32);
77+
private static readonly Feature __findProjectionExpressions = new Feature("FindProjectionExpressions", WireVersion.Server44);
7778
private static readonly Feature __geoNearCommand = new Feature("GeoNearCommand", WireVersion.Zero, WireVersion.Server42);
7879
private static readonly Feature __getField = new Feature("GetField", WireVersion.Server50);
7980
private static readonly Feature __getMoreComment = new Feature("GetMoreComment", WireVersion.Server44);
@@ -409,6 +410,11 @@ public class Feature
409410
[Obsolete("This property will be removed in a later release.")]
410411
public static Feature FindCommand => __findCommand;
411412

413+
/// <summary>
414+
/// Gets the find projection expressions feature.
415+
/// </summary>
416+
public static Feature FindProjectionExpressions => __findProjectionExpressions;
417+
412418
/// <summary>
413419
/// Gets the geoNear command feature.
414420
/// </summary>

src/MongoDB.Driver/Linq/Linq3Implementation/LinqProviderAdapterV3.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,7 @@ internal override RenderedProjectionDefinition<TProjection> TranslateExpressionT
140140
IBsonSerializer<TSource> sourceSerializer,
141141
IBsonSerializerRegistry serializerRegistry)
142142
{
143-
// TODO: implement using LINQ3 instead of falling back to LINQ2
144-
return LinqProviderAdapter.V2.TranslateExpressionToFindProjection(expression, sourceSerializer, serializerRegistry);
143+
return TranslateExpressionToProjection(expression, sourceSerializer, serializerRegistry, translationOptions: null);
145144
}
146145

147146
internal override RenderedProjectionDefinition<TOutput> TranslateExpressionToGroupProjection<TInput, TKey, TOutput>(

tests/MongoDB.Driver.Tests/FindFluentTests.cs

+19-6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
using MongoDB.Driver.Linq;
2525
using Moq;
2626
using Xunit;
27+
using MongoDB.Driver.Core.TestHelpers.XunitExtensions;
28+
using MongoDB.Driver.Core.Misc;
2729

2830
namespace MongoDB.Driver.Tests
2931
{
@@ -303,10 +305,17 @@ public void ToCursor_should_call_collection_Find_with_expected_arguments(
303305
}
304306
}
305307

306-
[Fact]
307-
public void ToString_should_return_the_correct_string()
308+
[Theory]
309+
[ParameterAttributeData]
310+
public void ToString_should_return_the_correct_string(
311+
[Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider)
308312
{
309-
var subject = CreateSubject();
313+
if (linqProvider == LinqProvider.V3)
314+
{
315+
RequireServer.Check().Supports(Feature.FindProjectionExpressions);
316+
}
317+
318+
var subject = CreateSubject(linqProvider: linqProvider);
310319
subject.Filter = new BsonDocument("Age", 20);
311320
subject.Options.Collation = new Collation("en_US");
312321
subject.Options.Comment = "awesome";
@@ -333,8 +342,12 @@ public void ToString_should_return_the_correct_string()
333342

334343
var str = find.ToString();
335344

345+
var expectedProjection = linqProvider == LinqProvider.V2 ?
346+
"{ \"FirstName\" : 1, \"LastName\" : 1, \"_id\" : 0 }" :
347+
"{ \"_v\" : { \"$concat\" : [\"$FirstName\", \" \", \"$LastName\"] }, \"_id\" : 0 }";
348+
336349
str.Should().Be(
337-
"find({ \"Age\" : 20 }, { \"FirstName\" : 1, \"LastName\" : 1, \"_id\" : 0 })" +
350+
"find({ \"Age\" : 20 }, " + expectedProjection + ")" +
338351
".collation({ \"locale\" : \"en_US\" })" +
339352
".sort({ \"LastName\" : 1, \"FirstName\" : -1 })" +
340353
".skip(2)" +
@@ -356,9 +369,9 @@ private IClientSessionHandle CreateSession(bool usingSession)
356369
return usingSession ? Mock.Of<IClientSessionHandle>() : null;
357370
}
358371

359-
private IFindFluent<Person, Person> CreateSubject(IClientSessionHandle session = null, FilterDefinition<Person> filter = null, FindOptions<Person, Person> options = null)
372+
private IFindFluent<Person, Person> CreateSubject(IClientSessionHandle session = null, FilterDefinition<Person> filter = null, FindOptions<Person, Person> options = null, LinqProvider linqProvider = LinqProvider.V3)
360373
{
361-
var clientSettings = new MongoClientSettings { LinqProvider = LinqProvider.V2 };
374+
var clientSettings = new MongoClientSettings { LinqProvider = linqProvider };
362375
var mockClient = new Mock<IMongoClient>();
363376
mockClient.SetupGet(c => c.Settings).Returns(clientSettings);
364377

tests/MongoDB.Driver.Tests/IFindFluentExtensionsTests.cs

+38-14
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@
2020
using FluentAssertions;
2121
using MongoDB.Bson;
2222
using MongoDB.Bson.Serialization;
23+
using MongoDB.Driver.Core.Misc;
24+
using MongoDB.Driver.Core.TestHelpers.XunitExtensions;
25+
using MongoDB.Driver.Linq;
26+
using MongoDB.Driver.Tests.Linq.Linq3ImplementationTests;
2327
using MongoDB.TestHelpers.XunitExtensions;
2428
using Moq;
2529
using Xunit;
2630

2731
namespace MongoDB.Driver.Tests
2832
{
29-
public class IFindFluentExtensionsTests
33+
public class IFindFluentExtensionsTests : Linq3IntegrationTest
3034
{
3135
// public methods
3236
[Theory]
@@ -228,15 +232,27 @@ public void Project_should_generate_the_correct_fields_when_a_string_is_used()
228232
AssertProjection(subject, expectedProjection);
229233
}
230234

231-
[Fact]
232-
public void Project_should_generate_the_correct_fields_and_assign_the_correct_result_serializer()
235+
[Theory]
236+
[ParameterAttributeData]
237+
public void Project_should_generate_the_correct_fields_and_assign_the_correct_result_serializer(
238+
[Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider)
233239
{
234-
var subject = CreateSubject()
240+
if (linqProvider == LinqProvider.V3)
241+
{
242+
RequireServer.Check().Supports(Feature.FindProjectionExpressions);
243+
}
244+
245+
var subject = CreateSubject(linqProvider)
235246
.Project(x => x.FirstName + " " + x.LastName);
236247

237-
var expectedProjection = BsonDocument.Parse("{FirstName: 1, LastName: 1, _id: 0}");
248+
var expectedProjection = linqProvider == LinqProvider.V2 ?
249+
BsonDocument.Parse("{ FirstName : 1, LastName : 1, _id : 0}") :
250+
BsonDocument.Parse("{ _v : { $concat : ['$FirstName', ' ', '$LastName'] }, _id : 0 }");
238251

239-
AssertProjection(subject, expectedProjection);
252+
AssertProjection(subject, expectedProjection, linqProvider);
253+
254+
var results = subject.ToList();
255+
results.Should().Equal("John Doe");
240256
}
241257

242258
[Theory]
@@ -435,23 +451,31 @@ public void SortByDescending_ThenByDescending_should_generate_the_correct_sort()
435451
AssertSort(subject, expectedSort);
436452
}
437453

438-
private static void AssertProjection<TResult>(IFindFluent<Person, TResult> subject, BsonDocument expectedProjection)
454+
private static void AssertProjection<TResult>(IFindFluent<Person, TResult> subject, BsonDocument expectedProjection, LinqProvider linqProvider = LinqProvider.V3)
439455
{
440-
Assert.Equal(expectedProjection, subject.Options.Projection.Render(BsonSerializer.SerializerRegistry.GetSerializer<Person>(), BsonSerializer.SerializerRegistry).Document);
456+
Assert.Equal(expectedProjection, subject.Options.Projection.Render(BsonSerializer.SerializerRegistry.GetSerializer<Person>(), BsonSerializer.SerializerRegistry, linqProvider).Document);
441457
}
442458

443459
private static void AssertSort(IFindFluent<Person, Person> subject, BsonDocument expectedSort)
444460
{
445461
Assert.Equal(expectedSort, subject.Options.Sort.Render(BsonSerializer.SerializerRegistry.GetSerializer<Person>(), BsonSerializer.SerializerRegistry));
446462
}
447463

448-
private IFindFluent<Person, Person> CreateSubject()
464+
private IMongoCollection<Person> CreateCollection(LinqProvider linqProvider = LinqProvider.V3)
465+
{
466+
var collection = GetCollection<Person>(linqProvider: linqProvider);
467+
468+
CreateCollection(
469+
collection,
470+
new Person { FirstName = "John", LastName = "Doe", Age = 21 });
471+
472+
return collection;
473+
}
474+
475+
private IFindFluent<Person, Person> CreateSubject(LinqProvider linqProvider = LinqProvider.V3)
449476
{
450-
var settings = new MongoCollectionSettings();
451-
var mockCollection = new Mock<IMongoCollection<Person>>();
452-
mockCollection.SetupGet(c => c.Settings).Returns(settings);
453-
var options = new FindOptions<Person, Person>();
454-
return new FindFluent<Person, Person>(session: null, collection: mockCollection.Object, filter: new BsonDocument(), options: options);
477+
var collection = CreateCollection(linqProvider);
478+
return collection.Find("{}");
455479
}
456480

457481
public class Person

tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/LinqProviderV3Tests.cs

+1-3
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,7 @@ public void TranslateExpressionToFindProjection_should_return_expected_result()
139139

140140
var result = subject.TranslateExpressionToFindProjection(expression, documentSerializer, serializerRegistry);
141141

142-
var expectedResult = LinqProviderAdapter.V2.TranslateExpressionToFindProjection(expression, documentSerializer, serializerRegistry);
143-
expectedResult.Document.Should().Be("{ X : 1, _id : 0 }");
144-
result.Document.Should().Be(expectedResult.Document);
142+
result.Document.Should().Be("{ _v : '$X', _id : 0 }");
145143
result.ProjectionSerializer.ValueType.Should().Be(typeof(int));
146144
}
147145

0 commit comments

Comments
 (0)