FlexQuery.NET.Parsers.Jql
3.1.0
dotnet add package FlexQuery.NET.Parsers.Jql --version 3.1.0
NuGet\Install-Package FlexQuery.NET.Parsers.Jql -Version 3.1.0
<PackageReference Include="FlexQuery.NET.Parsers.Jql" Version="3.1.0" />
<PackageVersion Include="FlexQuery.NET.Parsers.Jql" Version="3.1.0" />
<PackageReference Include="FlexQuery.NET.Parsers.Jql" />
paket add FlexQuery.NET.Parsers.Jql --version 3.1.0
#r "nuget: FlexQuery.NET.Parsers.Jql, 3.1.0"
#:package FlexQuery.NET.Parsers.Jql@3.1.0
#addin nuget:?package=FlexQuery.NET.Parsers.Jql&version=3.1.0
#tool nuget:?package=FlexQuery.NET.Parsers.Jql&version=3.1.0
FlexQuery.NET.Parsers.Jql
JQL (Jira Query Language) parser for FlexQuery.NET.
When to Use This Package
Install this package when you want to support JQL-style filter syntax in your API. The JQL parser integrates with the QueryOptionsParser pipeline so JQL queries are automatically detected and parsed alongside the native DSL format.
Installation
dotnet add package FlexQuery.NET.Parsers.Jql
Quick Start
using FlexQuery.NET.Parsers.Jql;
var parser = new JqlQueryParser();
var filterGroup = parser.Parse("Status = 'Active' AND Age >= 18");
// GET /api/users?filter=Status = 'Active' AND Age >= 18
JQL Syntax Examples
Status = 'Active'
Age >= 18 AND
Name CONTAINS 'john'
Status = 'Active' AND Age >= 18
Category = 'Electronics' OR Category = 'Books'
DeletedAt IS NULL
Status IN ('Active', 'Pending')
Features
JqlQueryParser— Parses JQL filter expressions intoFilterGroupAST- Auto-Detection — Registers as
IQueryParserso JQL queries are handled seamlessly - Supported Operators — eq, neq, gt, gte, lt, lte, contains, startswith, endswith, like, isnull, isnotnull, in, notin, between, any, all, count
- Standalone Usage — Can be used without the full FlexQuery execution pipeline
Known Limitations
- The parser implements a subset of the full JQL specification — complex Jira functions and custom fields are not supported
- Date/time parsing uses .NET conventions rather than Jira-specific formats
Related Packages
- FlexQuery.NET — Core query engine
- FlexQuery.NET.Parsers.MiniOData — Alternative parser for OData syntax
Documentation
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- FlexQuery.NET (>= 3.1.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
-
net6.0
- FlexQuery.NET (>= 3.1.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0.0)
-
net8.0
- FlexQuery.NET (>= 3.1.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
# FlexQuery.NET v3.1.0 Release Notes
**Release date:** 2026-06-25
## Overview
v3.1.0 introduces query execution observability, AG Grid camelCase support, HAVING query pipeline fixes, a shared projection metadata layer unifying Dapper and EF Core serialization behavior, DynamicType materialization for GroupBy/aggregate results, and a new diagnostics API. The Dapper return type is unified with EF Core (`QueryResult<object>`).
---
## What's New
### 1. Execution Diagnostics / Observability
New `IFlexQueryExecutionListener` interface with 4 lifecycle events that let you observe the full query pipeline:
| Event | When | Data |
|-------|------|------|
| `QueryParsed` | After parsing input parameters | `QueryOptions`, elapsed time |
| `QueryTranslated` | After SQL generation | SQL string, parameters |
| `QueryExecuted` | After database query returns rows | Row count, optional exception |
| `QueryMaterialized` | After `QueryResult<object>` is built | Final result, optional exception |
Both Dapper and EF Core providers now accept an optional `configureExecution` callback on all overloads:
```csharp
var result = await connection.FlexQueryAsync<Customer>(parameters, opts,
exec => exec.Listener = new MyTelemetryListener());
```
New types:
- `FlexQueryExecutionConfig` — config class with `Listener` property
- `FlexQueryExecutionContext` — internal context carrying `QueryId`, `Stopwatch`, listener, and `CancellationToken`
- `FlexQueryExecutionEvent` base class with event data types `QueryParsedEvent`, `QueryTranslatedEvent`, `QueryExecutedEvent`, `QueryMaterializedEvent`
- `IFlexQueryExecutionListener` — 4 async methods for lifecycle hooks
### 2. AG Grid SSRM camelCase Support
`AgGridResponseConverter.Convert()` and `ToAgGridServerSideResponse()` extension methods accept a new `camelCase` parameter. When `true`, POCO property names in row data dictionaries are automatically converted from PascalCase to camelCase. Group metadata field names remain configurable via `AgGridResponseFieldOptions`.
### 3. DynamicType for GroupBy / Aggregate Results
GroupBy and aggregate queries now produce `DynamicType` instances instead of `Dictionary<string, object>`. DynamicType properties flow through ASP.NET Core's `PropertyNamingPolicy`, so camelCase serialization works automatically without manual conversion.
### 4. Shared Projection Metadata Layer
`ProjectionMetadataBuilder` extracted as a shared component used by both Dapper and EF Core providers. Reduces duplication and ensures consistent projection resolution (nested paths, field types, IEnumerable detection) across both pipeline implementations.
### 5. Sample Web API Project
New `FlexQuery.NET.Samples.WebApi` demonstrating EF Core, Dapper, AG Grid SSRM, and Kendo UI integrations with SQLite, Swagger, and demo frontends.
### 6. Benchmarks
- `DapperSqlGenerationBenchmarks` — simple, complex-filter, and aggregate SQL generation against SqlServer dialect
- `ProjectionBenchmarks` — `SelectTreeBuilder`, `DynamicTypeBuilder` cache hit/miss, `QueryCacheKeyBuilder` performance
---
## What's Fixed
### 1. HAVING Pipeline — Parser, SQL Generation, Alias Naming
Three independent bugs that caused HAVING clauses to be silently dropped or return incorrect results:
**a) HavingParser regex regression:**
The field portion of the HAVING expression was non-optional. Input like `count:gt:20` would misinterpret `gt` as a field name and return `null` because no field was found before the colon.
```csharp
// Before: field-less aggregate HAVING parsed as null
var having = parser.Parse("count:gt:20"); // null
// After:
var having = parser.Parse("count:gt:20"); // { Field: null, Aggregate: Count, Operator: GreaterThan, Value: 20 }
```
**b) Dapper SqlTranslator.BuildHavingClause:**
Field-less aggregates (`COUNT(*)`) emitted quoted `[*]` columns (`COUNT([*])`) and bound comparison values as strings (`'20'` instead of `20`). SQLite rejected `INTEGER > TEXT` comparisons, returning zero rows.
```sql
-- Before (broken for SQLite):
HAVING COUNT([*]) > '20'
-- After:
HAVING COUNT(*) > @p0 -- @p0 = 20 (int)
```
**c) BuildAggregateAlias:**
Field-less aggregates produced `allCount` as the alias instead of the predictable `Count`.
```sql
-- Before:
SELECT COUNT(*) AS [allCount]
-- After:
SELECT COUNT(*) AS [Count]
```
### 2. Dapper TotalCount / ResultCount Alignment with EF Core
**Before:** `ResultCount` was unconditionally set to `items.Count` (the page size), even when `IncludeTotalCount` was false.
**After:** `ResultCount` is only set when counts are explicitly enabled. Checks both `options.IncludeCount` (user-level) and `execOptions.IncludeTotalCount` (server-level). Both providers now return identical `QueryResult` structure for all scenarios.
### 3. Dapper Connection Lifecycle
Documented that `FlexQueryAsync<T>()` auto-opens the connection if closed but never auto-closes it. Added `CancellationToken` parameter to `ExecuteQueryAsync`.
---
## Breaking Changes
### 1. Dapper Return Type: `QueryResult<T>` → `QueryResult<object>`
**Affected package:** `FlexQuery.NET.Dapper`
**Before:** `FlexQueryAsync<T>()` returned `Task<QueryResult<T>>`.
**After:** Returns `Task<QueryResult<object>>`.
This unifies the Dapper provider's return type with the EF Core provider. Required because Dapper now uses `DynamicType` for projection materialization, and the projected type is not known at compile time.
**Migration:**
```csharp
// Before (will not compile):
QueryResult<Customer> result = await connection.FlexQueryAsync<Customer>(...);
// After:
QueryResult<object> result = await connection.FlexQueryAsync<Customer>(...);
// OR:
var result = await connection.FlexQueryAsync<Customer>(...);
```
### 2. DebugResult Namespace Change
**Affected package:** `FlexQuery.NET`
`DebugResult` moved from inline class in `FlexQuery.NET.Extensions` (inside `FlexQueryDebugExtensions.cs`) to `FlexQuery.NET.Models`.
**Migration:** Add `using FlexQuery.NET.Models;` where `DebugResult` is referenced.
---
## Behavioral Changes
| Change | Impact | Mitigation |
|--------|--------|------------|
| Dapper return type → `QueryResult<object>` | Compile break on explicit return type annotations | Use `var` or change to `QueryResult<object>` |
| `DebugResult` → `FlexQuery.NET.Models` | Compile break if relying on `FlexQuery.NET.Extensions` | Add `using FlexQuery.NET.Models;` |
| Dapper `ResultCount` null when count is off | Matches EF Core; previously returned `items.Count` | Add null check if needed |
| Field-less HAVING `Count` alias | Previously `allCount` | Update any HAVING filter referencing `allCount` |
| GroupBy/aggregate rows → `DynamicType` | Property names respect JSON naming policy; no longer `Dictionary<string, object>` | Access via properties instead of dictionary keys, or use reflection/DynamicType API |
---
## New Test Coverage
### HAVING Pipeline (19 new tests)
| Test | Area | Verifies |
|------|------|----------|
| `Parse_Having_FieldLessAggregate` | Parser | `count:gt:10` parses with null field |
| `Parse_Having_ParenthesizedValue` | Parser | `(count:gt:10)` parenthesized syntax |
| `Parse_Having_ColonSeparatedField` | Parser | Field-based HAVING with colons |
| `Parse_Having_Count` | Parser | `count` aggregate parsed |
| `Parse_Having_Sum` | Parser | `sum` aggregate parsed |
| `Parse_Having_Avg` | Parser | `avg` aggregate parsed |
| `Parse_Having_Max` | Parser | `max` aggregate parsed |
| `Parse_Having_Min` | Parser | `min` aggregate parsed |
| `BuildHavingClause_CountStar_NoField` | SQL generation | `COUNT(*) > @p0` without field |
| `BuildHavingClause_SumField` | SQL generation | `SUM([Amount]) > @p0` with field |
| `BuildHavingClause_AvgField` | SQL generation | `AVG([Amount]) > @p0` with field |
| `BuildHavingClause_MaxField` | SQL generation | `MAX([Amount]) > @p0` with field |
| `BuildHavingClause_MinField` | SQL generation | `MIN([Amount]) > @p0` with field |
| `BuildAggregateAlias_FieldLessAggregate_ReturnsCount` | Alias | Field-less → `Count` |
| `BuildAggregateAlias_FieldAggregate_ReturnsFieldFunction` | Alias | Field + function → `AmountSum` |
| `Having_Count_Grouped_Sorted_Paged` | Integration | Full pipeline: Count HAVING + sort + paging |
| `Having_Sum_Grouped_Sorted_Paged` | Integration | Full pipeline: Sum HAVING + sort + paging |
| `Having_Avg_Grouped_Projected` | Integration | HAVING with projection |
| `Having_Max_Min_Grouped_Projected` | Integration | Multiple aggregates HAVING with projection |
### GroupBy / Integration (204 new test lines)
Additional GroupBy execution tests across EF Core and Dapper covering dict vs DynamicType materialization, grouping with sort/paging combinations, and aggregate result structures.
### Parser (117 new test lines)
Additional parser coverage for edge cases and boundary conditions.
### AgGridResponseConverter
Adapted existing tests to cover the new `camelCase` parameter (both `true` and `false` paths).
---
## Additional Changes (post-commit)
- **ToQueryOptions extension method:** New `FlexQueryParametersExtensions.ToQueryOptions()` convenience extension on `FlexQueryParameters` for direct-to-`QueryOptions` parsing without calling `QueryOptionsParser.Parse()`.
- **Sample controllers simplified:** `DapperCustomersController` and `EfCustomersController` migrated to primary constructors and simplified to use the full FlexQueryAsync API instead of manual GroupBy handling.
- **MiniOData assembly name fix:** `QueryOptionsParser` registration corrected from `FlexQuery.NET.MiniOData` to `FlexQuery.NET.Parsers.MiniOData`.
- **Internal call sites updated:** `FlexQueryDapperExtensions`, `ProjectionEfCoreExtensions`, `QueryableEfCoreExtensions`, and `QueryableExtensions` all updated to use `parameters.ToQueryOptions()`.
---
## Migration Guide
### Dapper Return Type
Replace explicit `QueryResult<T>` with `QueryResult<object>` or use `var`:
```csharp
// Before:
QueryResult<Customer> result = await connection.FlexQueryAsync<Customer>(...);
// After:
var result = await connection.FlexQueryAsync<Customer>(...);
```
### HAVING Alias
If you reference the auto-generated alias for field-less aggregate HAVING filters, replace `allCount` with `Count`:
```csharp
// Before:
options.Having.Add(new HavingNode("allCount:gt:10"));
// After:
options.Having.Add(new HavingNode("Count:gt:10"));
```
### DebugResult Import
```csharp
// Add this using:
using FlexQuery.NET.Models;
```
---
## Upgrading
```bash
dotnet add package FlexQuery.NET --version 3.1.0
dotnet add package FlexQuery.NET.Dapper --version 3.1.0
dotnet add package FlexQuery.NET.EntityFrameworkCore --version 3.1.0
dotnet add package FlexQuery.NET.Parsers.Jql --version 3.1.0
dotnet add package FlexQuery.NET.Parsers.MiniOData --version 3.1.0
dotnet add package FlexQuery.NET.Adapters.AgGrid --version 3.1.0
dotnet add package FlexQuery.NET.Adapters.Kendo --version 3.1.0
dotnet add package FlexQuery.NET.AspNetCore --version 3.1.0
```