Skip to content

QueryEngine — Filtering, Sorting & Pagination

Granit.QueryEngine provides a declarative search engine: a strongly-typed QueryDefinition<T> describes columns, filter groups, quick filters, and sort options. The same definition drives the API query engine and the frontend data grid — no duplication.

  • DirectoryGranit.QueryEngine.Abstractions/ Contracts: QueryRequest, PagedResult, ISavedViewStore, SavedView
    • DirectoryGranit.QueryEngine/ QueryDefinition builder, DI wiring, null-object defaults
      • Granit.QueryEngine.EntityFrameworkCore EF Core IQueryEngine implementation
      • Granit.QueryEngine.AspNetCore Generic search endpoint, query binding, saved view CRUD
    • Granit.QueryEngine.AI NL-to-query translation via LLM
PackageRoleDepends on
Granit.QueryEngine.AbstractionsQueryRequest, PagedResult<T>, ISavedViewStoreReader/Writer, SavedViewGranit
Granit.QueryEngineQueryDefinition<T>, IQueryEngine<T>, DI wiring, null-object defaultsGranit.QueryEngine.Abstractions
Granit.QueryEngine.EntityFrameworkCoreEF Core query executor, EfCoreSavedViewStoreGranit.QueryEngine, Granit.Persistence
Granit.QueryEngine.AspNetCoreMapGranitQuery<T>() generic search endpointGranit.QueryEngine
Granit.QueryEngine.AINL-to-query translation via LLMGranit.QueryEngine.Abstractions, Granit.AI

The query engine applies defense-in-depth across multiple layers:

All endpoints registered via MapGranitQuery<T>() require authentication by default. To apply a named policy use AuthorizationPolicy; to opt out explicitly use AllowAnonymous:

endpoints.MapGranitQuery<Invoice>("/api/invoices", sp => ..., options =>
{
options.AuthorizationPolicy = "Invoices.Read"; // named policy
});
// Rare: anonymous access (public catalog)
endpoints.MapGranitQuery<Product>("/api/products", sp => ..., options =>
{
options.AllowAnonymous = true;
});

Update, delete, and set-default operations verify that the authenticated user owns the target view (or that the view is shared, for set-default). The response DTO exposes IsOwner (boolean) instead of the raw UserId to prevent user enumeration.

Search terms and string filter values (Contains, StartsWith, EndsWith) have SQL LIKE metacharacters (%, _, [) escaped before reaching EF Core, preventing wildcard injection probing (CWE-943).

Keyset pagination cursors can be signed with HMAC-SHA256 to prevent forgery. Configure a 256-bit key in QueryEngineOptions.CursorHmacKey (base64):

{
"QueryEngine": {
"CursorHmacKey": "BASE64_ENCODED_32_BYTE_KEY"
}
}

When configured, tampered cursors are rejected with a silent fallback.

ControlDefaultOption
Max page size100MaxPageSize
Max stream size100 000MaxStreamSize
Max group-by cardinality1 000MaxGroupCount
Max saved views per user100MaxSavedViewsPerUser
Max filter entries50Validator constant
Max filter key length200 charsValidator constant
Max filter value length2 000 charsValidator constant
Max JSON field length (saved views)10 000 charsValidator + EF config

Filter operators are validated at runtime against the column’s CLR type. For example, Contains is only accepted on string columns; Between is only accepted on numeric and date types. Invalid operator/type combinations are silently ignored.

Filter values, cursor payloads, and natural language inputs are never written to log messages. Only field names, target types, and input lengths are logged.