diff --git a/QueryKit.UnitTests/PropertyResolverTests.cs b/QueryKit.UnitTests/PropertyResolverTests.cs index 040943f..e42f206 100644 --- a/QueryKit.UnitTests/PropertyResolverTests.cs +++ b/QueryKit.UnitTests/PropertyResolverTests.cs @@ -675,7 +675,7 @@ public void property_prevented_for_filter_and_sort_is_removed_by_its_member_name } [Fact] - public void alias_replacement_replaces_a_query_name_in_a_nested_path() + public void alias_replacement_does_not_replace_a_query_name_in_a_nested_path() { var config = new QueryKitConfiguration(config => { @@ -684,7 +684,20 @@ public void alias_replacement_replaces_a_query_name_in_a_nested_path() var input = config.PropertyMappings.ReplaceAliasesWithPropertyPaths("""Author.Name == "x" && name == "y" """); - input.Should().Be("""Author.Title == "x" && Title == "y" """); + input.Should().Be("""Author.Name == "x" && Title == "y" """); + } + + [Fact] + public void filter_with_a_query_name_in_a_nested_path_still_throws_for_the_replaced_path() + { + var config = new QueryKitConfiguration(config => + { + config.Property(x => x.Title).HasQueryName("name"); + }); + + var act = () => FilterParser.ParseFilter("""Author.Name == "x" """, config); + + act.Should().ThrowExactly().WithMessage("*'Title'*"); } [Fact] diff --git a/QueryKit/FilterParser.cs b/QueryKit/FilterParser.cs index 07ead9d..4a1c9a5 100644 --- a/QueryKit/FilterParser.cs +++ b/QueryKit/FilterParser.cs @@ -24,7 +24,7 @@ public static Expression> ParseFilter(string input, IQueryKitCo input = config?.ReplaceLogicalAliases(input) ?? input; input = config?.ReplaceComparisonAliases(input) ?? input; - input = config?.PropertyMappings?.ReplaceAliasesWithPropertyPaths(input) ?? input; + input = config?.PropertyMappings?.ReplaceAliasesWithPropertyPaths(input, replaceAfterDot: true) ?? input; var parameter = Expression.Parameter(typeof(T), "x"); Expression expr; diff --git a/QueryKit/QueryKitPropertyMappings.cs b/QueryKit/QueryKitPropertyMappings.cs index 9408114..0d562ab 100644 --- a/QueryKit/QueryKitPropertyMappings.cs +++ b/QueryKit/QueryKitPropertyMappings.cs @@ -124,6 +124,10 @@ protected override Expression VisitParameter(ParameterExpression node) } public string ReplaceAliasesWithPropertyPaths(string input) + => ReplaceAliasesWithPropertyPaths(input, replaceAfterDot: false); + + // The filter parser keeps the v1.14.2 match, which also replaces a query name after a dot. + internal string ReplaceAliasesWithPropertyPaths(string input, bool replaceAfterDot) { var operators = ComparisonOperator.List.Select(x => x.Operator()).ToList(); @@ -136,8 +140,10 @@ public string ReplaceAliasesWithPropertyPaths(string input) { // Use regular expression to isolate left side of the expression. Query names and // operators are matched literally, so escape any regex metacharacters they contain - // (e.g. the `^` in `^^` would otherwise be read as a start-of-line anchor). - var regex = AliasRegexCache.Get($@"\b{Regex.Escape(queryKitPropertyInfo.QueryName!)}\b(?=\s*{Regex.Escape(op)})"); + // (e.g. the `^` in `^^` would otherwise be read as a start-of-line anchor). A query name + // after a dot is a segment of a nested path, so only the parser replaces it. + var afterDot = replaceAfterDot ? "" : @"(?