Skip to content

Commit 73537b8

Browse files
committed
feat: added boolean filter
1 parent 0098642 commit 73537b8

File tree

10 files changed

+72
-9
lines changed

10 files changed

+72
-9
lines changed

example/Dto/UserDto.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public record UserDto
88
public string Firstname { get; set; } = string.Empty;
99
public string Lastname { get; set; } = string.Empty;
1010
public int Age { get; set; }
11+
public bool IsEmailVerified { get; set; }
1112
public double Test { get; set; }
1213
public int? NullableInt { get; set; }
1314
public DateTime DateOfBirthUtc { get; set; }

example/Entities/User.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public record User
77
public string Lastname { get; set; } = string.Empty;
88
public int Age { get; set; }
99
public bool IsDeleted { get; set; }
10+
public bool IsEmailVerified { get; set; }
1011
public double Test { get; set; }
1112
public int? NullableInt { get; set; }
1213

example/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
.RuleFor(x => x.IsDeleted, f => f.Random.Bool())
4343
.RuleFor(x => x.Test, f => f.Random.Double())
4444
.RuleFor(x => x.NullableInt, f => f.Random.Bool() ? f.Random.Int(1, 100) : null)
45+
.RuleFor(x => x.IsEmailVerified, f => f.Random.Bool())
4546
.Rules((f, u) =>
4647
{
4748
var timeZone = TimeZoneInfo.FindSystemTimeZoneById("America/New_York");

src/GoatQuery/src/Ast/Literals.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,14 @@ public sealed class NullLiteral : QueryExpression
8585
public NullLiteral(Token token) : base(token)
8686
{
8787
}
88+
}
89+
90+
public sealed class BooleanLiteral : QueryExpression
91+
{
92+
public bool Value { get; set; }
93+
94+
public BooleanLiteral(Token token, bool value) : base(token)
95+
{
96+
Value = value;
97+
}
8898
}

src/GoatQuery/src/Evaluator/FilterEvaluator.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ public static Result<Expression> Evaluate(QueryExpression expression, ParameterE
6565
case NullLiteral _:
6666
value = Expression.Constant(null, property.Type);
6767
break;
68+
case BooleanLiteral literal:
69+
value = Expression.Constant(literal.Value, property.Type);
70+
break;
6871
default:
6972
return Result.Fail($"Unsupported literal type: {exp.Right.GetType().Name}");
7073
}

src/GoatQuery/src/Lexer/Lexer.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ public Token NextToken()
6868
return token;
6969
}
7070

71+
if (token.Literal.Equals(Keywords.True, StringComparison.OrdinalIgnoreCase) ||
72+
token.Literal.Equals(Keywords.False, StringComparison.OrdinalIgnoreCase))
73+
{
74+
token.Type = TokenType.BOOLEAN;
75+
return token;
76+
}
77+
7178
if (IsDigit(token.Literal[0]))
7279
{
7380
if (IsDate(token.Literal))

src/GoatQuery/src/Parser/Parser.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ private Result<InfixExpression> ParseFilterStatement()
140140

141141
var statement = new InfixExpression(_currentToken, identifier, _currentToken.Literal);
142142

143-
if (!PeekTokenIn(TokenType.STRING, TokenType.INT, TokenType.GUID, TokenType.DATETIME, TokenType.DECIMAL, TokenType.FLOAT, TokenType.DOUBLE, TokenType.DATE, TokenType.NULL))
143+
if (!PeekTokenIn(TokenType.STRING, TokenType.INT, TokenType.GUID, TokenType.DATETIME, TokenType.DECIMAL, TokenType.FLOAT, TokenType.DOUBLE, TokenType.DATE, TokenType.NULL, TokenType.BOOLEAN))
144144
{
145145
return Result.Fail("Invalid value type within filter");
146146
}
@@ -159,7 +159,7 @@ private Result<InfixExpression> ParseFilterStatement()
159159

160160
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))
161161
{
162-
return Result.Fail($"Value must be an integer when using '{statement.Operator}' operand");
162+
return Result.Fail($"Value must be a numeric or date type when using '{statement.Operator}' operand");
163163
}
164164

165165
switch (_currentToken.Type)
@@ -218,6 +218,12 @@ private Result<InfixExpression> ParseFilterStatement()
218218
case TokenType.NULL:
219219
statement.Right = new NullLiteral(_currentToken);
220220
break;
221+
case TokenType.BOOLEAN:
222+
if (bool.TryParse(_currentToken.Literal, out var boolValue))
223+
{
224+
statement.Right = new BooleanLiteral(_currentToken, boolValue);
225+
}
226+
break;
221227
}
222228

223229
return statement;

src/GoatQuery/src/Token/Token.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public enum TokenType
1212
DATETIME,
1313
DATE,
1414
NULL,
15+
BOOLEAN,
1516
LPAREN,
1617
RPAREN,
1718
}
@@ -30,6 +31,8 @@ public static class Keywords
3031
internal const string And = "and";
3132
internal const string Or = "or";
3233
internal const string Null = "null";
34+
internal const string True = "true";
35+
internal const string False = "false";
3336
}
3437

3538
public sealed class Token

src/GoatQuery/tests/Filter/FilterTest.cs

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ public sealed class FilterTest
44
{
55
private static readonly Dictionary<string, User> _users = new Dictionary<string, User>
66
{
7-
["John"] = new User { Age = 2, Firstname = "John", UserId = Guid.Parse("58cdeca3-645b-457c-87aa-7d5f87734255"), DateOfBirth = DateTime.Parse("2004-01-31 23:59:59"), BalanceDecimal = 1.50m },
8-
["Jane"] = new User { Age = 1, Firstname = "Jane", UserId = Guid.Parse("58cdeca3-645b-457c-87aa-7d5f87734255"), DateOfBirth = DateTime.Parse("2020-05-09 15:30:00"), BalanceDecimal = 0 },
9-
["Apple"] = new User { Age = 2, Firstname = "Apple", UserId = Guid.Parse("58cdeca3-645b-457c-87aa-7d5f87734255"), DateOfBirth = DateTime.Parse("1980-12-31 00:00:01"), BalanceFloat = 1204050.98f },
10-
["Harry"] = new User { Age = 1, Firstname = "Harry", UserId = Guid.Parse("e4c7772b-8947-4e46-98ed-644b417d2a08"), DateOfBirth = DateTime.Parse("2002-08-01"), BalanceDecimal = 0.5372958205929493m },
11-
["Doe"] = new User { Age = 3, Firstname = "Doe", UserId = Guid.Parse("58cdeca3-645b-457c-87aa-7d5f87734255"), DateOfBirth = DateTime.Parse("2023-07-26 12:00:30"), BalanceDecimal = null },
12-
["Egg"] = new User { Age = 3, Firstname = "Egg", UserId = Guid.Parse("58cdeca3-645b-457c-87aa-7d5f87734255"), DateOfBirth = DateTime.Parse("2000-01-01 00:00:00"), BalanceDouble = 1334534453453433.33435443343231235652d },
13-
["NullUser"] = new User { Age = 4, Firstname = "NullUser", UserId = Guid.Parse("11111111-1111-1111-1111-111111111111"), DateOfBirth = null, BalanceDecimal = null, BalanceDouble = null, BalanceFloat = null },
7+
["John"] = new User { Age = 2, Firstname = "John", UserId = Guid.Parse("58cdeca3-645b-457c-87aa-7d5f87734255"), DateOfBirth = DateTime.Parse("2004-01-31 23:59:59"), BalanceDecimal = 1.50m, IsEmailVerified = true },
8+
["Jane"] = new User { Age = 1, Firstname = "Jane", UserId = Guid.Parse("58cdeca3-645b-457c-87aa-7d5f87734255"), DateOfBirth = DateTime.Parse("2020-05-09 15:30:00"), BalanceDecimal = 0, IsEmailVerified = false },
9+
["Apple"] = new User { Age = 2, Firstname = "Apple", UserId = Guid.Parse("58cdeca3-645b-457c-87aa-7d5f87734255"), DateOfBirth = DateTime.Parse("1980-12-31 00:00:01"), BalanceFloat = 1204050.98f, IsEmailVerified = true },
10+
["Harry"] = new User { Age = 1, Firstname = "Harry", UserId = Guid.Parse("e4c7772b-8947-4e46-98ed-644b417d2a08"), DateOfBirth = DateTime.Parse("2002-08-01"), BalanceDecimal = 0.5372958205929493m, IsEmailVerified = false },
11+
["Doe"] = new User { Age = 3, Firstname = "Doe", UserId = Guid.Parse("58cdeca3-645b-457c-87aa-7d5f87734255"), DateOfBirth = DateTime.Parse("2023-07-26 12:00:30"), BalanceDecimal = null, IsEmailVerified = true },
12+
["Egg"] = new User { Age = 3, Firstname = "Egg", UserId = Guid.Parse("58cdeca3-645b-457c-87aa-7d5f87734255"), DateOfBirth = DateTime.Parse("2000-01-01 00:00:00"), BalanceDouble = 1334534453453433.33435443343231235652d, IsEmailVerified = false },
13+
["NullUser"] = new User { Age = 4, Firstname = "NullUser", UserId = Guid.Parse("11111111-1111-1111-1111-111111111111"), DateOfBirth = null, BalanceDecimal = null, BalanceDouble = null, BalanceFloat = null, IsEmailVerified = true },
1414
};
1515

1616
public static IEnumerable<object[]> Parameters()
@@ -264,6 +264,36 @@ public static IEnumerable<object[]> Parameters()
264264
"firstname eq 'Doe' and balanceDecimal eq null",
265265
new[] { _users["Doe"] }
266266
};
267+
268+
yield return new object[] {
269+
"isEmailVerified eq true",
270+
new[] { _users["John"], _users["Apple"], _users["Doe"], _users["NullUser"] }
271+
};
272+
273+
yield return new object[] {
274+
"isEmailVerified eq false",
275+
new[] { _users["Jane"], _users["Harry"], _users["Egg"] }
276+
};
277+
278+
yield return new object[] {
279+
"isEmailVerified ne true",
280+
new[] { _users["Jane"], _users["Harry"], _users["Egg"] }
281+
};
282+
283+
yield return new object[] {
284+
"isEmailVerified ne false",
285+
new[] { _users["John"], _users["Apple"], _users["Doe"], _users["NullUser"] }
286+
};
287+
288+
yield return new object[] {
289+
"age gt 2 and isEmailVerified eq true",
290+
new[] { _users["Doe"], _users["NullUser"] }
291+
};
292+
293+
yield return new object[] {
294+
"isEmailVerified eq false or age eq 2",
295+
new[] { _users["John"], _users["Jane"], _users["Apple"], _users["Harry"], _users["Egg"] }
296+
};
267297
}
268298

269299
[Theory]

src/GoatQuery/tests/User.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public record User
99
public double? BalanceDouble { get; set; }
1010
public float? BalanceFloat { get; set; }
1111
public DateTime? DateOfBirth { get; set; }
12+
public bool IsEmailVerified { get; set; }
1213
}
1314

1415
public sealed record CustomJsonPropertyUser : User

0 commit comments

Comments
 (0)