-
Notifications
You must be signed in to change notification settings - Fork 1.3k
CSHARP-5730: Support static String.Compare method #1789
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
| var strATranslation = ExpressionToAggregationExpressionTranslator.Translate(context, strAExpression); | ||
| var strBExpression = arguments[1]; | ||
| var strBTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, strBExpression); | ||
| var ast = AstExpression.Cmp(strATranslation.Ast, strBTranslation.Ast); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add support for case insensitive/more overloads comparison by translating to $strcasecmp?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose if we do this, we will have to support this compare overload -> https://learn.microsoft.com/en-us/dotnet/api/system.string.compare?view=net-9.0#system-string-compare(system-string-system-string-system-boolean)
| var strATranslation = ExpressionToAggregationExpressionTranslator.Translate(context, strAExpression); | ||
| var strBExpression = arguments[1]; | ||
| var strBTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, strBExpression); | ||
| var ast = AstExpression.Cmp(strATranslation.Ast, strBTranslation.Ast); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose if we do this, we will have to support this compare overload -> https://learn.microsoft.com/en-us/dotnet/api/system.string.compare?view=net-9.0#system-string-compare(system-string-system-string-system-boolean)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that this class was extended to support the static string.Compare method, it seems its name is a bit outdated now? I would suggest updating the class name though I don't have a good suggestion maybe StringComparisonExpressionToFilterTranslator?
| throw new ExpressionNotSupportedException(expression); | ||
| } | ||
|
|
||
| private static bool IsStaticCompareMethod(MethodInfo method) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The IsStaticCompareMethod doesn't actually check if the method is named "Compare". I doubt this is intentional?
rstam
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did some refactoring and added support for the overload of Compare that has an ignoreCase parameter.
|
|
||
| internal static class AstComparisonFilterOperatorExtensions | ||
| { | ||
| public static AstComparisonFilterOperator GetComparisonOperatorForSwappedLeftAndRight(this AstComparisonFilterOperator @operator) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put here so that it can be used from more than one place.
| return false; | ||
| } | ||
|
|
||
| public static bool IsInstanceCompareToMethod(this MethodInfo method) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put here so that it can be used from more than one place.
|
|
||
| namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators | ||
| { | ||
| internal static class CompareMethodToAggregationExpressionTranslator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compare and CompareTo handling is consolidated into a single class.
|
|
||
| namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.ExpressionTranslators | ||
| { | ||
| internal static class CompareComparisonExpressionToFilterTranslator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compare and CompareTo handling is consolidated into a single class.
| { | ||
| Expression value1Expression; | ||
| Expression value2Expression; | ||
| if (method.IsStatic) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main difference between Compare and CompareTo is whether it's a static method or not.
|
|
||
| Expression fieldExpression; | ||
| Expression innerValueExpression; | ||
| if (compareMethod.IsStatic) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main difference between Compare and CompareTo is whether it's a static method or not.
| } | ||
| } | ||
|
|
||
| private static AstComparisonFilterOperator GetComparisonOperatorForSwappedLeftAndRight(Expression expression) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to an extension method so it can be used from more than one class.
| [InlineData(11, false, "{ $match : { B : { $gte : 'A' } } }", new int[] { 1, 2, 3, 4, 5, 6 })] | ||
| [InlineData(12, false, "{ $match : { B : { $lte : 'A' } } }", new int[] { 1, 3 })] | ||
| [InlineData(1, true, "{ $match : { $expr : { $eq : [{ $strcasecmp : ['A', '$B'] }, -1] } } }", new int[] { 2, 5 })] | ||
| [InlineData(2, true, "{ $match : { $expr : { $eq : [{ $strcasecmp : ['A', '$B'] }, 0] } } }", new int[] { 1, 3, 4, 6 })] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one might have been possible without using $expr by using regular expressions but I don't think it's worth the effort.
sanych-sun
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM + minor comments
|
|
||
| namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira; | ||
|
|
||
| public class CSharp5730Tests : LinqIntegrationTest<CSharp5730Tests.ClassFixture> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have tests for CompareTo and IComparable<T>.CompareTo?
|
|
||
| var value1Translation = ExpressionToAggregationExpressionTranslator.Translate(context, value1Expression); | ||
| var value2Translation = ExpressionToAggregationExpressionTranslator.Translate(context, value2Expression); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we check if the value1 and value2 has string representation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds support for the static String.Compare method in LINQ queries, enabling translation of string comparison operations to MongoDB queries. The implementation extends the existing CompareTo method support to include both static and instance comparison methods, with optional case-insensitive comparison support.
Key Changes:
- Unified comparison method translation by consolidating
String.Compare,CompareTo, andIComparable<T>.CompareTohandling - Added support for case-insensitive string comparison using MongoDB's
$strcasecmpoperator - Refactored comparison operator swapping logic into a reusable extension method
Reviewed Changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5730Tests.cs | Comprehensive test suite covering all comparison scenarios with static Compare, instance CompareTo, and IComparable methods, testing both case-sensitive and case-insensitive comparisons |
| src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/ComparisonExpressionToFilterTranslator.cs | Refactored to use the new unified CompareComparisonExpressionToFilterTranslator and extracted operator swapping logic |
| src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/CompareToComparisonExpressionToFilterTranslator.cs | Removed file - functionality consolidated into CompareComparisonExpressionToFilterTranslator |
| src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/CompareComparisonExpressionToFilterTranslator.cs | New unified translator handling static Compare, instance CompareTo, and IComparable methods, including ignoreCase parameter support |
| src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/CompareToMethodToAggregationExpressionTranslator.cs | Removed file - functionality consolidated into CompareMethodToAggregationExpressionTranslator |
| src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/CompareMethodToAggregationExpressionTranslator.cs | New unified translator for aggregation expressions supporting both static and instance comparison methods, with conditional $strcasecmp usage |
| src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs | Updated routing to use new CompareMethodToAggregationExpressionTranslator for both Compare and CompareTo methods |
| src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/StringMethod.cs | Added static field definitions for String.Compare methods (with and without ignoreCase parameter) |
| src/MongoDB.Driver/Linq/Linq3Implementation/Misc/MethodInfoExtensions.cs | Added extension methods to identify static Compare and instance CompareTo methods via reflection |
| src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Filters/AstComparisonFilterOperator.cs | Added GetComparisonOperatorForSwappedLeftAndRight extension method to centralize operator reversal logic |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| var ignoreCase = ignoreCaseExpression.GetConstantValue<bool>(containingExpression: compareMethodCallExpression); | ||
| if (ignoreCase) | ||
| { | ||
| throw new ExpressionNotSupportedException(compareMethodCallExpression, because: "ignoreCase must be false"); |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message 'ignoreCase must be false' is misleading in a filter context. The code should not throw an exception when ignoreCase is true; instead it should allow ignoreCase but not generate a simplified field comparison. The current implementation incorrectly restricts functionality that should work (though it may require $expr). Consider either supporting ignoreCase:true with $expr or clarifying the error message.
| throw new ExpressionNotSupportedException(compareMethodCallExpression, because: "ignoreCase must be false"); | |
| throw new ExpressionNotSupportedException(compareMethodCallExpression, because: "Case-insensitive comparisons (ignoreCase: true) are not supported in this filter context."); |
sanych-sun
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
No description provided.