Skip to content

Commit d77eee7

Browse files
committed
wip
1 parent c2c3abc commit d77eee7

File tree

8 files changed

+814
-457
lines changed

8 files changed

+814
-457
lines changed

copilot-instructions.md

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# Copilot Instructions
2+
3+
This file provides guidance for GitHub Copilot and other AI coding assistants when working with code in this repository.
4+
5+
## Project Overview
6+
7+
GoatQuery is a .NET library that provides REST API query capabilities including paging, ordering, filtering, searching, and selecting. It consists of two main packages:
8+
9+
- **GoatQuery**: Core library targeting .NET Standard 2.0/2.1 with lexer, parser, and evaluator components
10+
- **GoatQuery.AspNetCore**: ASP.NET Core integration targeting .NET 6.0/8.0 with action filters and middleware
11+
12+
The library parses OData-like query syntax into expression trees and applies them to IQueryable sources.
13+
14+
## Common Development Commands
15+
16+
### Build
17+
18+
Use the following commands to build the project:
19+
20+
- Build all projects:
21+
- `make build`
22+
- Build individual projects:
23+
- `dotnet build ./src/GoatQuery/src --configuration Release`
24+
- `dotnet build ./src/GoatQuery.AspNetCore/src --configuration Release`
25+
26+
### Test
27+
28+
Run tests using:
29+
30+
- All tests:
31+
- `make test`
32+
- Directly with dotnet:
33+
- `dotnet test ./src/GoatQuery/tests`
34+
- Specific test categories:
35+
- `dotnet test ./src/GoatQuery/tests --filter "Category=Filter"`
36+
- `dotnet test ./src/GoatQuery/tests --filter "Category=OrderBy"`
37+
38+
### Package and Publish
39+
40+
- Create NuGet packages:
41+
- `make package`
42+
- Publish to local feed:
43+
- `make publish-local`
44+
45+
### Run Example Application
46+
47+
To run the example app:
48+
49+
- `cd example`
50+
- `dotnet run`
51+
Endpoints are available at `/controller/users` or `/minimal/users`.
52+
53+
## Architecture Overview
54+
55+
### Core Components
56+
57+
**Lexer (`QueryLexer`)**: Tokenizes query strings into structured tokens (identifiers, operators, literals, etc.)
58+
59+
**Parser (`QueryParser`)**: Converts tokens into Abstract Syntax Tree (AST) nodes representing filter expressions and order-by statements
60+
61+
**Evaluators**: Transform AST nodes into LINQ Expression Trees:
62+
63+
- `FilterEvaluator`: Handles filtering logic with support for comparison operators, logical operators, and special nullable handling
64+
- `OrderByEvaluator`: Processes ordering statements with multiple columns and directions
65+
66+
**Query Application (`QueryableExtension.Apply`)**: Main entry point that orchestrates the entire pipeline from query string to modified IQueryable
67+
68+
### AST Node Hierarchy
69+
70+
- `QueryExpression` (base)
71+
- `InfixExpression`: Binary operations (eq, ne, and, or, etc.)
72+
- `Identifier`: Property references
73+
- Literals: `StringLiteral`, `IntegerLiteral`, `GuidLiteral`, `DateTimeLiteral`, etc.
74+
75+
### Supported Query Operations
76+
77+
- **Filtering**: `firstname eq 'John'`, `age gt 21`, `date lt 2024-01-01`
78+
- **Logical operators**: `and`, `or` with proper precedence
79+
- **Comparison operators**: `eq`, `ne`, `lt`, `lte`, `gt`, `gte`, `contains`
80+
- **Data types**: String, Integer, Decimal, Float, Double, DateTime, Date, Guid, Boolean, Null
81+
- **Grouping**: Parentheses for expression precedence
82+
- **Ordering**: `firstname desc, age asc`
83+
- **Paging**: `top=10`, `skip=20`
84+
- **Counting**: `count=true`
85+
86+
### ASP.NET Core Integration
87+
88+
The `EnableQueryAttribute<T>` action filter automatically:
89+
90+
1. Extracts query parameters from HTTP request
91+
2. Constructs Query object
92+
3. Applies query to controller's IQueryable result
93+
4. Returns paginated response with optional count
94+
95+
### Property Mapping
96+
97+
Uses `JsonPropertyNameAttribute` for mapping JSON property names to C# properties, enabling consistent API naming conventions.
98+
99+
### Error Handling
100+
101+
Uses FluentResults pattern throughout for comprehensive error reporting with detailed messages for invalid queries, unsupported operations, and type mismatches.
102+
103+
## Testing Patterns
104+
105+
Tests use xUnit with parameterized test data to validate:
106+
107+
- Lexer tokenization accuracy
108+
- Parser AST generation
109+
- Filter evaluation correctness
110+
- OrderBy functionality
111+
- Integration scenarios
112+
113+
Test data includes comprehensive edge cases for nullable types, date handling, numeric precision, and complex nested expressions.
114+
115+
## Key Implementation Notes
116+
117+
- **Nullable Handling**: Special logic for nullable DateTime comparisons using date-only operations
118+
- **Type Conversion**: Automatic conversion between numeric types with overflow detection
119+
- **Case Sensitivity**: Property names are case-insensitive, string operations use culture-invariant comparisons
120+
- **Performance**: Expression tree compilation for efficient query execution
121+
- **Security**: Parameterized expressions prevent injection attacks
122+
123+
## Development Guidelines for Copilot
124+
125+
When using Copilot to work on query evaluation logic:
126+
127+
- Ensure consistency between lexer tokens, parser AST nodes, and evaluator logic
128+
- Add or update tests for new operators or data types
129+
- Consider nullable type handling for new features
130+
- Update both the core library and ASP.NET Core integration when adding functionality
131+
- Use the FluentResults pattern for error handling
132+
133+
If you are refactoring, adding features, or fixing bugs, please:
134+
135+
- Follow .NET and C# best practices
136+
- Write clear, maintainable code
137+
- Update documentation and tests as needed
138+
139+
For any automation, prefer using Makefile targets or standard dotnet CLI commands.

src/GoatQuery/src/Ast/Node.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public Node(Token token)
77
_token = token;
88
}
99

10-
public string TokenLiteral()
10+
public virtual string TokenLiteral()
1111
{
1212
return _token.Literal;
1313
}

src/GoatQuery/src/Ast/PropertyPath.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,8 @@ public string GetFullPath()
1515
return string.Join("/", Segments);
1616
}
1717

18-
public string GetFirstSegment()
18+
public override string TokenLiteral()
1919
{
20-
return Segments.FirstOrDefault();
21-
}
22-
23-
public List<string> GetRemainingSegments()
24-
{
25-
return Segments.Skip(1).ToList();
20+
return GetFullPath();
2621
}
2722
}

0 commit comments

Comments
 (0)