CSHARP-5655: Support regular expressions in $replaceAll search string and $split delimiter#1955
CSHARP-5655: Support regular expressions in $replaceAll search string and $split delimiter#1955sanych-sun wants to merge 2 commits intomongodb:mainfrom
Conversation
… and $split delimiter
There was a problem hiding this comment.
Pull request overview
Adds LINQ3 translation support for using regular expressions as the $replaceAll.find argument and as the $split delimiter, along with accompanying serializer deduction and test coverage.
Changes:
- Implemented
$replaceAlltranslation forstring.Replace(...)andRegex.Replace(...)(including regex literals and regex values stored as BSON regular expressions). - Extended
$splittranslation to support regex delimiters viaRegex.Split(...)and regex instance.Split(...)when stored as BSON regular expressions, plus additionalstring.Splitoverload handling. - Added/updated unit + integration tests and updated expression preprocessing in test helpers.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/MongoDB.Driver.Tests/Linq/TestHelpers.cs | Preprocesses test expressions using LinqExpressionPreprocessor before serializer finding/translation. |
| tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/…/SplitMethodToAggregationExpressionTranslatorTests.cs | New unit tests for $split translation across many string.Split overloads and regex delimiters. |
| tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/…/ReplaceMethodToAggregationExpressionTranslatorTests.cs | New unit tests for $replaceAll translation including regex-based replace. |
| tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/SerializerFinders/StringTests.cs | Expands serializer-finder coverage for Replace/Split and adds unsupported-case assertions. |
| tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/SerializerFinders/RegexTests.cs | Expands serializer-finder coverage for regex Replace/Split and adds unsupported-case assertions. |
| tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5928Tests.cs | Updates a non-translatable-expression regression to use unsupported Regex.Replace overload and asserts message content. |
| tests/MongoDB.Driver.Tests/Linq/Integration/StringSplitTests.cs | New integration tests validating $split output for supported string.Split shapes. |
| tests/MongoDB.Driver.Tests/Linq/Integration/StringReplaceTests.cs | New integration tests validating $replaceAll for string.Replace (feature-gated). |
| tests/MongoDB.Driver.Tests/Linq/Integration/RegexSplitTests.cs | New integration tests validating regex delimiter support in $split (feature-gated). |
| tests/MongoDB.Driver.Tests/Linq/Integration/RegexReplaceTests.cs | New integration tests validating regex find support in $replaceAll (feature-gated). |
| src/MongoDB.Driver/Linq/Linq3Implementation/Translators/…/SplitMethodToAggregationExpressionTranslator.cs | Adds regex delimiter support and extends handling of additional string.Split overloads. |
| src/MongoDB.Driver/Linq/Linq3Implementation/Translators/…/ReplaceMethodToAggregationExpressionTranslator.cs | New translator implementing $replaceAll for string/regex replace operations. |
| src/MongoDB.Driver/Linq/Linq3Implementation/Translators/…/MethodCallExpressionToAggregationExpressionTranslator.cs | Routes method calls named Replace to the new replace translator. |
| src/MongoDB.Driver/Linq/Linq3Implementation/SerializerFinders/SerializerFinderVisitMethodCall.cs | Deduces serializers for Replace and extends Split deduction to regex splits. |
| src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/StringMethod.cs | Adds Replace reflection metadata and expands split overload sets (char + string separator overloads). |
| src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/RegexMethod.cs | Adds Replace/Split reflection metadata and overload sets for serializer finding/translation. |
| src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs | Adds AstExpression.ReplaceAll(...) factory method. |
| src/MongoDB.Driver/Core/Misc/Feature.cs | Fixes lookup feature typos and introduces ReplaceAll, ReplaceAllWithRegex, and SplitWithRegex feature flags. |
Comments suppressed due to low confidence (1)
src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/SplitMethodToAggregationExpressionTranslator.cs:73
- For the string separator overloads, a null or empty separator has special meaning in .NET (and MongoDB $split also rejects empty delimiters). Currently separatorString/separatorStrings[0] are used without validating null/empty, which can generate an invalid $split delimiter or change semantics. Please add explicit validation (e.g., reject null/empty with ExpressionNotSupportedException, or implement equivalent semantics if intended).
else if (method.IsOneOf(StringMethod.SplitWithStringOverloads))
{
var separatorsExpression = arguments[0];
var separatorString = separatorsExpression.GetConstantValue<string>(containingExpression: expression);
delimiter = separatorString;
}
else if (method.IsOneOf(StringMethod.SplitWithStringsOverloads))
{
var separatorsExpression = arguments[0];
var separatorStrings = separatorsExpression.GetConstantValue<string[]>(containingExpression: expression);
if (separatorStrings.Length != 1)
{
goto notSupported;
}
delimiter = separatorStrings[0];
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // We must run LinqExpressionPreprocessor on the expression before passing them into SerializerFinder or Translators, | ||
| // both of them are expecting to work with subset of Expressions functionality based on PartialEvaluator and ClrCompatExpressionRewriter output. |
There was a problem hiding this comment.
The comment has several grammar issues (e.g., “passing them”, “both of them are expecting”, “subset of Expressions functionality”). Please rewrite for clarity/grammar so it’s easy to understand why preprocessing is required here.
| // We must run LinqExpressionPreprocessor on the expression before passing them into SerializerFinder or Translators, | |
| // both of them are expecting to work with subset of Expressions functionality based on PartialEvaluator and ClrCompatExpressionRewriter output. | |
| // We must run LinqExpressionPreprocessor on the expression before passing it to SerializerFinder or the translators. | |
| // They expect to work with only the subset of expression functionality produced by PartialEvaluator and ClrCompatExpressionRewriter. |
|
|
||
| [Theory] | ||
| [MemberData(nameof(NonSupportedTestCases))] | ||
| public void SerializerFinder_should_unknowable_serializer__for_unsupported_regex_methods(LambdaExpression expression) |
There was a problem hiding this comment.
Test method name contains a grammatical error and an extra underscore ("should_unknowable_serializer__for..."). Please rename it to something like "SerializerFinder_should_set_unknowable_serializer_for_unsupported_regex_methods" for readability and consistency with the StringTests naming.
| public void SerializerFinder_should_unknowable_serializer__for_unsupported_regex_methods(LambdaExpression expression) | |
| public void SerializerFinder_should_set_unknowable_serializer_for_unsupported_regex_methods(LambdaExpression expression) |
| if (method.Is(StringMethod.ReplaceWithString)) | ||
| { | ||
| inputAst = ExpressionToAggregationExpressionTranslator.Translate(context, expression.Object).Ast; | ||
| findAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]).Ast; | ||
| replacementAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[1]).Ast; | ||
| } |
There was a problem hiding this comment.
For string.Replace(string oldValue, string newValue), .NET treats newValue == null as string.Empty. Translating a null replacement via ExpressionToAggregationExpressionTranslator will emit BSON null, which diverges from .NET semantics and is likely invalid for $replaceAll. Consider normalizing a constant null replacement to "" (or explicitly rejecting null with a clear ExpressionNotSupportedException).
No description provided.