Skip to content

Commit

Permalink
Merge pull request #48 from mehmetuken/develop
Browse files Browse the repository at this point in the history
Adding IsNull, IsNotNull, IsEmpty, IsNotEmpty filters.
  • Loading branch information
enisn authored Jan 28, 2022
2 parents e47ce55 + ed9005f commit ce8e5a5
Show file tree
Hide file tree
Showing 8 changed files with 321 additions and 4 deletions.
17 changes: 14 additions & 3 deletions src/AutoFilterer/Attributes/OperatorComparisonAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@ public OperatorComparisonAttribute(OperatorType operatorType)

public OperatorType OperatorType { get; }

public override Expression BuildExpression(Expression expressionBody, PropertyInfo targetProperty, PropertyInfo filterProperty, object value)
public override Expression BuildExpression(Expression expressionBody, PropertyInfo targetProperty,
PropertyInfo filterProperty, object value)
{
var prop = Expression.Property(expressionBody, targetProperty.Name);
var param = Expression.Constant(value);
var targetIsNullable = targetProperty.PropertyType.IsNullable() || targetProperty.PropertyType == typeof(string);

if (targetProperty.PropertyType.IsNullable())
prop = Expression.Property(prop, nameof(Nullable<bool>.Value));

switch (OperatorType)
{
case OperatorType.Equal:
Expand All @@ -40,6 +42,10 @@ public override Expression BuildExpression(Expression expressionBody, PropertyIn
return Expression.LessThan(prop, param);
case OperatorType.LessThanOrEqual:
return Expression.LessThanOrEqual(prop, param);
case OperatorType.IsNull:
return targetIsNullable ? Expression.Equal(Expression.Property(expressionBody, targetProperty.Name), Expression.Constant(null)) : null;
case OperatorType.IsNotNull:
return targetIsNullable ? Expression.Not(Expression.Equal(Expression.Property(expressionBody, targetProperty.Name), Expression.Constant(null))) : null;
}

return Expression.Empty();
Expand All @@ -53,6 +59,8 @@ public override Expression BuildExpression(Expression expressionBody, PropertyIn
public static OperatorComparisonAttribute GreaterThanOrEqual { get; }
public static OperatorComparisonAttribute LessThan { get; }
public static OperatorComparisonAttribute LessThanOrEqual { get; }
public static OperatorComparisonAttribute IsNull { get; }
public static OperatorComparisonAttribute IsNotNull { get; }

static OperatorComparisonAttribute()
{
Expand All @@ -62,6 +70,9 @@ static OperatorComparisonAttribute()
GreaterThanOrEqual = new OperatorComparisonAttribute(OperatorType.GreaterThanOrEqual);
LessThan = new OperatorComparisonAttribute(OperatorType.LessThan);
LessThanOrEqual = new OperatorComparisonAttribute(OperatorType.LessThanOrEqual);
IsNull = new OperatorComparisonAttribute(OperatorType.IsNull);
IsNotNull = new OperatorComparisonAttribute(OperatorType.IsNotNull);
}

#endregion
}
}
2 changes: 2 additions & 0 deletions src/AutoFilterer/Enums/OperatorType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ public enum OperatorType
GreaterThanOrEqual,
LessThan,
LessThanOrEqual,
IsNull,
IsNotNull,
}
26 changes: 26 additions & 0 deletions src/AutoFilterer/Types/OperatorFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public class OperatorFilter<T> : IFilterableType
public virtual T? Lt { get; set; }
public virtual T? Gte { get; set; }
public virtual T? Lte { get; set; }
public virtual bool? IsNull { get; set; }
public virtual bool? IsNotNull { get; set; }

public virtual CombineType CombineWith { get; set; }
public virtual Expression BuildExpression(Expression expressionBody, PropertyInfo targetProperty, PropertyInfo filterProperty, object value)
Expand All @@ -42,6 +44,30 @@ public virtual Expression BuildExpression(Expression expressionBody, PropertyInf
if (Not != null)
expression = expression.Combine(OperatorComparisonAttribute.NotEqual.BuildExpression(expressionBody, targetProperty, filterProperty, Not), CombineWith);

if (IsNull != null)
{
if (IsNull.Value)
{
expression = expression.Combine(OperatorComparisonAttribute.IsNull.BuildExpression(expressionBody, targetProperty, filterProperty, IsNull.Value), CombineWith);
}
else
{
expression = expression.Combine(OperatorComparisonAttribute.IsNotNull.BuildExpression(expressionBody, targetProperty, filterProperty, IsNull.Value), CombineWith);
}
}

if (IsNotNull != null)
{
if (IsNotNull.Value)
{
expression = expression.Combine(OperatorComparisonAttribute.IsNotNull.BuildExpression(expressionBody, targetProperty, filterProperty, IsNotNull.Value), CombineWith);
}
else
{
expression = expression.Combine(OperatorComparisonAttribute.IsNull.BuildExpression(expressionBody, targetProperty, filterProperty, IsNotNull.Value), CombineWith);
}
}

return expression;
}
}
51 changes: 51 additions & 0 deletions src/AutoFilterer/Types/StringFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,26 @@ public class StringFilter : IFilterableType
/// Provides parameter to !String.EndsWith method query.
/// </summary>
public virtual string NotEndsWith { get; set; }

/// <summary>
/// Provides parameter to check is null.
/// </summary>
public virtual bool? IsNull { get; set; }

/// <summary>
/// Provides parameter to check is not null.
/// </summary>
public virtual bool? IsNotNull { get; set; }

/// <summary>
/// Provides parameter to check is empty string.
/// </summary>
public virtual bool? IsEmpty { get; set; }

/// <summary>
/// Provides parameter to check is not empty string.
/// </summary>
public virtual bool? IsNotEmpty { get; set; }

/// <summary>
/// <inheritdoc />
Expand All @@ -77,6 +97,12 @@ public virtual Expression BuildExpression(Expression expressionBody, PropertyInf
if (Not != null)
expression = expression.Combine(OperatorComparisonAttribute.NotEqual.BuildExpression(expressionBody, targetProperty, filterProperty, Not), CombineWith);

if (IsNull != null)
expression = expression.Combine(OperatorComparisonAttribute.IsNull.BuildExpression(expressionBody, targetProperty, filterProperty, IsNull), CombineWith);

if (IsNotNull != null)
expression = expression.Combine(OperatorComparisonAttribute.IsNotNull.BuildExpression(expressionBody, targetProperty, filterProperty, IsNotNull), CombineWith);

if (Equals != null)
expression = expression.Combine(new StringFilterOptionsAttribute(StringFilterOption.Equals) { Comparison = Compare }.BuildExpression(expressionBody, targetProperty, filterProperty, Equals), CombineWith);

Expand All @@ -98,6 +124,31 @@ public virtual Expression BuildExpression(Expression expressionBody, PropertyInf
if (NotEndsWith != null)
expression = expression.Combine(Expression.Not(new StringFilterOptionsAttribute(StringFilterOption.EndsWith) { Comparison = Compare }.BuildExpression(expressionBody, targetProperty, filterProperty, NotEndsWith)), CombineWith);


if (IsEmpty != null)
{
if (IsEmpty.Value)
{
expression = expression.Combine(new StringFilterOptionsAttribute(StringFilterOption.Equals) { Comparison = Compare }.BuildExpression(expressionBody, targetProperty, filterProperty, ""), CombineWith);
}
else
{
expression = expression.Combine(Expression.Not(new StringFilterOptionsAttribute(StringFilterOption.Equals) { Comparison = Compare }.BuildExpression(expressionBody, targetProperty, filterProperty, "")), CombineWith);
}
}

if (IsNotEmpty != null)
{
if (IsNotEmpty.Value)
{
expression = expression.Combine(Expression.Not(new StringFilterOptionsAttribute(StringFilterOption.Equals) { Comparison = Compare }.BuildExpression(expressionBody, targetProperty, filterProperty, "")), CombineWith);
}
else
{
expression = expression.Combine(new StringFilterOptionsAttribute(StringFilterOption.Equals) { Comparison = Compare }.BuildExpression(expressionBody, targetProperty, filterProperty, ""), CombineWith);
}
}

return expression;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using AutoFilterer.Types;

namespace AutoFilterer.Tests.Environment.Dtos;

public class BookFilter_OperatorFilter_Views : FilterBase
{
public OperatorFilter<int> Views { get; set; }
}
17 changes: 17 additions & 0 deletions tests/AutoFilterer.Tests/PerformanceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public void BuildExpression_TotalPageWithAnd_SholdMatchCount(List<Book> data, Bo
{
// Arrange
filter.CombineWith = CombineType.And;

Stopwatch sw = new Stopwatch();

// Act
Expand Down Expand Up @@ -60,6 +61,22 @@ IQueryable<Book> GetAndQuery(IQueryable<Book> query, BookFilter_OperatorFilter_T

if (f.TotalPage.Lte != null)
query = query.Where(x => x.TotalPage <= f.TotalPage.Lt);

if (f.TotalPage.IsNull != null)
{
if (f.TotalPage.IsNull.Value)
query = query.Where(x => x.TotalPage == null);
else
query = query.Where(x => x.TotalPage != null);
}

if (f.TotalPage.IsNotNull != null)
{
if (f.TotalPage.IsNotNull.Value)
query = query.Where(x => x.TotalPage != null);
else
query = query.Where(x => x.TotalPage == null);
}

return query;
}
Expand Down
80 changes: 79 additions & 1 deletion tests/AutoFilterer.Tests/Types/OperatorQueryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,22 @@ IQueryable<Book> GetAndQuery(IQueryable<Book> query, BookFilter_OperatorFilter_T
if (f.TotalPage.Lte!= null)
query = query.Where(x => x.TotalPage <= f.TotalPage.Lt);

if (f.TotalPage.IsNull != null)
{
if (f.TotalPage.IsNull.Value)
query = query.Where(x => x.TotalPage == null);
else
query = query.Where(x => x.TotalPage != null);
}

if (f.TotalPage.IsNotNull != null)
{
if (f.TotalPage.IsNotNull.Value)
query = query.Where(x => x.TotalPage != null);
else
query = query.Where(x => x.TotalPage == null);
}

return query;
}
}
Expand Down Expand Up @@ -260,7 +276,7 @@ public void BuildExpression_TotalPageLtGtWithOr_ShouldMatchCount(List<Book> data
foreach (var item in actualResult)
Assert.Contains(item, result);
}

[Theory, AutoMoqData(count: 64)]
public void BuildExpression_TotalPageLtEqWithOr_ShouldMatchCount(List<Book> data, int max, int exact)
{
Expand All @@ -286,5 +302,67 @@ public void BuildExpression_TotalPageLtEqWithOr_ShouldMatchCount(List<Book> data
foreach (var item in actualResult)
Assert.Contains(item, result);
}

[Theory, AutoMoqData(count: 64)]
public void BuildExpression_ViewsIsNull_ShouldMatchCount(List<Book> data)
{
for (var i = 0; i < 5; i++)
{
data[i].Views = null;
}

// Arrange
var filter = new BookFilter_OperatorFilter_Views
{
Views = new OperatorFilter<int>
{
IsNull = true
}
};

// Act
var query = data.AsQueryable().ApplyFilter(filter);
var result = query.ToList();

// Assert
var actualResult = data.AsQueryable().Where(x => x.Views == null).ToList();

Assert.Equal(actualResult.Count, result.Count);
Assert.Equal(5, actualResult.Count);
Assert.Equal(5, result.Count);
foreach (var item in actualResult)
Assert.Contains(item, result);
}

[Theory, AutoMoqData(count: 64)]
public void BuildExpression_ViewsIsNotNull_ShouldMatchCount(List<Book> data)
{
for (var i = 0; i < 5; i++)
{
data[i].Views = null;
}

// Arrange
var filter = new BookFilter_OperatorFilter_Views
{
Views = new OperatorFilter<int>
{
IsNotNull = true
}
};

// Act
var query = data.AsQueryable().ApplyFilter(filter);
var result = query.ToList();

// Assert
var actualResult = data.AsQueryable().Where(x => x.Views != null).ToList();

Assert.Equal(actualResult.Count, result.Count);
Assert.Equal(64 - 5, actualResult.Count);
Assert.Equal(64 - 5, result.Count);
foreach (var item in actualResult)
Assert.Contains(item, result);
}
}
}
Loading

0 comments on commit ce8e5a5

Please sign in to comment.