diff --git a/Sieve/Models/FilterTerm.cs b/Sieve/Models/FilterTerm.cs index ef2b71e..69395ed 100644 --- a/Sieve/Models/FilterTerm.cs +++ b/Sieve/Models/FilterTerm.cs @@ -11,6 +11,10 @@ namespace Sieve.Models private const string EscapedPipePattern = @"(? 1 ? Regex.Split(filterSplits[1], EscapedPipePattern).Select(t => t.Trim()).ToArray() : null; Operator = Array.Find(Operators, o => value.Contains(o)) ?? "=="; OperatorParsed = GetOperatorParsed(Operator); - OperatorIsCaseInsensitive = Operator.Contains("*"); + OperatorIsCaseInsensitive = Operator.EndsWith("*"); + OperatorIsNegated = OperatorParsed != FilterOperator.NotEquals && Operator.StartsWith("!"); } } @@ -47,12 +52,11 @@ namespace Sieve.Models public string Operator { get; private set; } - private FilterOperator GetOperatorParsed(string Operator) + private FilterOperator GetOperatorParsed(string @operator) { - switch (Operator.Trim().ToLower()) + switch (@operator.TrimEnd('*')) { case "==": - case "==*": return FilterOperator.Equals; case "!=": return FilterOperator.NotEquals; @@ -65,10 +69,10 @@ namespace Sieve.Models case "<=": return FilterOperator.LessThanOrEqualTo; case "@=": - case "@=*": + case "!@=": return FilterOperator.Contains; case "_=": - case "_=*": + case "!_=": return FilterOperator.StartsWith; default: return FilterOperator.Equals; @@ -77,6 +81,8 @@ namespace Sieve.Models public bool OperatorIsCaseInsensitive { get; private set; } + public bool OperatorIsNegated { get; private set; } + public bool Equals(FilterTerm other) { return Names.SequenceEqual(other.Names) diff --git a/Sieve/Models/IFilterTerm.cs b/Sieve/Models/IFilterTerm.cs index fd773e9..f937637 100644 --- a/Sieve/Models/IFilterTerm.cs +++ b/Sieve/Models/IFilterTerm.cs @@ -6,6 +6,7 @@ string[] Names { get; } string Operator { get; } bool OperatorIsCaseInsensitive { get; } + bool OperatorIsNegated { get; } FilterOperator OperatorParsed { get; } string[] Values { get; } } diff --git a/Sieve/Services/SieveProcessor.cs b/Sieve/Services/SieveProcessor.cs index 3e739db..b403adc 100644 --- a/Sieve/Services/SieveProcessor.cs +++ b/Sieve/Services/SieveProcessor.cs @@ -203,13 +203,20 @@ namespace Sieve.Services .First(m => m.Name == "ToUpper" && m.GetParameters().Length == 0)); } + var expression = GetExpression(filterTerm, filterValue, propertyValue); + + if (filterTerm.OperatorIsNegated) + { + expression = Expression.Not(expression); + } + if (innerExpression == null) { - innerExpression = GetExpression(filterTerm, filterValue, propertyValue); + innerExpression = expression; } else { - innerExpression = Expression.Or(innerExpression, GetExpression(filterTerm, filterValue, propertyValue)); + innerExpression = Expression.Or(innerExpression, expression); } } } diff --git a/SieveUnitTests/General.cs b/SieveUnitTests/General.cs index eefaff6..2f6ff83 100644 --- a/SieveUnitTests/General.cs +++ b/SieveUnitTests/General.cs @@ -101,6 +101,19 @@ namespace SieveUnitTests Assert.IsTrue(result.Count() == 0); } + [TestMethod] + public void NotContainsWorks() + { + var model = new SieveModel() + { + Filters = "Title!@=D", + }; + + var result = _processor.Apply(model, _posts); + + Assert.IsTrue(result.Count() == 3); + } + [TestMethod] public void CanFilterBools() {