Skip to content

Commit fb728dc

Browse files
committed
CSHARP-5730: Support static String.Compare method
1 parent b73979a commit fb728dc

File tree

5 files changed

+172
-5
lines changed

5 files changed

+172
-5
lines changed

src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/StringMethod.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ internal static class StringMethod
7272
private static readonly MethodInfo __startsWithWithString;
7373
private static readonly MethodInfo __startsWithWithStringAndComparisonType;
7474
private static readonly MethodInfo __startsWithWithStringAndIgnoreCaseAndCulture;
75+
private static readonly MethodInfo __staticCompare;
7576
private static readonly MethodInfo __stringInWithEnumerable;
7677
private static readonly MethodInfo __stringInWithParams;
7778
private static readonly MethodInfo __stringNinWithEnumerable;
@@ -151,6 +152,7 @@ static StringMethod()
151152
__startsWithWithString = ReflectionInfo.Method((string s, string value) => s.StartsWith(value));
152153
__startsWithWithStringAndComparisonType = ReflectionInfo.Method((string s, string value, StringComparison comparisonType) => s.StartsWith(value, comparisonType));
153154
__startsWithWithStringAndIgnoreCaseAndCulture = ReflectionInfo.Method((string s, string value, bool ignoreCase, CultureInfo culture) => s.StartsWith(value, ignoreCase, culture));
155+
__staticCompare = ReflectionInfo.Method((string strA, string strB) => String.Compare(strA, strB));
154156
__stringInWithEnumerable = ReflectionInfo.Method((string s, IEnumerable<StringOrRegularExpression> values) => s.StringIn(values));
155157
__stringInWithParams = ReflectionInfo.Method((string s, StringOrRegularExpression[] values) => s.StringIn(values));
156158
__stringNinWithEnumerable = ReflectionInfo.Method((string s, IEnumerable<StringOrRegularExpression> values) => s.StringNin(values));
@@ -220,6 +222,7 @@ static StringMethod()
220222
public static MethodInfo StartsWithWithString => __startsWithWithString;
221223
public static MethodInfo StartsWithWithStringAndComparisonType => __startsWithWithStringAndComparisonType;
222224
public static MethodInfo StartsWithWithStringAndIgnoreCaseAndCulture => __startsWithWithStringAndIgnoreCaseAndCulture;
225+
public static MethodInfo StaticCompare => __staticCompare;
223226
public static MethodInfo StringInWithEnumerable => __stringInWithEnumerable;
224227
public static MethodInfo StringInWithParams => __stringInWithParams;
225228
public static MethodInfo StringNinWithEnumerable => __stringNinWithEnumerable;

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC
3333
case "AsQueryable": return AsQueryableMethodToAggregationExpressionTranslator.Translate(context, expression);
3434
case "Average": return AverageMethodToAggregationExpressionTranslator.Translate(context, expression);
3535
case "Ceiling": return CeilingMethodToAggregationExpressionTranslator.Translate(context, expression);
36+
case "Compare": return CompareMethodToAggregationExpressionTranslator.Translate(context, expression);
3637
case "CompareTo": return CompareToMethodToAggregationExpressionTranslator.Translate(context, expression);
3738
case "Concat": return ConcatMethodToAggregationExpressionTranslator.Translate(context, expression);
3839
case "Constant": return ConstantMethodToAggregationExpressionTranslator.Translate(context, expression);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System.Linq.Expressions;
17+
using MongoDB.Bson.Serialization.Serializers;
18+
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions;
19+
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
20+
21+
namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators
22+
{
23+
internal static class CompareMethodToAggregationExpressionTranslator
24+
{
25+
public static TranslatedExpression Translate(TranslationContext context, MethodCallExpression expression)
26+
{
27+
var method = expression.Method;
28+
var arguments = expression.Arguments;
29+
30+
if (method.Is(StringMethod.StaticCompare))
31+
{
32+
var strAExpression = arguments[0];
33+
var strATranslation = ExpressionToAggregationExpressionTranslator.Translate(context, strAExpression);
34+
var strBExpression = arguments[1];
35+
var strBTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, strBExpression);
36+
var ast = AstExpression.Cmp(strATranslation.Ast, strBTranslation.Ast);
37+
return new TranslatedExpression(expression, ast, Int32Serializer.Instance);
38+
}
39+
40+
throw new ExpressionNotSupportedException(expression);
41+
}
42+
}
43+
}

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/CompareToComparisonExpressionToFilterTranslator.cs

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
using System.Linq.Expressions;
17+
using System.Reflection;
1718
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Filters;
1819
using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods;
1920
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
@@ -28,7 +29,8 @@ public static bool CanTranslate(Expression leftExpression)
2829
{
2930
return
3031
leftExpression is MethodCallExpression leftMethodCallExpression &&
31-
IComparableMethod.IsCompareToMethod(leftMethodCallExpression.Method);
32+
leftMethodCallExpression.Method is var method &&
33+
(IComparableMethod.IsCompareToMethod(method) || IsStaticCompareMethod(method));
3234
}
3335

3436
// caller is responsible for ensuring constant is on the right
@@ -39,13 +41,27 @@ public static AstFilter Translate(
3941
AstComparisonFilterOperator comparisonOperator,
4042
Expression rightExpression)
4143
{
42-
if (leftExpression is MethodCallExpression leftMethodCallExpression &&
43-
IComparableMethod.IsCompareToMethod(leftMethodCallExpression.Method))
44+
if (CanTranslate(leftExpression))
4445
{
45-
var fieldExpression = leftMethodCallExpression.Object;
46+
var leftMethodCallExpression = (MethodCallExpression)leftExpression;
47+
var method= leftMethodCallExpression.Method;
48+
var arguments = leftMethodCallExpression.Arguments;
49+
50+
Expression fieldExpression;
51+
Expression valueExpression;
52+
if (method.IsStatic)
53+
{
54+
fieldExpression = arguments[0];
55+
valueExpression = arguments[1];
56+
}
57+
else
58+
{
59+
fieldExpression = leftMethodCallExpression.Object;
60+
valueExpression = arguments[0];
61+
}
62+
4663
var fieldTranslation = ExpressionToFilterFieldTranslator.Translate(context, fieldExpression);
4764

48-
var valueExpression = leftMethodCallExpression.Arguments[0];
4965
var value = valueExpression.GetConstantValue<object>(containingExpression: expression);
5066
var serializedValue = SerializationHelper.SerializeValue(fieldTranslation.Serializer, value);
5167

@@ -58,5 +74,17 @@ public static AstFilter Translate(
5874

5975
throw new ExpressionNotSupportedException(expression);
6076
}
77+
78+
private static bool IsStaticCompareMethod(MethodInfo method)
79+
{
80+
return
81+
method.IsStatic &&
82+
method.IsPublic &&
83+
method.ReturnType == typeof(int) &&
84+
method.GetParameters() is var parameters &&
85+
parameters.Length == 2 &&
86+
parameters[0].ParameterType == method.DeclaringType &&
87+
parameters[1].ParameterType == parameters[0].ParameterType;
88+
}
6189
}
6290
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System.Collections.Generic;
17+
using System.Linq;
18+
using MongoDB.Driver.TestHelpers;
19+
using FluentAssertions;
20+
using Xunit;
21+
22+
namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira;
23+
24+
public class CSharp5730Tests : LinqIntegrationTest<CSharp5730Tests.ClassFixture>
25+
{
26+
public CSharp5730Tests(ClassFixture fixture)
27+
: base(fixture)
28+
{
29+
}
30+
31+
[Fact]
32+
public void Where_String_Compare_greater_than_zero_should_work()
33+
{
34+
var collection = Fixture.Collection;
35+
36+
var queryable = collection.AsQueryable()
37+
.Where(d => string.Compare(d.Key, "a4e48b55-0519-4ab3-b6b9-7c532fc65b56") > 0);
38+
39+
var stages = Translate(collection, queryable);
40+
AssertStages(stages, "{ $match : { Key : { $gt : 'a4e48b55-0519-4ab3-b6b9-7c532fc65b56' } } }");
41+
42+
var result = queryable.ToList();
43+
result.Select(x => x. Id).Should().Equal(4);
44+
}
45+
46+
[Fact]
47+
public void Where_String_CompareTo_greater_than_zero_should_work()
48+
{
49+
var collection = Fixture.Collection;
50+
51+
var queryable = collection.AsQueryable()
52+
.Where(d => d.Key.CompareTo("a4e48b55-0519-4ab3-b6b9-7c532fc65b56") > 0);
53+
54+
var stages = Translate(collection, queryable);
55+
AssertStages(stages, "{ $match : { Key : { $gt : 'a4e48b55-0519-4ab3-b6b9-7c532fc65b56' } } }");
56+
57+
var result = queryable.ToList();
58+
result.Select(x => x. Id).Should().Equal(4);
59+
}
60+
61+
[Fact]
62+
public void Select_String_Compare_should_work()
63+
{
64+
var collection = Fixture.Collection;
65+
66+
var queryable = collection.AsQueryable()
67+
.Select(d => string.Compare(d.Key, "a4e48b55-0519-4ab3-b6b9-7c532fc65b56") > 0);
68+
69+
var stages = Translate(collection, queryable);
70+
AssertStages(stages, "{ $project : { _v : { $gt : [{ $cmp : ['$Key', 'a4e48b55-0519-4ab3-b6b9-7c532fc65b56'] }, 0] }, _id : 0 } }");
71+
72+
var results = queryable.ToList();
73+
results.Should().Equal(false, false, false, true);
74+
}
75+
76+
public class C
77+
{
78+
public int Id { get; set; }
79+
public string Key { get; set; }
80+
}
81+
82+
public sealed class ClassFixture : MongoCollectionFixture<C>
83+
{
84+
protected override IEnumerable<C> InitialData =>
85+
[
86+
new C { Id = 1, Key = "1b2bc240-ec2a-4a17-8790-8407e3bbb847"},
87+
new C { Id = 2, Key = "a4e48b55-0519-4ab3-b6b9-7c532fc65b56"},
88+
new C { Id = 3, Key = "9ff72c5d-189e-4511-b7ad-3f83489e4ea4"},
89+
new C { Id = 4, Key = "d78ca958-abac-46cd-94a7-fbf7a2ba683d"}
90+
];
91+
}
92+
}

0 commit comments

Comments
 (0)