implement ability to filter int values using string operators

This commit is contained in:
v.chechetkin 2022-11-16 00:51:39 +04:00
parent b73f748dba
commit 22dae2eaf7
2 changed files with 125 additions and 17 deletions

View File

@ -202,13 +202,8 @@ namespace Sieve.Services
if (filterTerm.OperatorIsCaseInsensitive && !isFilterTermValueNull) if (filterTerm.OperatorIsCaseInsensitive && !isFilterTermValueNull)
{ {
propertyValue = Expression.Call(propertyValue, propertyValue = GetStringMethodExpression(propertyValue, "ToUpper");
typeof(string).GetMethods() filterValue = GetStringMethodExpression(filterValue, "ToUpper");
.First(m => m.Name == "ToUpper" && m.GetParameters().Length == 0));
filterValue = Expression.Call(filterValue,
typeof(string).GetMethods()
.First(m => m.Name == "ToUpper" && m.GetParameters().Length == 0));
} }
var expression = GetExpression(filterTerm, filterValue, propertyValue); var expression = GetExpression(filterTerm, filterValue, propertyValue);
@ -333,19 +328,30 @@ namespace Sieve.Services
FilterOperator.LessThan => Expression.LessThan(propertyValue, filterValue), FilterOperator.LessThan => Expression.LessThan(propertyValue, filterValue),
FilterOperator.GreaterThanOrEqualTo => Expression.GreaterThanOrEqual(propertyValue, filterValue), FilterOperator.GreaterThanOrEqualTo => Expression.GreaterThanOrEqual(propertyValue, filterValue),
FilterOperator.LessThanOrEqualTo => Expression.LessThanOrEqual(propertyValue, filterValue), FilterOperator.LessThanOrEqualTo => Expression.LessThanOrEqual(propertyValue, filterValue),
FilterOperator.Contains => Expression.Call(propertyValue, FilterOperator.Contains => GetStringMethodExpression(filterValue, propertyValue, "Contains"),
typeof(string).GetMethods().First(m => m.Name == "Contains" && m.GetParameters().Length == 1), FilterOperator.StartsWith => GetStringMethodExpression(filterValue, propertyValue, "StartsWith"),
filterValue), FilterOperator.EndsWith => GetStringMethodExpression(filterValue, propertyValue, "EndsWith"),
FilterOperator.StartsWith => Expression.Call(propertyValue,
typeof(string).GetMethods().First(m => m.Name == "StartsWith" && m.GetParameters().Length == 1),
filterValue),
FilterOperator.EndsWith => Expression.Call(propertyValue,
typeof(string).GetMethods().First(m => m.Name == "EndsWith" && m.GetParameters().Length == 1),
filterValue),
_ => Expression.Equal(propertyValue, filterValue) _ => Expression.Equal(propertyValue, filterValue)
}; };
} }
private static Expression GetStringMethodExpression(dynamic filterValue, dynamic propertyValue, string methodName) =>
propertyValue.Type == typeof(string) && filterValue.Type == typeof(string)
? Expression.Call(propertyValue, GetStringMethod(methodName, 1), filterValue)
: Expression.Call(CallToString(propertyValue), GetStringMethod(methodName, 1), CallToString(filterValue));
private static Expression GetStringMethodExpression(dynamic propertyValue, string methodName) =>
propertyValue.Type == typeof(string)
? Expression.Call(propertyValue, GetStringMethod(methodName, 0))
: Expression.Call(CallToString(propertyValue), GetStringMethod(methodName, 0));
private static Expression CallToString(dynamic value) =>
Expression.Call(value, typeof(object).GetMethod("ToString"));
private static MethodInfo GetStringMethod(string methodName, int paramCount) =>
typeof(string).GetMethods()
.First(m => m.Name == methodName && m.GetParameters().Length == paramCount);
// Workaround to ensure that the filter value gets passed as a parameter in generated SQL from EF Core // Workaround to ensure that the filter value gets passed as a parameter in generated SQL from EF Core
private static Expression GetClosureOverConstant<T>(T constant, Type targetType) private static Expression GetClosureOverConstant<T>(T constant, Type targetType)
{ {

View File

@ -39,7 +39,7 @@ namespace SieveUnitTests
{ {
Id = 0, Id = 0,
Title = "A", Title = "A",
LikeCount = 100, LikeCount = 500,
IsDraft = true, IsDraft = true,
CategoryId = null, CategoryId = null,
TopComment = new Comment { Id = 0, Text = "A1" }, TopComment = new Comment { Id = 0, Text = "A1" },
@ -254,6 +254,108 @@ namespace SieveUnitTests
Assert.True(result.Count() == 2); Assert.True(result.Count() == 2);
Assert.True(nullableResult.Count() == 3); Assert.True(nullableResult.Count() == 3);
} }
[Theory]
[InlineData(5)]
[InlineData(0)]
public void CanFilterIntsUsingContainsOperator(int likeCount)
{
var model = new SieveModel
{
Filters = $"LikeCount@={likeCount}"
};
var result = _processor.Apply(model, _posts);
Assert.True(result.Count() == 3);
}
[Theory]
[InlineData(5)]
[InlineData(0)]
public void CanFilterIntsUsingCaseInsensitiveContainsOperator(int likeCount)
{
var model = new SieveModel
{
Filters = $"LikeCount@=*{likeCount}"
};
var result = _processor.Apply(model, _posts);
Assert.True(result.Count() == 3);
}
[Theory]
[InlineData(5)]
[InlineData(0)]
public void CanFilterIntsUsingDoesNotContainsOperator(int likeCount)
{
var model = new SieveModel
{
Filters = $"LikeCount!@={likeCount}"
};
var result = _processor.Apply(model, _posts);
Assert.True(result.Count() == 2);
}
[Theory]
[InlineData(5)]
[InlineData(0)]
public void CanFilterIntsUsingCaseInsensitiveDoesNotContainsOperator(int likeCount)
{
var model = new SieveModel
{
Filters = $"LikeCount!@=*{likeCount}"
};
var result = _processor.Apply(model, _posts);
Assert.True(result.Count() == 2);
}
[Theory]
[InlineData(5)]
public void CanFilterIntsUsingCaseInsensitiveDoesNotStartsWithOperator(int likeCount)
{
var model = new SieveModel
{
Filters = $"LikeCount!_=*{likeCount}"
};
var result = _processor.Apply(model, _posts);
Assert.True(result.Count() == 2);
}
[Theory]
[InlineData(5)]
public void CanFilterIntsUsingStartsWithOperator(int likeCount)
{
var model = new SieveModel
{
Filters = $"LikeCount_={likeCount}"
};
var result = _processor.Apply(model, _posts);
Assert.True(result.Count() == 3);
}
[Theory]
[InlineData(0)]
public void CanFilterIntsUsingEndsWithOperator(int likeCount)
{
var model = new SieveModel
{
Filters = $"LikeCount_-={likeCount}"
};
var result = _processor.Apply(model, _posts);
Assert.True(result.Count() == 3);
}
[Theory] [Theory]
[InlineData(@"Text@=*\,")] [InlineData(@"Text@=*\,")]