urlquery

package
v0.0.0-...-d283dcf Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 2, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package urlquery translates URL query parameters into the arguments the database layer builds a query from.

The package owns the parsing of one input, url.Values, and produces nothing but database query primitives: filter conditions, present field markers, pagination and cursor arguments. It knows nothing about HTTP handlers or services, so both the framework list controllers and application services parse a request exactly the same way.

Which parameters a request may use is decided by the model: capabilities are opted in to by embedding model.Query, model.Pagination or model.Cursor. The parameter readers (Pagination, Cursor, Orders) ignore parameters the model did not opt in to and fall back to the framework defaults rather than failing the request; rejecting those capability keys is the list controller's job, not this package's. Keys that map to nothing are different and always fail — in Decode and Filters alike — because silently dropping a mistyped filter would widen the result set.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Cursor

func Cursor(q url.Values, m types.Model) (types.Cursor, error)

Cursor returns the cursor position of the request, ready to be passed to Database.WithCursor.

A model opts in to cursor pagination by embedding model.Cursor; any other model yields a zero cursor, which WithCursor treats as a no-op. The cursor column is validated against the model's filterable columns and the cursor value against that column's Go type, so an unknown column or a mistyped value fails here instead of reaching the database, which would coerce the value instead of failing (MySQL turns a non-numeric boundary on a numeric column into 0) and silently restart the feed from the first page.

A URL cursor always pages an ascending feed: _cursor_next only chooses whether the request travels along the feed or back down it. A descending feed is a service-side cursor, built with types.CursorForward on a Desc order.

func Decode

func Decode(q url.Values, m types.Model) error

Decode fills the model's own query fields from the URL query, producing the query value WithQuery takes as its first argument.

Decoding runs on a per-type field plan compiled once and cached. A field's URL name follows modelschema.QueryColumnName — the query tag wins over the json tag, which wins over the field name, snake-cased — the same rule that names filter columns, so a field's bare key and its operator-filter key always agree. A field carrying query:"-" is not decodable, and a field whose type no setter exists for (structs such as time.Time, slices, maps) is left out of the plan, so its key reports as unsupported.

The keys Filters owns never reach the plan, so the "field[op]" bracket syntax and the bare framework timestamp keys stay with Filters. _format belongs to the export action and is dropped for every caller. _size is dropped when the model cannot carry it: a model embedding only Cursor accepts the parameter as its batch size, but the Size field lives in Pagination.

Every remaining key must map to a decodable field, so a mistyped filter name is reported instead of silently widening the result set, and every offending key is reported at once. Value semantics match the streaming decoder this replaced: the last value of a repeated key wins, an empty value means "not filtering" and is skipped, and pointer fields are allocated on demand.

func Filters

func Filters(q url.Values, m types.Model) ([]types.Filter, error)

Filters extracts field-level operator filters from URL query keys of the form "field[op]=value", e.g. "age[gt]=20" or "remark[like]=hello", plus the bare framework timestamp keys ("created_at", "updated_at"), which act as exact-match (eq) filters. The field token must resolve (after snake case normalization) to a queryable column of the model, and op must be a known types.FilterOp; anything else is rejected so a mistyped filter can never silently widen the result set. Empty values mean "not filtering" and are skipped. Filters require the model to embed model.Query, and the returned conditions are sorted by key for deterministic SQL.

func Orders

func Orders(q url.Values, m types.Model) ([]types.Order, error)

Orders returns the ORDER BY terms of the request, ready to be passed to Database.WithOrder.

A model opts in to sorting by embedding model.Query; any other model yields no order at all. Column names are validated against the model's filterable columns, so an unknown column fails here with an error the caller turns into a client error, instead of reaching the database and failing the whole page with a SQL error. The value carried in each Order is the database column name, while the URL names the column by its query name.

func Pagination

func Pagination(q url.Values, m types.Model) (page, size int)

Pagination returns the page and size arguments of the request, ready to be passed to Database.WithPagination.

A model opts in to client-controlled paging by embedding model.Pagination (page and size) or model.Cursor (size only); a parameter the model did not opt in to is ignored and falls back to the framework default. The returned page is always at least 1 — an unset, non-positive or unparsable page means the first page — so a caller can compute offsets or slice in-memory pages without normalizing again. An unset size defaults to a small first page and an oversized one clamps to the cap, while a model without client size control keeps the full-table safety limit. An active cursor resets page to 1 so offset paging cannot stack on top of cursor filtering.

func PresentFields

func PresentFields(q url.Values) map[string]struct{}

PresentFields collects the model filter keys explicitly provided in the URL query string, keyed by snake case column name, so the database layer can keep zero values (false, 0) of these columns as query conditions. Framework parameters (the "_" prefix namespace) and keys whose values are all empty are excluded: they are not model filter columns, and an empty value means the caller is not filtering by that key.

func UnsupportedParameterError

func UnsupportedParameterError(keys []string) error

UnsupportedParameterError reports query keys the request may not use, either because they map to no query field of the model or because the model did not opt in to the capability owning them. Every offending key is listed, sorted, so the client sees the full set at once.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL