From a5b72bcd1b52b64642d907ea6dd49404cbbef8dc Mon Sep 17 00:00:00 2001 From: Jamess-Lucass <23193271+Jamess-Lucass@users.noreply.github.com> Date: Wed, 11 Dec 2024 21:49:56 +0000 Subject: [PATCH 1/2] feat(filter): Added ability to only filter by date, instead of datetime --- .../src/Ast/{StringLiteral.cs => Literals.cs} | 10 ++++++++++ src/GoatQuery/src/Evaluator/FilterEvaluator.cs | 5 +++++ src/GoatQuery/src/Lexer/Lexer.cs | 11 +++++++++++ src/GoatQuery/src/Parser/Parser.cs | 10 ++++++++-- src/GoatQuery/src/Token/Token.cs | 1 + src/GoatQuery/tests/Filter/FilterLexerTest.cs | 10 +++++----- src/GoatQuery/tests/Filter/FilterTest.cs | 5 +++++ 7 files changed, 45 insertions(+), 7 deletions(-) rename src/GoatQuery/src/Ast/{StringLiteral.cs => Literals.cs} (87%) diff --git a/src/GoatQuery/src/Ast/StringLiteral.cs b/src/GoatQuery/src/Ast/Literals.cs similarity index 87% rename from src/GoatQuery/src/Ast/StringLiteral.cs rename to src/GoatQuery/src/Ast/Literals.cs index e5b38c2..459cb68 100644 --- a/src/GoatQuery/src/Ast/StringLiteral.cs +++ b/src/GoatQuery/src/Ast/Literals.cs @@ -68,4 +68,14 @@ public DateTimeLiteral(Token token, DateTime value) : base(token) { Value = value; } +} + +public sealed class DateLiteral : QueryExpression +{ + public DateTime Value { get; set; } + + public DateLiteral(Token token, DateTime value) : base(token) + { + Value = value.Date; + } } \ No newline at end of file diff --git a/src/GoatQuery/src/Evaluator/FilterEvaluator.cs b/src/GoatQuery/src/Evaluator/FilterEvaluator.cs index 45d2b20..94b5d70 100644 --- a/src/GoatQuery/src/Evaluator/FilterEvaluator.cs +++ b/src/GoatQuery/src/Evaluator/FilterEvaluator.cs @@ -51,6 +51,11 @@ public static Result Evaluate(QueryExpression expression, ParameterE case DateTimeLiteral literal: value = Expression.Constant(literal.Value, property.Type); break; + case DateLiteral literal: + property = Expression.Property(property, "Date"); + + value = Expression.Constant(literal.Value.Date, property.Type); + break; default: return Result.Fail($"Unsupported literal type: {exp.Right.GetType().Name}"); } diff --git a/src/GoatQuery/src/Lexer/Lexer.cs b/src/GoatQuery/src/Lexer/Lexer.cs index d2dbca4..398067b 100644 --- a/src/GoatQuery/src/Lexer/Lexer.cs +++ b/src/GoatQuery/src/Lexer/Lexer.cs @@ -64,6 +64,12 @@ public Token NextToken() if (IsDigit(token.Literal[0])) { + if (IsDate(token.Literal)) + { + token.Type = TokenType.DATE; + return token; + } + if (IsDateTime(token.Literal)) { token.Type = TokenType.DATETIME; @@ -103,6 +109,11 @@ public Token NextToken() return token; } + private bool IsDate(string value) + { + return DateTime.TryParseExact(value, new[] { "yyyy-MM-dd" }, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out _); + } + private bool IsDateTime(string value) { return DateTime.TryParse(value, out _); diff --git a/src/GoatQuery/src/Parser/Parser.cs b/src/GoatQuery/src/Parser/Parser.cs index 745b336..d6f65a3 100644 --- a/src/GoatQuery/src/Parser/Parser.cs +++ b/src/GoatQuery/src/Parser/Parser.cs @@ -140,7 +140,7 @@ private Result ParseFilterStatement() var statement = new InfixExpression(_currentToken, identifier, _currentToken.Literal); - if (!PeekTokenIn(TokenType.STRING, TokenType.INT, TokenType.GUID, TokenType.DATETIME, TokenType.DECIMAL, TokenType.FLOAT, TokenType.DOUBLE)) + if (!PeekTokenIn(TokenType.STRING, TokenType.INT, TokenType.GUID, TokenType.DATETIME, TokenType.DECIMAL, TokenType.FLOAT, TokenType.DOUBLE, TokenType.DATE)) { return Result.Fail("Invalid value type within filter"); } @@ -152,7 +152,7 @@ private Result ParseFilterStatement() return Result.Fail("Value must be a string when using 'contains' operand"); } - if (statement.Operator.In(Keywords.Lt, Keywords.Lte, Keywords.Gt, Keywords.Gte) && !CurrentTokenIn(TokenType.INT, TokenType.DECIMAL, TokenType.FLOAT, TokenType.DOUBLE, TokenType.DATETIME)) + if (statement.Operator.In(Keywords.Lt, Keywords.Lte, Keywords.Gt, Keywords.Gte) && !CurrentTokenIn(TokenType.INT, TokenType.DECIMAL, TokenType.FLOAT, TokenType.DOUBLE, TokenType.DATETIME, TokenType.DATE)) { return Result.Fail($"Value must be an integer when using '{statement.Operator}' operand"); } @@ -204,6 +204,12 @@ private Result ParseFilterStatement() statement.Right = new DateTimeLiteral(_currentToken, dateTimeValue); } break; + case TokenType.DATE: + if (DateTime.TryParse(_currentToken.Literal, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var dateValue)) + { + statement.Right = new DateLiteral(_currentToken, dateValue); + } + break; } return statement; diff --git a/src/GoatQuery/src/Token/Token.cs b/src/GoatQuery/src/Token/Token.cs index 058b681..a2921b0 100644 --- a/src/GoatQuery/src/Token/Token.cs +++ b/src/GoatQuery/src/Token/Token.cs @@ -10,6 +10,7 @@ public enum TokenType DOUBLE, GUID, DATETIME, + DATE, LPAREN, RPAREN, } diff --git a/src/GoatQuery/tests/Filter/FilterLexerTest.cs b/src/GoatQuery/tests/Filter/FilterLexerTest.cs index 03a9908..ff2b2a3 100644 --- a/src/GoatQuery/tests/Filter/FilterLexerTest.cs +++ b/src/GoatQuery/tests/Filter/FilterLexerTest.cs @@ -331,7 +331,7 @@ public static IEnumerable Parameters() { new (TokenType.IDENT, "dateOfBirth"), new (TokenType.IDENT, "eq"), - new (TokenType.DATETIME, "2000-01-01"), + new (TokenType.DATE, "2000-01-01"), } }; @@ -342,7 +342,7 @@ public static IEnumerable Parameters() { new (TokenType.IDENT, "dateOfBirth"), new (TokenType.IDENT, "lt"), - new (TokenType.DATETIME, "2000-01-01"), + new (TokenType.DATE, "2000-01-01"), } }; @@ -353,7 +353,7 @@ public static IEnumerable Parameters() { new (TokenType.IDENT, "dateOfBirth"), new (TokenType.IDENT, "lte"), - new (TokenType.DATETIME, "2000-01-01"), + new (TokenType.DATE, "2000-01-01"), } }; @@ -364,7 +364,7 @@ public static IEnumerable Parameters() { new (TokenType.IDENT, "dateOfBirth"), new (TokenType.IDENT, "gt"), - new (TokenType.DATETIME, "2000-01-01"), + new (TokenType.DATE, "2000-01-01"), } }; @@ -375,7 +375,7 @@ public static IEnumerable Parameters() { new (TokenType.IDENT, "dateOfBirth"), new (TokenType.IDENT, "gte"), - new (TokenType.DATETIME, "2000-01-01"), + new (TokenType.DATE, "2000-01-01"), } }; diff --git a/src/GoatQuery/tests/Filter/FilterTest.cs b/src/GoatQuery/tests/Filter/FilterTest.cs index 8e2d2c0..52af816 100644 --- a/src/GoatQuery/tests/Filter/FilterTest.cs +++ b/src/GoatQuery/tests/Filter/FilterTest.cs @@ -174,6 +174,11 @@ public static IEnumerable Parameters() new[] { _users["Egg"] } }; + yield return new object[] { + "dateOfBirth eq 2020-05-09", + new[] { _users["Jane"] } + }; + yield return new object[] { "dateOfBirth lt 2010-01-01", new[] { _users["John"], _users["Apple"], _users["Harry"], _users["Egg"] } From 885628c2fec8272dd43791f6470d8e36bb231e75 Mon Sep 17 00:00:00 2001 From: Jamess-Lucass <23193271+Jamess-Lucass@users.noreply.github.com> Date: Wed, 11 Dec 2024 21:53:59 +0000 Subject: [PATCH 2/2] remove unnecessary .date within dateLiteral --- src/GoatQuery/src/Ast/Literals.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GoatQuery/src/Ast/Literals.cs b/src/GoatQuery/src/Ast/Literals.cs index 459cb68..e068366 100644 --- a/src/GoatQuery/src/Ast/Literals.cs +++ b/src/GoatQuery/src/Ast/Literals.cs @@ -76,6 +76,6 @@ public sealed class DateLiteral : QueryExpression public DateLiteral(Token token, DateTime value) : base(token) { - Value = value.Date; + Value = value; } } \ No newline at end of file