Skip to content

Commit 92ab159

Browse files
CSHARP-2757: Unable to call ToString on Projections.
1 parent 6e438c0 commit 92ab159

File tree

4 files changed

+181
-8
lines changed

4 files changed

+181
-8
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class Feature
3737
private static readonly Feature __aggregateLet = new Feature("AggregateLet", new SemanticVersion(3, 6, 0));
3838
private static readonly Feature __aggregateMerge = new Feature("AggregateMerge", new SemanticVersion(4, 2, 0));
3939
private static readonly Feature __aggregateOut = new Feature("AggregateOut", new SemanticVersion(2, 6, 0));
40+
private static readonly Feature __aggregateToString = new Feature("AggregateToString", new SemanticVersion(4, 0, 0));
4041
private static readonly ArrayFiltersFeature __arrayFilters = new ArrayFiltersFeature("ArrayFilters", new SemanticVersion(3, 5, 11));
4142
private static readonly Feature __bypassDocumentValidation = new Feature("BypassDocumentValidation", new SemanticVersion(3, 2, 0));
4243
private static readonly Feature __changeStreamStage = new Feature("ChangeStreamStage", new SemanticVersion(3, 5, 11));
@@ -150,6 +151,11 @@ public class Feature
150151
/// </summary>
151152
public static Feature AggregateOut => __aggregateOut;
152153

154+
/// <summary>
155+
/// Gets the aggregate toString feature.
156+
/// </summary>
157+
public static Feature AggregateToString => __aggregateToString;
158+
153159
/// <summary>
154160
/// Gets the arrayFilters feature.
155161
/// </summary>

src/MongoDB.Driver/Linq/Translators/AggregateLanguageTranslator.cs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,26 @@ private BsonValue TranslateMethodCall(MethodCallExpression node)
393393
return result;
394394
}
395395

396+
if (node.Object.Type == typeof(ObjectId)
397+
&& TryTranslateObjectIdCall(node, out result))
398+
{
399+
return result;
400+
}
401+
402+
Type[] supportedSimpleTypes =
403+
{
404+
typeof(bool),
405+
typeof(int),
406+
typeof(long),
407+
typeof(double),
408+
typeof(decimal)
409+
};
410+
if (supportedSimpleTypes.Contains(node.Object.Type)
411+
&& TryTranslateSimpleTypeCall(node, out result))
412+
{
413+
return result;
414+
}
415+
396416
if (node.Object.Type.GetTypeInfo().IsGenericType
397417
&& node.Object.Type.GetGenericTypeDefinition() == typeof(HashSet<>)
398418
&& TryTranslateHashSetMethodCall(node, out result))
@@ -479,7 +499,7 @@ private BsonValue TranslateNewDateTime(NewExpression node)
479499
}
480500

481501
if (year == null)
482-
{
502+
{
483503
throw new NotSupportedException($"The DateTime constructor {node} is not supported.");
484504
}
485505

@@ -823,7 +843,10 @@ private bool TryTranslateDateTimeCall(MethodCallExpression node, out BsonValue r
823843
});
824844
return true;
825845
}
826-
break;
846+
else
847+
{
848+
return TryTranslateViaToString(node, field, out result);
849+
}
827850
}
828851

829852
return false;
@@ -975,6 +998,24 @@ private bool TryTranslateMinResultOperator(PipelineExpression node, out BsonValu
975998
return false;
976999
}
9771000

1001+
private bool TryTranslateObjectIdCall(MethodCallExpression node, out BsonValue result)
1002+
{
1003+
var field = TranslateValue(node.Object);
1004+
return TryTranslateViaToString(node, field, out result); // supports only ToString for now
1005+
}
1006+
1007+
private bool TryTranslateSimpleTypeCall(MethodCallExpression node, out BsonValue result)
1008+
{
1009+
result = null;
1010+
var field = TranslateValue(node.Object);
1011+
switch (node.Method.Name)
1012+
{
1013+
case "ToString":
1014+
return TryTranslateViaToString(node, field, out result);
1015+
}
1016+
return false;
1017+
}
1018+
9781019
private bool TryTranslateStaticDateTimeMethodCall(MethodCallExpression node, out BsonValue result)
9791020
{
9801021
result = null;
@@ -1245,6 +1286,8 @@ private bool TryTranslateStringMethodCall(MethodCallExpression node, out BsonVal
12451286
return true;
12461287
}
12471288
break;
1289+
case "ToString":
1290+
return TryTranslateViaToString(node, field, out result);
12481291
}
12491292

12501293
return false;
@@ -1276,5 +1319,19 @@ private bool TryTranslateSumResultOperator(PipelineExpression node, out BsonValu
12761319
result = null;
12771320
return false;
12781321
}
1322+
1323+
private bool TryTranslateViaToString(MethodCallExpression node, BsonValue field, out BsonValue result)
1324+
{
1325+
result = null;
1326+
if (node.Arguments.Count == 0)
1327+
{
1328+
result = new BsonDocument("$toString", field);
1329+
return true;
1330+
}
1331+
else
1332+
{
1333+
return false;
1334+
}
1335+
}
12791336
}
12801337
}

tests/MongoDB.Driver.Tests/Linq/IntegrationTestBase.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ private void InsertFirst()
7272
{
7373
var root = new Root
7474
{
75+
ObjectId = ObjectId.Parse("555925bfb69aa7d5be29126b"),
7576
A = "Awesome",
7677
B = "Balloon",
7778
C = new C
@@ -151,7 +152,7 @@ private void InsertFirst()
151152
new E
152153
{
153154
F = 4
154-
}
155+
}
155156
}
156157
}
157158
}
@@ -182,7 +183,11 @@ private void InsertFirst()
182183
R = new DateTime(2013, 1, 2, 3, 4, 5, 6, DateTimeKind.Utc),
183184
T = new Dictionary<string, int> { { "one", 1 }, { "two", 2 } },
184185
U = 1.23456571661743267789m,
185-
V = "2017-02-08T12:10:40.787"
186+
V = "2017-02-08T12:10:40.787",
187+
W = 8,
188+
X = 9,
189+
Y = 10,
190+
Z = 11
186191
};
187192
__collection.InsertOne(root);
188193
}
@@ -232,7 +237,8 @@ private void InsertSecond()
232237
M = new[] { 3, 5, 6 },
233238
O = new List<long> { 100, 200, 300 },
234239
P = 1.1,
235-
U = -1.234565723762724332233489m
240+
U = -1.234565723762724332233489m,
241+
Z = 10
236242
};
237243
__collection.InsertOne(root);
238244
}
@@ -270,6 +276,8 @@ public class DerivedRootView : RootView
270276

271277
public class Root : IRoot
272278
{
279+
public ObjectId ObjectId { get; set; }
280+
273281
public int Id { get; set; }
274282

275283
public string A { get; set; }
@@ -300,6 +308,14 @@ public class Root : IRoot
300308
public decimal U { get; set; }
301309

302310
public string V { get; set; }
311+
312+
public double W { get; set; }
313+
314+
public long X { get; set; }
315+
316+
public int Y { get; set; }
317+
318+
public decimal Z { get; set; }
303319
}
304320

305321
public class RootDescended : Root

tests/MongoDB.Driver.Tests/Linq/Translators/AggregateProjectTranslatorTests.cs

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,10 @@
1717
using System.Collections.Generic;
1818
using System.Linq;
1919
using System.Linq.Expressions;
20-
using System.Threading.Tasks;
2120
using FluentAssertions;
2221
using MongoDB.Bson;
2322
using MongoDB.Bson.Serialization;
24-
using MongoDB.Bson.TestHelpers.XunitExtensions;
25-
using MongoDB.Driver.Core;
23+
using MongoDB.Driver.Core.Misc;
2624
using MongoDB.Driver.Core.TestHelpers.XunitExtensions;
2725
using MongoDB.Driver.Linq;
2826
using MongoDB.Driver.Linq.Translators;
@@ -339,6 +337,18 @@ public void Should_translate_avg_with_selector()
339337
result.Value.Result.Should().Be(44);
340338
}
341339

340+
[SkippableFact]
341+
public void Should_translate_boolToString()
342+
{
343+
RequireServer.Check().Supports(Feature.AggregateToString);
344+
345+
var result = Project(x => new { Result = x.K.ToString() });
346+
347+
result.Projection.Should().Be("{ Result: { \"$toString\": \"$K\" }, _id: 0 }");
348+
349+
result.Value.Result.Should().Be("true");
350+
}
351+
342352
[SkippableFact]
343353
public void Should_translate_ceil()
344354
{
@@ -417,6 +427,18 @@ public void Should_translate_condition()
417427
result.Value.Result.Should().Be("b");
418428
}
419429

430+
[SkippableFact]
431+
public void Should_translate_dateTimeToString()
432+
{
433+
RequireServer.Check().Supports(Feature.AggregateToString);
434+
435+
var result = Project(x => new { Result = x.J.ToString() }); // without a format argument
436+
437+
result.Projection.Should().Be("{ Result: { \"$toString\": \"$J\" }, _id: 0 }");
438+
439+
result.Value.Result.Should().Be("2012-12-01T13:14:15.016Z");
440+
}
441+
420442
[SkippableFact]
421443
public void Should_translate_dateToString()
422444
{
@@ -459,6 +481,18 @@ public void Should_translate_day_of_year()
459481
result.Value.Result.Should().Be(336);
460482
}
461483

484+
[SkippableFact]
485+
public void Should_translate_decimalToString()
486+
{
487+
RequireServer.Check().Supports(Feature.AggregateToString);
488+
489+
var result = Project(x => new { Result = x.Z.ToString() });
490+
491+
result.Projection.Should().Be("{ Result: { \"$toString\": \"$Z\" }, _id: 0 }");
492+
493+
result.Value.Result.Should().Be("11");
494+
}
495+
462496
[Fact]
463497
public void Should_translate_divide()
464498
{
@@ -479,6 +513,18 @@ public void Should_translate_divide_3_numbers()
479513
result.Value.Result.Should().BeApproximately(0.04, .01);
480514
}
481515

516+
[SkippableFact]
517+
public void Should_translate_doubleToString()
518+
{
519+
RequireServer.Check().Supports(Feature.AggregateToString);
520+
521+
var result = Project(x => new { Result = x.W.ToString() });
522+
523+
result.Projection.Should().Be("{ Result: { \"$toString\": \"$W\" }, _id: 0 }");
524+
525+
result.Value.Result.Should().Be("8");
526+
}
527+
482528
[Fact]
483529
public void Should_translate_equals()
484530
{
@@ -613,6 +659,18 @@ public void Should_translate_indexOfCP()
613659
result.Value.Result.Should().Be(-1);
614660
}
615661

662+
[SkippableFact]
663+
public void Should_translate_intToString()
664+
{
665+
RequireServer.Check().Supports(Feature.AggregateToString);
666+
667+
var result = Project(x => new { Result = x.Y.ToString() });
668+
669+
result.Projection.Should().Be("{ Result: { \"$toString\": \"$Y\" }, _id: 0 }");
670+
671+
result.Value.Result.Should().Be("10");
672+
}
673+
616674
[Fact]
617675
public void Should_translate_less_than()
618676
{
@@ -681,6 +739,18 @@ public void Should_translate_log10()
681739
result.Value.Result.Should().BeApproximately(1.0413928515823, .0001);
682740
}
683741

742+
[SkippableFact]
743+
public void Should_translate_longToString()
744+
{
745+
RequireServer.Check().Supports(Feature.AggregateToString);
746+
747+
var result = Project(x => new { Result = x.X.ToString() });
748+
749+
result.Projection.Should().Be("{ Result: { \"$toString\": \"$X\" }, _id: 0 }");
750+
751+
result.Value.Result.Should().Be("9");
752+
}
753+
684754
[SkippableFact]
685755
public void Should_translate_map_with_document()
686756
{
@@ -863,6 +933,18 @@ public void Should_translate_not_equals()
863933
result.Value.Result.Should().BeTrue();
864934
}
865935

936+
[SkippableFact]
937+
public void Should_translate_objectIdToString()
938+
{
939+
RequireServer.Check().Supports(Feature.AggregateToString);
940+
941+
var result = Project(x => new { Result = x.ObjectId.ToString() });
942+
943+
result.Projection.Should().Be("{ Result: { \"$toString\": \"$ObjectId\" }, _id: 0 }");
944+
945+
result.Value.Result.Should().Be("555925bfb69aa7d5be29126b");
946+
}
947+
866948
[Fact]
867949
public void Should_translate_or()
868950
{
@@ -1159,6 +1241,18 @@ public void Should_translate_sqrt()
11591241
result.Value.Result.Should().BeApproximately(3.31662479, .0001);
11601242
}
11611243

1244+
[SkippableFact]
1245+
public void Should_translate_stringToString()
1246+
{
1247+
RequireServer.Check().Supports(Feature.AggregateToString);
1248+
1249+
var result = Project(x => new { Result = x.A.ToString() });
1250+
1251+
result.Projection.Should().Be("{ Result: { \"$toString\": \"$A\" }, _id: 0 }");
1252+
1253+
result.Value.Result.Should().Be("Awesome");
1254+
}
1255+
11621256
[SkippableFact]
11631257
public void Should_translate_trunc()
11641258
{

0 commit comments

Comments
 (0)