From 116795e30be2f8fd518a44b8fc02edddde95e728 Mon Sep 17 00:00:00 2001
From: AliReZa Sabouri <7004080+alirezanet@users.noreply.github.com>
Date: Tue, 21 Sep 2021 13:25:52 +0430
Subject: [PATCH] v2.1.0 (#22)
Add support Case-Insensitive search using `/i` operator. #21
---
.github/workflows/publush.yml | 31 ++++++
.../workflows/{dotnet.yml => pullRequest.yml} | 6 +-
README.md | 22 +++-
.../Gridify.EntityFramework.csproj | 4 +-
src/Gridify/Gridify.csproj | 4 +-
src/Gridify/Syntax/Lexer.cs | 6 +-
src/Gridify/Syntax/Parser.cs | 10 +-
src/Gridify/Syntax/SyntaxKind.cs | 3 +-
.../Syntax/SyntaxTreeToQueryConvertor.cs | 79 ++++++++------
src/Gridify/Syntax/ValueExpressionSyntax.cs | 4 +-
.../EntityFramework6Tests.cs | 2 +-
test/Gridify.Tests/GridifyExtensionsShould.cs | 103 +++++++++++++-----
12 files changed, 192 insertions(+), 82 deletions(-)
create mode 100644 .github/workflows/publush.yml
rename .github/workflows/{dotnet.yml => pullRequest.yml} (82%)
diff --git a/.github/workflows/publush.yml b/.github/workflows/publush.yml
new file mode 100644
index 00000000..f8ffda1d
--- /dev/null
+++ b/.github/workflows/publush.yml
@@ -0,0 +1,31 @@
+#https://lukelowrey.com/use-github-actions-to-publish-nuget-packages/
+name: Publish Packages
+
+on:
+ push:
+ branches: [ master ]
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: 5.0.x
+ - name: Restore dependencies
+ run: dotnet restore
+ - name: Build
+ run: dotnet build --configuration Release --no-restore
+ - name: Test
+ run: dotnet test --configuration Release --no-build --verbosity normal
+ - name: Publish Gridify
+ uses: brandedoutcast/publish-nuget@v2.5.2
+ with:
+ PROJECT_FILE_PATH: src/Gridify/Gridify.csproj
+ NUGET_KEY: ${{secrets.NUGET_API_KEY}}
+ - name: Publish Gridify.EntityFramework
+ uses: brandedoutcast/publish-nuget@v2.5.2
+ with:
+ PROJECT_FILE_PATH: src/Gridify.EntityFramework/Gridify.EntityFramework.csproj
+ NUGET_KEY: ${{secrets.NUGET_API_KEY}}
\ No newline at end of file
diff --git a/.github/workflows/dotnet.yml b/.github/workflows/pullRequest.yml
similarity index 82%
rename from .github/workflows/dotnet.yml
rename to .github/workflows/pullRequest.yml
index 47ebafb8..388760b7 100644
--- a/.github/workflows/dotnet.yml
+++ b/.github/workflows/pullRequest.yml
@@ -1,10 +1,8 @@
-name: .NET
+name: Build Pull Requests
on:
- push:
- branches: [ master , version2]
pull_request:
- branches: [ master , version2]
+ branches: [ master ]
jobs:
build:
diff --git a/README.md b/README.md
index 80303145..3dc0d6ee 100644
--- a/README.md
+++ b/README.md
@@ -165,22 +165,32 @@ But for example, if you need to just filter your data without paging or sorting
We can easily create complex queries using parenthesis`()` with AND (`,`) + OR (`|`) operators.
-**Escape character hint**:
+---
+## Case-Insensitive search
+
+The **'/i'** operator can be use after string values for case insensitive search:
+```c#
+var gq = new GridifyQuery() { Filter = "FirstName=John/i" };
+// this is matched by => JOHN - john - John - jOHn - ...
+```
+---
+
+## Escape character
-Filtering has four special character `, | ( )` to handle complex queries. If you want to use these characters in your query values (after `=`), you should add a backslash \
before them.
+Filtering has five special character `, | ( ) /i` to handle complex queries and case-insensitive search. If you want to use these characters in your query values (after operator), you should add a backslash \
before them. having bellow regex could be helpfull `([(),|]|\/i)`.
JavaScript escape example:
```javascript
-let esc = (v) => v.replace(/([(),|])/g, '\\$1')
+let esc = (v) => v.replace(/([(),|]|\/i)/g, '\\$1')
```
Csharp escape example:
```csharp
var value = "(test,test2)";
-var esc = Regex.Replace(value, "([(),|])", "\\$1" ); // esc = \(test\,test2\)
+var esc = Regex.Replace(value, "([(),|]|\/i)", "\\$1" ); // esc = \(test\,test2\)
```
-
+
---
-
+
## Multiple OrderBy
OrderBy accepts comma-separated field names followed by `asc` or `desc` keyword.
by default, if you don't add these keywords,
diff --git a/src/Gridify.EntityFramework/Gridify.EntityFramework.csproj b/src/Gridify.EntityFramework/Gridify.EntityFramework.csproj
index 9b0e3165..a533edaa 100644
--- a/src/Gridify.EntityFramework/Gridify.EntityFramework.csproj
+++ b/src/Gridify.EntityFramework/Gridify.EntityFramework.csproj
@@ -9,11 +9,11 @@
netstandard2.0
Gridify.EntityFramework
- 2.0.0
+ 2.1.0
Alireza Sabouri
TuxTeam
Gridify (EntityFramework), Easy and optimized way to apply Filtering, Sorting, and Pagination using text-based data.
- https://github.com/alirezanet/Gridify/tree/version2
+ https://github.com/alirezanet/Gridify
MIT
true
diff --git a/src/Gridify/Gridify.csproj b/src/Gridify/Gridify.csproj
index 762db467..00092876 100644
--- a/src/Gridify/Gridify.csproj
+++ b/src/Gridify/Gridify.csproj
@@ -3,11 +3,11 @@
netstandard2.0
Gridify
- 2.0.0
+ 2.1.0
Alireza Sabouri
TuxTeam
Gridify, Easy and optimized way to apply Filtering, Sorting, and Pagination using text-based data.
- https://github.com/alirezanet/Gridify/tree/version2
+ https://github.com/alirezanet/Gridify
MIT
latest
true
diff --git a/src/Gridify/Syntax/Lexer.cs b/src/Gridify/Syntax/Lexer.cs
index 7b843cc0..a7259a6a 100644
--- a/src/Gridify/Syntax/Lexer.cs
+++ b/src/Gridify/Syntax/Lexer.cs
@@ -61,6 +61,8 @@ public SyntaxToken NextToken()
return new SyntaxToken(SyntaxKind.NotEqual, _position += 2, "!=");
case '!' when peek == '*':
return new SyntaxToken(SyntaxKind.NotLike, _position += 2, "!*");
+ case '/' when peek == 'i':
+ return new SyntaxToken(SyntaxKind.CaseInsensitive, _position += 2, "/i");
case '<':
return peek == '=' ? new SyntaxToken(SyntaxKind.LessOrEqualThan, _position += 2, "<=") :
new SyntaxToken(SyntaxKind.LessThan, _position++, "<");
@@ -103,7 +105,9 @@ public SyntaxToken NextToken()
var exitCharacters = new[] {'(', ')', ',', '|'};
var lastChar = '\0';
- while ((!exitCharacters.Contains(Current) || exitCharacters.Contains(Current) && lastChar == '\\') && _position < _text.Length)
+ while ((!exitCharacters.Contains(Current) || exitCharacters.Contains(Current) && lastChar == '\\') &&
+ _position < _text.Length &&
+ (!(Current == '/' && Peek(1) == 'i') || (Current == '/' && Peek(1) == 'i') && lastChar == '\\')) // exit on case-insensitive operator
{
lastChar = Current;
Next();
diff --git a/src/Gridify/Syntax/Parser.cs b/src/Gridify/Syntax/Parser.cs
index 8980dc0b..a0451ac6 100644
--- a/src/Gridify/Syntax/Parser.cs
+++ b/src/Gridify/Syntax/Parser.cs
@@ -90,7 +90,8 @@ private ExpressionSyntax ParseFactor()
private ExpressionSyntax ParseValueExpression()
{
var valueToken = Match(SyntaxKind.ValueToken);
- return new ValueExpressionSyntax(valueToken);
+ var isCaseInsensitive = IsMatch(SyntaxKind.CaseInsensitive);
+ return new ValueExpressionSyntax(valueToken, isCaseInsensitive);
}
private SyntaxToken NextToken()
@@ -100,6 +101,13 @@ private SyntaxToken NextToken()
return current;
}
+ private bool IsMatch(SyntaxKind kind)
+ {
+ if (Current.Kind != kind) return false;
+ NextToken();
+ return true;
+ }
+
private SyntaxToken Match(SyntaxKind kind)
{
if (Current.Kind == kind)
diff --git a/src/Gridify/Syntax/SyntaxKind.cs b/src/Gridify/Syntax/SyntaxKind.cs
index 530cb9b8..ad3d09c5 100644
--- a/src/Gridify/Syntax/SyntaxKind.cs
+++ b/src/Gridify/Syntax/SyntaxKind.cs
@@ -30,6 +30,7 @@ public enum SyntaxKind
ValueToken,
ParenthesizedExpression,
NotStartsWith,
- NotEndsWith
+ NotEndsWith,
+ CaseInsensitive
}
}
\ No newline at end of file
diff --git a/src/Gridify/Syntax/SyntaxTreeToQueryConvertor.cs b/src/Gridify/Syntax/SyntaxTreeToQueryConvertor.cs
index 6550d65e..978e7890 100644
--- a/src/Gridify/Syntax/SyntaxTreeToQueryConvertor.cs
+++ b/src/Gridify/Syntax/SyntaxTreeToQueryConvertor.cs
@@ -15,7 +15,7 @@ private static (Expression> Expression, bool IsNested)? ConvertBin
BinaryExpressionSyntax binarySyntax, IGridifyMapper mapper)
{
var left = (binarySyntax.Left as FieldExpressionSyntax)?.FieldToken.Text.Trim();
- var right = (binarySyntax.Right as ValueExpressionSyntax)?.ValueToken.Text;
+ var right = (binarySyntax.Right as ValueExpressionSyntax);
var op = binarySyntax.OperatorToken;
if (left == null || right == null) return null;
@@ -41,7 +41,7 @@ private static (Expression> Expression, bool IsNested)? ConvertBin
private static Expression>? GenerateNestedExpression(
IGridifyMapper mapper,
IGMap gMap,
- string stringValue,
+ ValueExpressionSyntax value,
SyntaxNode op)
{
var body = gMap.To.Body;
@@ -49,7 +49,7 @@ private static (Expression> Expression, bool IsNested)? ConvertBin
if (body is MethodCallExpression selectExp && selectExp.Method.Name == "Select")
{
var targetExp = selectExp.Arguments.Single(a => a.NodeType == ExpressionType.Lambda) as LambdaExpression;
- var conditionExp = GenerateExpression(targetExp!.Body, targetExp.Parameters[0], stringValue, op, mapper.Configuration.AllowNullSearch,
+ var conditionExp = GenerateExpression(targetExp!.Body, targetExp.Parameters[0], value, op, mapper.Configuration.AllowNullSearch,
gMap.Convertor);
if (conditionExp == null) return null;
@@ -68,13 +68,15 @@ private static LambdaExpression ParseMethodCallExpression(MethodCallExpression e
case MemberExpression member:
return GetAnyExpression(member, predicate);
case MethodCallExpression subExp when subExp.Method.Name == "SelectMany" &&
- subExp.Arguments.Last() is LambdaExpression {Body: MemberExpression lambdaMember}:
+ subExp.Arguments.Last() is LambdaExpression { Body: MemberExpression lambdaMember }:
{
var newPredicate = GetAnyExpression(lambdaMember, predicate);
return ParseMethodCallExpression(subExp, newPredicate);
}
case MethodCallExpression subExp when subExp.Method.Name == "Select" && subExp.Arguments.Last() is LambdaExpression
- {Body: MemberExpression lambdaMember} lambda:
+ {
+ Body: MemberExpression lambdaMember
+ } lambda:
{
var newExp = new PredicateBuilder.ReplaceExpressionVisitor(predicate.Parameters[0], lambdaMember).Visit(predicate.Body);
var newPredicate = GetExpressionWithNullCheck(lambdaMember, lambda.Parameters[0], newExp!);
@@ -112,39 +114,45 @@ private static LambdaExpression GetExpressionWithNullCheck(MemberExpression prop
private static LambdaExpression? GenerateExpression(
Expression body,
ParameterExpression parameter,
- string stringValue,
+ ValueExpressionSyntax valueExpression,
SyntaxNode op,
bool allowNullSearch,
Func? convertor)
{
// Remove the boxing for value types
- if (body.NodeType == ExpressionType.Convert) body = ((UnaryExpression) body).Operand;
+ if (body.NodeType == ExpressionType.Convert) body = ((UnaryExpression)body).Operand;
- object? value = stringValue;
+ object? value = valueExpression.ValueToken.Text;
// execute user custom Convertor
if (convertor != null)
- value = convertor.Invoke(stringValue);
+ value = convertor.Invoke(valueExpression.ValueToken.Text) ?? null;
+
+ if (allowNullSearch && op.Kind is SyntaxKind.Equal or SyntaxKind.NotEqual && value?.ToString() == "null")
+ value = null;
- if (value != null && body.Type != value.GetType())
+ // type fixer
+ if (value is not null && body.Type != value.GetType())
+ {
try
{
- if (allowNullSearch && op.Kind is SyntaxKind.Equal or SyntaxKind.NotEqual && value.ToString() == "null")
- value = null;
- else
- {
- // handle broken guids, github issue #2
- if (body.Type == typeof(Guid) && !Guid.TryParse(value.ToString(), out _)) value = Guid.NewGuid().ToString();
+ // handle broken guids, github issue #2
+ if (body.Type == typeof(Guid) && !Guid.TryParse(value.ToString(), out _)) value = Guid.NewGuid().ToString();
- var converter = TypeDescriptor.GetConverter(body.Type);
- value = converter.ConvertFromString(value.ToString())!;
- }
+ var converter = TypeDescriptor.GetConverter(body.Type);
+ value = converter.ConvertFromString(value.ToString())!;
}
catch (FormatException)
{
// return no records in case of any exception in formatting
return Expression.Lambda(Expression.Constant(false), parameter); // q => false
}
+ }
+ else if (valueExpression.IsCaseInsensitive)
+ {
+ value = value?.ToString().ToLower();
+ body = Expression.Call(body, GetToLowerMethod());
+ }
Expression be;
@@ -224,16 +232,18 @@ private static LambdaExpression GetExpressionWithNullCheck(MemberExpression prop
private static MethodInfo GetAnyMethod(Type @type) =>
typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2).MakeGenericMethod(@type);
- private static MethodInfo GetEndsWithMethod() => typeof(string).GetMethod("EndsWith", new[] {typeof(string)})!;
+ private static MethodInfo GetEndsWithMethod() => typeof(string).GetMethod("EndsWith", new[] { typeof(string) })!;
- private static MethodInfo GetStartWithMethod() => typeof(string).GetMethod("StartsWith", new[] {typeof(string)})!;
+ private static MethodInfo GetStartWithMethod() => typeof(string).GetMethod("StartsWith", new[] { typeof(string) })!;
- private static MethodInfo GetContainsMethod() => typeof(string).GetMethod("Contains", new[] {typeof(string)})!;
+ private static MethodInfo GetContainsMethod() => typeof(string).GetMethod("Contains", new[] { typeof(string) })!;
+ private static MethodInfo GetToLowerMethod() => typeof(string).GetMethod("ToLower", new Type[] { })!;
private static MethodInfo GetToStringMethod() => typeof(object).GetMethod("ToString")!;
+
internal static (Expression> Expression, bool IsNested)
- GenerateQuery(ExpressionSyntax expression, IGridifyMapper mapper,bool isParenthesisOpen=false)
+ GenerateQuery(ExpressionSyntax expression, IGridifyMapper mapper, bool isParenthesisOpen = false)
{
while (true)
switch (expression.Kind)
@@ -245,9 +255,9 @@ internal static (Expression> Expression, bool IsNested)
if (bExp!.Left is FieldExpressionSyntax && bExp.Right is ValueExpressionSyntax)
return ConvertBinaryExpressionSyntaxToQuery(bExp, mapper) ?? throw new GridifyFilteringException("Invalid expression");
- (Expression> exp,bool isNested) leftQuery;
- (Expression> exp,bool isNested) rightQuery;
-
+ (Expression> exp, bool isNested) leftQuery;
+ (Expression> exp, bool isNested) rightQuery;
+
if (bExp.Left is ParenthesizedExpressionSyntax lpExp)
{
leftQuery = GenerateQuery(lpExp.Expression, mapper, true);
@@ -257,14 +267,14 @@ internal static (Expression> Expression, bool IsNested)
if (bExp.Right is ParenthesizedExpressionSyntax rpExp)
- rightQuery = GenerateQuery(rpExp.Expression, mapper,true);
+ rightQuery = GenerateQuery(rpExp.Expression, mapper, true);
else
rightQuery = GenerateQuery(bExp.Right, mapper);
// check for nested collections
if (isParenthesisOpen &&
- CheckIfCanMerge(leftQuery, rightQuery,bExp.OperatorToken.Kind) is Expression> mergedResult)
- return (mergedResult,true);
+ CheckIfCanMerge(leftQuery, rightQuery, bExp.OperatorToken.Kind) is Expression> mergedResult)
+ return (mergedResult, true);
var result = bExp.OperatorToken.Kind switch
{
@@ -285,7 +295,7 @@ internal static (Expression> Expression, bool IsNested)
}
private static LambdaExpression? CheckIfCanMerge((Expression> exp, bool isNested) leftQuery,
- (Expression> exp, bool isNested) rightQuery,SyntaxKind op)
+ (Expression> exp, bool isNested) rightQuery, SyntaxKind op)
{
if (leftQuery.isNested && rightQuery.isNested)
{
@@ -303,7 +313,7 @@ internal static (Expression> Expression, bool IsNested)
if (leftLambda is null || rightLambda is null)
return null;
- var visitedRight= new PredicateBuilder.ReplaceExpressionVisitor(rightLambda.Parameters[0], leftLambda.Parameters[0])
+ var visitedRight = new PredicateBuilder.ReplaceExpressionVisitor(rightLambda.Parameters[0], leftLambda.Parameters[0])
.Visit(rightLambda.Body);
var mergedExpression = op switch
@@ -312,12 +322,13 @@ internal static (Expression> Expression, bool IsNested)
SyntaxKind.Or => Expression.OrElse(leftLambda.Body, visitedRight),
_ => throw new InvalidOperationException()
};
-
+
var mergedLambda = Expression.Lambda(mergedExpression, leftLambda.Parameters);
var newLambda = GetAnyExpression(leftMember, mergedLambda) as Expression>;
return newLambda;
}
}
+
return null;
}
@@ -325,12 +336,10 @@ private static MethodCallExpression ParseNestedExpression(Expression exp)
{
return exp switch
{
- BinaryExpression {Right: MethodCallExpression cExp} => cExp,
+ BinaryExpression { Right: MethodCallExpression cExp } => cExp,
MethodCallExpression mcExp => mcExp,
_ => throw new InvalidExpressionException()
};
}
-
-
}
}
\ No newline at end of file
diff --git a/src/Gridify/Syntax/ValueExpressionSyntax.cs b/src/Gridify/Syntax/ValueExpressionSyntax.cs
index 709a9887..9abaf3a7 100644
--- a/src/Gridify/Syntax/ValueExpressionSyntax.cs
+++ b/src/Gridify/Syntax/ValueExpressionSyntax.cs
@@ -4,9 +4,10 @@ namespace Gridify.Syntax
{
internal sealed class ValueExpressionSyntax : ExpressionSyntax
{
- public ValueExpressionSyntax(SyntaxToken valueToken)
+ public ValueExpressionSyntax(SyntaxToken valueToken, bool isCaseInsensitive)
{
ValueToken = valueToken;
+ IsCaseInsensitive = isCaseInsensitive;
}
public override SyntaxKind Kind => SyntaxKind.ValueExpression;
@@ -17,5 +18,6 @@ public override IEnumerable GetChildren()
}
public SyntaxToken ValueToken { get; }
+ public bool IsCaseInsensitive { get; }
}
}
\ No newline at end of file
diff --git a/test/EntityFramework6IntegrationTests.cs/EntityFramework6Tests.cs b/test/EntityFramework6IntegrationTests.cs/EntityFramework6Tests.cs
index ad350290..bc68d9ce 100644
--- a/test/EntityFramework6IntegrationTests.cs/EntityFramework6Tests.cs
+++ b/test/EntityFramework6IntegrationTests.cs/EntityFramework6Tests.cs
@@ -55,7 +55,7 @@ public void EntityFramework6_FilteringAndOrdering() // Issue #8
using (var context = new EntityContext(connection))
{
var gm = new GridifyQuery() { OrderBy = "name desc" , Filter = "id > 3"};
- var expected = context.Customers.ApplyFilterAndOrdering(gm).ToList();
+ var expected = context.Customers.ApplyFilteringAndOrdering(gm).ToList();
var actual = context.Customers
.Where(q=>q.Id > 3)
.OrderByDescending(q => q.Name)
diff --git a/test/Gridify.Tests/GridifyExtensionsShould.cs b/test/Gridify.Tests/GridifyExtensionsShould.cs
index b7ab8136..27da189f 100644
--- a/test/Gridify.Tests/GridifyExtensionsShould.cs
+++ b/test/Gridify.Tests/GridifyExtensionsShould.cs
@@ -34,7 +34,7 @@ public void ApplyFiltering_SingleField()
[Fact]
public void ApplyFiltering_SingleField_GridifyQuery()
{
- var gq = new GridifyQuery { Filter = "name=John" };
+ var gq = new GridifyQuery {Filter = "name=John"};
var actual = _fakeRepository.AsQueryable()
.ApplyFiltering(gq)
.ToList();
@@ -62,6 +62,7 @@ public void ApplyFiltering_NullHandlingUsingCustomConvertor()
Assert.Equal(expected, actual);
Assert.True(actual.Any());
}
+
[Fact]
public void ApplyFiltering_NullHandlingUsingMapper()
{
@@ -78,7 +79,7 @@ public void ApplyFiltering_NullHandlingUsingMapper()
Assert.Equal(expected, actual);
Assert.True(actual.Any());
}
-
+
[Fact]
public void ApplyFiltering_DisableNullHandlingUsingMapper()
{
@@ -116,7 +117,7 @@ public void ApplyFiltering_DuplicateFiledName()
[InlineData(@" name =LI \| AM", @"LI | AM")]
public void ApplyFiltering_EscapeSpecialCharacters(string textFilter, string rawText)
{
- var gq = new GridifyQuery { Filter = textFilter };
+ var gq = new GridifyQuery {Filter = textFilter};
var actual = _fakeRepository.AsQueryable()
.ApplyFiltering(gq)
.ToList();
@@ -129,7 +130,7 @@ public void ApplyFiltering_EscapeSpecialCharacters(string textFilter, string raw
[Fact]
public void ApplyFiltering_ParenthesisQueryWithoutEscapeShouldThrowException()
{
- var gq = new GridifyQuery { Filter = @"name=(LI,AM)" };
+ var gq = new GridifyQuery {Filter = @"name=(LI,AM)"};
Action act = () => _fakeRepository.AsQueryable()
.ApplyFiltering(gq);
@@ -142,7 +143,7 @@ public void ApplyFiltering_SingleGuidField()
{
var guidString = "e2cec5dd-208d-4bb5-a852-50008f8ba366";
var guid = Guid.Parse(guidString);
- var gq = new GridifyQuery { Filter = "myGuid=" + guidString };
+ var gq = new GridifyQuery {Filter = "myGuid=" + guidString};
var actual = _fakeRepository.AsQueryable()
.ApplyFiltering(gq)
.ToList();
@@ -156,7 +157,7 @@ public void ApplyFiltering_SingleGuidField()
public void ApplyFiltering_SingleBrokenGuidField()
{
var brokenGuidString = "e2cec5dd-208d-4bb5-a852-";
- var gq = new GridifyQuery { Filter = "myGuid=" + brokenGuidString };
+ var gq = new GridifyQuery {Filter = "myGuid=" + brokenGuidString};
var actual = _fakeRepository.AsQueryable()
.ApplyFiltering(gq)
@@ -170,7 +171,7 @@ public void ApplyFiltering_SingleBrokenGuidField()
public void ApplyFiltering_SingleBrokenGuidField_NotEqual()
{
var brokenGuidString = "e2cec5dd-208d-4bb5-a852-";
- var gq = new GridifyQuery { Filter = "myGuid!=" + brokenGuidString };
+ var gq = new GridifyQuery {Filter = "myGuid!=" + brokenGuidString};
var actual = _fakeRepository.AsQueryable()
.ApplyFiltering(gq)
@@ -183,7 +184,7 @@ public void ApplyFiltering_SingleBrokenGuidField_NotEqual()
[Fact]
public void ApplyFiltering_InvalidFilterExpressionShouldThrowException()
{
- var gq = new GridifyQuery { Filter = "=guid,d=" };
+ var gq = new GridifyQuery {Filter = "=guid,d="};
Assert.Throws(() =>
_fakeRepository.AsQueryable().ApplyFiltering(gq).ToList());
}
@@ -191,7 +192,7 @@ public void ApplyFiltering_InvalidFilterExpressionShouldThrowException()
[Fact]
public void ApplyFiltering_InvalidCharacterShouldThrowException()
{
- var gq = new GridifyQuery { Filter = "@name=ali" };
+ var gq = new GridifyQuery {Filter = "@name=ali"};
Assert.Throws(() =>
_fakeRepository.AsQueryable().ApplyFiltering(gq).ToList());
}
@@ -359,7 +360,7 @@ public void ApplyFiltering_ComplexWithParenthesis()
public void ApplyFiltering_NestedParenthesisWithSpace()
{
// we shouldn't add spaces for values
- var gq = new GridifyQuery { Filter = " ( name =*J| ( name =*S , Id <5 ) )" };
+ var gq = new GridifyQuery {Filter = " ( name =*J| ( name =*S , Id <5 ) )"};
var actual = _fakeRepository.AsQueryable()
.ApplyFiltering(gq)
.ToList();
@@ -372,7 +373,7 @@ public void ApplyFiltering_NestedParenthesisWithSpace()
[Fact]
public void ApplyFiltering_UsingChildClassProperty()
{
- var gq = new GridifyQuery { Filter = "Child_Name=Bob" };
+ var gq = new GridifyQuery {Filter = "Child_Name=Bob"};
var gm = new GridifyMapper()
.GenerateMappings()
.AddMap("Child_name", q => q.ChildClass!.Name);
@@ -382,7 +383,52 @@ public void ApplyFiltering_UsingChildClassProperty()
.ApplyFiltering(gq, gm)
.ToList();
- var expected = _fakeRepository.Where(q => q.ChildClass is { Name: "Bob" }).ToList();
+ var expected = _fakeRepository.Where(q => q.ChildClass is {Name: "Bob"}).ToList();
+ Assert.Equal(expected.Count, actual.Count);
+ Assert.Equal(expected, actual);
+ Assert.True(actual.Any());
+ }
+
+ [Fact]
+ public void ApplyFiltering_CaseInsensitiveSearchUsingConvertor() //issue #21
+ {
+ var gq = new GridifyQuery { Filter = "name=BOB" };
+ var gm = new GridifyMapper()
+ .AddMap("name", q => q.Name.ToLower() , c => c.ToLower());
+
+ var actual = _fakeRepository.AsQueryable()
+ .ApplyFiltering(gq, gm)
+ .ToList();
+
+ var expected = _fakeRepository.Where(q => q.Name.ToLower() == "BOB".ToLower()).ToList();
+ Assert.Equal(expected.Count, actual.Count);
+ Assert.Equal(expected, actual);
+ Assert.True(actual.Any());
+ }
+
+ [Fact]
+ public void ApplyFiltering_CaseInsensitiveSearch() //issue #21
+ {
+ var gq = new GridifyQuery { Filter = "name=BOB/i " };
+
+ var actual = _fakeRepository.AsQueryable()
+ .ApplyFiltering(gq)
+ .ToList();
+
+ var expected = _fakeRepository.Where(q => q.Name.ToLower() == "BOB".ToLower()).ToList();
+ Assert.Equal(expected.Count, actual.Count);
+ Assert.Equal(expected, actual);
+ Assert.True(actual.Any());
+ }
+
+ [Fact]
+ public void ApplyFiltering_EscapeCaseInsensitiveSearch() //issue #21
+ {
+ var actual = _fakeRepository.AsQueryable()
+ .ApplyFiltering(@"name=Case\/i" )
+ .ToList();
+
+ var expected = _fakeRepository.Where(q => q.Name == "Case/i").ToList();
Assert.Equal(expected.Count, actual.Count);
Assert.Equal(expected, actual);
Assert.True(actual.Any());
@@ -395,7 +441,7 @@ public void ApplyFiltering_UsingChildClassProperty()
[Fact]
public void ApplyOrdering_OrderBy_Ascending()
{
- var gq = new GridifyQuery { OrderBy = "name" };
+ var gq = new GridifyQuery {OrderBy = "name"};
var actual = _fakeRepository.AsQueryable()
.ApplyOrdering(gq)
.ToList();
@@ -406,7 +452,7 @@ public void ApplyOrdering_OrderBy_Ascending()
[Fact]
public void ApplyOrdering_OrderBy_DateTime()
{
- var gq = new GridifyQuery { OrderBy = "MyDateTime" };
+ var gq = new GridifyQuery {OrderBy = "MyDateTime"};
var actual = _fakeRepository.AsQueryable()
.ApplyOrdering(gq)
.ToList();
@@ -419,7 +465,7 @@ public void ApplyOrdering_OrderBy_DateTime()
[Fact]
public void ApplyOrdering_OrderBy_Descending()
{
- var gq = new GridifyQuery { OrderBy = "Name desc" };
+ var gq = new GridifyQuery {OrderBy = "Name desc"};
var actual = _fakeRepository.AsQueryable()
.ApplyOrdering(gq)
.ToList();
@@ -432,7 +478,7 @@ public void ApplyOrdering_OrderBy_Descending()
[Fact]
public void ApplyOrdering_MultipleOrderBy()
{
- var gq = new GridifyQuery { OrderBy = "MyDateTime desc , id , name asc" };
+ var gq = new GridifyQuery {OrderBy = "MyDateTime desc , id , name asc"};
var actual = _fakeRepository.AsQueryable()
.ApplyOrdering(gq)
.ToList();
@@ -451,7 +497,7 @@ public void ApplyOrdering_MultipleOrderBy()
[Fact]
public void ApplyOrdering_SortUsingChildClassProperty()
{
- var gq = new GridifyQuery { OrderBy = "Child_Name desc" };
+ var gq = new GridifyQuery {OrderBy = "Child_Name desc"};
var gm = new GridifyMapper()
.GenerateMappings()
.AddMap("Child_Name", q => q.ChildClass!.Name);
@@ -517,7 +563,7 @@ public void ApplyPaging_UsingDefaultValues()
[InlineData(20, 10)]
public void ApplyPaging_UsingCustomValues(short page, int pageSize)
{
- var gq = new GridifyQuery { Page = page, PageSize = pageSize };
+ var gq = new GridifyQuery {Page = page, PageSize = pageSize};
var actual = _fakeRepository.AsQueryable()
.ApplyPaging(gq)
.ToList();
@@ -548,7 +594,7 @@ public void Gridify_ActionOverload()
var query = _fakeRepository.AsQueryable().Where(q => q.Name == "John").OrderByDescending(q => q.Name);
var totalItems = query.Count();
var items = query.Skip(-2).Take(15).ToList();
- var expected = new Paging() { Data = items, Count = totalItems };
+ var expected = new Paging() {Data = items, Count = totalItems};
Assert.Equal(expected.Count, actual.Count);
Assert.Equal(expected.Data.Count(), actual.Data.Count());
@@ -567,7 +613,7 @@ public void Gridify_ActionOverload()
public void ApplyOrderingAndPaging_UsingCustomValues(short page, int pageSize, bool isSortAsc)
{
var orderByExp = "name " + (isSortAsc ? "asc" : "desc");
- var gq = new GridifyQuery { Page = page, PageSize = pageSize, OrderBy = orderByExp };
+ var gq = new GridifyQuery {Page = page, PageSize = pageSize, OrderBy = orderByExp};
// actual
var actual = _fakeRepository.AsQueryable()
.ApplyOrderingAndPaging(gq)
@@ -596,21 +642,21 @@ private static IEnumerable GetSampleData()
var lst = new List();
lst.Add(new TestClass(1, "John", null, Guid.NewGuid(), DateTime.Now));
lst.Add(new TestClass(2, "Bob", null, Guid.NewGuid(), DateTime.UtcNow));
- lst.Add(new TestClass(3, "Jack", (TestClass)lst[0].Clone(), Guid.Empty, DateTime.Now.AddDays(2)));
+ lst.Add(new TestClass(3, "Jack", (TestClass) lst[0].Clone(), Guid.Empty, DateTime.Now.AddDays(2)));
lst.Add(new TestClass(4, "Rose", null, Guid.Parse("e2cec5dd-208d-4bb5-a852-50008f8ba366")));
lst.Add(new TestClass(5, "Ali", null));
- lst.Add(new TestClass(6, "Hamid", (TestClass)lst[0].Clone(), Guid.Parse("de12bae1-93fa-40e4-92d1-2e60f95b468c")));
- lst.Add(new TestClass(7, "Hasan", (TestClass)lst[1].Clone()));
- lst.Add(new TestClass(8, "Farhad", (TestClass)lst[2].Clone(), Guid.Empty));
+ lst.Add(new TestClass(6, "Hamid", (TestClass) lst[0].Clone(), Guid.Parse("de12bae1-93fa-40e4-92d1-2e60f95b468c")));
+ lst.Add(new TestClass(7, "Hasan", (TestClass) lst[1].Clone()));
+ lst.Add(new TestClass(8, "Farhad", (TestClass) lst[2].Clone(), Guid.Empty));
lst.Add(new TestClass(9, "Sara", null));
lst.Add(new TestClass(10, "Jorge", null));
lst.Add(new TestClass(11, "joe", null));
- lst.Add(new TestClass(12, "jimmy", (TestClass)lst[0].Clone()));
+ lst.Add(new TestClass(12, "jimmy", (TestClass) lst[0].Clone()));
lst.Add(new TestClass(13, "Nazanin", null));
lst.Add(new TestClass(14, "Reza", null));
- lst.Add(new TestClass(15, "Korosh", (TestClass)lst[0].Clone()));
- lst.Add(new TestClass(16, "Kamran", (TestClass)lst[1].Clone()));
- lst.Add(new TestClass(17, "Saeid", (TestClass)lst[2].Clone()));
+ lst.Add(new TestClass(15, "Korosh", (TestClass) lst[0].Clone()));
+ lst.Add(new TestClass(16, "Kamran", (TestClass) lst[1].Clone()));
+ lst.Add(new TestClass(17, "Saeid", (TestClass) lst[2].Clone()));
lst.Add(new TestClass(18, "jessi=ca", null));
lst.Add(new TestClass(19, "Ped=ram", null));
lst.Add(new TestClass(20, "Peyman!", null));
@@ -619,6 +665,7 @@ private static IEnumerable GetSampleData()
lst.Add(new TestClass(22, @"\Liam", null));
lst.Add(new TestClass(23, "LI | AM", null));
lst.Add(new TestClass(24, "(LI,AM)", null));
+ lst.Add(new TestClass(25, "Case/i", null));
return lst;
}