diff --git a/docs/usage/reading/filtering.md b/docs/usage/reading/filtering.md index a1c1215ccd..b99a14c3b2 100644 --- a/docs/usage/reading/filtering.md +++ b/docs/usage/reading/filtering.md @@ -196,7 +196,7 @@ matchTextExpression: ( 'contains' | 'startsWith' | 'endsWith' ) LPAREN fieldChain COMMA literalConstant RPAREN; anyExpression: - 'any' LPAREN fieldChain COMMA literalConstant ( COMMA literalConstant )+ RPAREN; + 'any' LPAREN fieldChain ( COMMA literalConstant )+ RPAREN; hasExpression: 'has' LPAREN fieldChain ( COMMA filterExpression )? RPAREN; diff --git a/src/JsonApiDotNetCore/Queries/Expressions/AnyExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/AnyExpression.cs index 2b855b1bdb..ecacc41c7c 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/AnyExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/AnyExpression.cs @@ -17,12 +17,7 @@ public class AnyExpression : FilterExpression public AnyExpression(ResourceFieldChainExpression targetAttribute, IImmutableSet constants) { ArgumentGuard.NotNull(targetAttribute); - ArgumentGuard.NotNull(constants); - - if (constants.Count < 2) - { - throw new ArgumentException("At least two constants are required.", nameof(constants)); - } + ArgumentGuard.NotNullNorEmpty(constants); TargetAttribute = targetAttribute; Constants = constants; diff --git a/src/JsonApiDotNetCore/Queries/Internal/Parsing/FilterParser.cs b/src/JsonApiDotNetCore/Queries/Internal/Parsing/FilterParser.cs index c68e0f77f7..b768eb15b1 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/Parsing/FilterParser.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/Parsing/FilterParser.cs @@ -199,11 +199,6 @@ protected AnyExpression ParseAny() LiteralConstantExpression constant = ParseConstant(); constantsBuilder.Add(constant); - EatSingleCharacterToken(TokenKind.Comma); - - constant = ParseConstant(); - constantsBuilder.Add(constant); - while (TokenStack.TryPeek(out Token? nextToken) && nextToken.Kind == TokenKind.Comma) { EatSingleCharacterToken(TokenKind.Comma); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterOperatorTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterOperatorTests.cs index 0abad96024..6d000f6433 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterOperatorTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterOperatorTests.cs @@ -466,6 +466,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Theory] + [InlineData("yes", "no", "'yes'")] [InlineData("two", "one two", "'one','two','three'")] [InlineData("two", "nine", "'one','two','three','four','five'")] public async Task Can_filter_in_set(string matchingText, string nonMatchingText, string filterText) diff --git a/test/JsonApiDotNetCoreTests/UnitTests/QueryStringParameters/FilterParseTests.cs b/test/JsonApiDotNetCoreTests/UnitTests/QueryStringParameters/FilterParseTests.cs index e471218ff6..562270a358 100644 --- a/test/JsonApiDotNetCoreTests/UnitTests/QueryStringParameters/FilterParseTests.cs +++ b/test/JsonApiDotNetCoreTests/UnitTests/QueryStringParameters/FilterParseTests.cs @@ -86,7 +86,7 @@ public void Reader_Is_Enabled(JsonApiQueryStringParameters parametersDisabled, b [InlineData("filter", "any(null,'a','b')", "Attribute 'null' does not exist on resource type 'blogs'.")] [InlineData("filter", "any('a','b','c')", "Field name expected.")] [InlineData("filter", "any(title,'b','c',)", "Value between quotes expected.")] - [InlineData("filter", "any(title,'b')", ", expected.")] + [InlineData("filter", "any(title)", ", expected.")] [InlineData("filter[posts]", "any(author,'a','b')", "Attribute 'author' does not exist on resource type 'blogPosts'.")] [InlineData("filter", "and(", "Filter function expected.")] [InlineData("filter", "or(equals(title,'some'),equals(title,'other')", ") expected.")] @@ -146,6 +146,7 @@ public void Reader_Read_Fails(string parameterName, string parameterValue, strin [InlineData("filter", "contains(title,'this')", null, "contains(title,'this')")] [InlineData("filter", "startsWith(title,'this')", null, "startsWith(title,'this')")] [InlineData("filter", "endsWith(title,'this')", null, "endsWith(title,'this')")] + [InlineData("filter", "any(title,'this')", null, "any(title,'this')")] [InlineData("filter", "any(title,'this','that','there')", null, "any(title,'that','there','this')")] [InlineData("filter", "and(contains(title,'sales'),contains(title,'marketing'),contains(title,'advertising'))", null, "and(contains(title,'sales'),contains(title,'marketing'),contains(title,'advertising'))")]