query

package
v0.27.0-rc.12 Latest Latest
Warning

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

Go to latest
Published: Sep 28, 2026 License: Apache-2.0 Imports: 7 Imported by: 7

Documentation

Index

Constants

View Source
const (
	FilterTypeProperty = "property"
	FilterTypeOperator = "operator"
)

The filter node types, as they appear in the encoded JSON a client sends.

View Source
const (
	MinPage        = 1   // MinPage represents the minimum allowed value for the pagination query's Page parameter.
	MinPerPage     = 1   // MinPerPage represents the minimum allowed value for the pagination query's PerPage parameter.
	DefaultPerPage = 10  // DefaultPerPage represents the default value for the pagination query's PerPage parameter.
	MaxPerPage     = 100 // MaxPerPage represents the maximum allowed value for the pagination query's PerPage parameter.
)

The bounds a paginator is normalized into. Anything outside them is clamped rather than rejected, so a client cannot ask for an unbounded page.

View Source
const (
	OrderAsc  = "asc"
	OrderDesc = "desc"
)

The sort directions a query may ask for. Anything else normalizes to OrderDesc.

View Source
const (
	// MaxFilterItems caps the number of entries in [Filters.Data].
	MaxFilterItems = 8
	// MaxStringValueLen caps a string in [FilterProperty.Value], and each
	// item when the value is an array.
	MaxStringValueLen = 256
	// MaxArrayLen caps the length of an array in [FilterProperty.Value].
	MaxArrayLen = 4
	// MaxFilterRawBytes caps the base64-encoded filter query parameter
	// before decode.
	MaxFilterRawBytes = 16 * 1024
)

Size limits applied to client-supplied filters. Together they bound the cost of serving a malicious filter payload: the outer limit caps the raw request size, the inner limits cap the structural complexity after decode.

Variables

View Source
var (
	// ErrFilterInvalid is returned when a filter is not base64url-encoded JSON of the expected shape.
	ErrFilterInvalid = errors.New("filter is invalid")
	// ErrFilterPropertyInvalid is returned when a property node names a field the store does not
	// allow filtering on.
	ErrFilterPropertyInvalid = errors.New("filter property is not valid")
	// ErrFilterOperatorInvalid is returned when a node names an operator the store does not support.
	ErrFilterOperatorInvalid = errors.New("filter operator is not valid")
	// ErrFilterTooLarge is returned when the encoded filter is longer than the cap, which bounds the
	// work a single query can ask for.
	ErrFilterTooLarge = errors.New("filter exceeds the maximum size")
	// ErrSorterFieldInvalid is returned when a sort names a field the store does not allow sorting on.
	ErrSorterFieldInvalid = errors.New("sort field is not valid")
)

Functions

func ValidateFilters added in v0.21.7

func ValidateFilters(filters *Filters, constraints FieldConstraints) error

ValidateFilters returns ErrFilterPropertyInvalid if any property filter references a (field, operator) pair not in constraints, compares a field against a value it does not declare (see FieldConstraints.WithValues), carries a non-primitive Value, or exceeds the configured size limits. Operator filters (and/or) are left to the store to parse.

Equality on a virtual bool-backed field (see FieldConstraints.IsVirtualBoolField) accepts anything bool-convertible, because ParseFilterProperty intercepts those before any column is bound. Every other field must be compared against a string, or Postgres answers a type mismatch with a 500.

func ValidateSorter added in v0.21.7

func ValidateSorter(sorter *Sorter, allowed FieldSet) error

ValidateSorter returns ErrSorterFieldInvalid if the sort field is set and not in allowed. An empty Sorter.By is valid (the store falls back to a stable default).

Types

type FieldConstraints added in v0.21.7

type FieldConstraints struct {
	// contains filtered or unexported fields
}

FieldConstraints maps a filter field name to the set of operators allowed against it. It lets the handler reject field+operator combinations that the database rejects with a server-side error (e.g. ILIKE on an enum), turning them into a clean HTTP 400 before they reach the store.

A subset of fields may be declared as virtual bool-backed at construction time (see NewFieldConstraints). Virtual fields are intercepted by ParseFilterProperty before any SQL column binding, so they safely accept bool-convertible values with eq/ne. Real boolean columns must NOT be declared virtual unless a corresponding ParseFilterProperty intercept exists.

func NewFieldConstraints added in v0.21.7

func NewFieldConstraints(entries map[string][]string, virtualBools ...string) FieldConstraints

NewFieldConstraints returns a FieldConstraints initialized with the given field→operators pairs. An empty operators slice means the field is rejected entirely. virtualBools lists the field names that are virtual bool-backed: they are intercepted by ParseFilterProperty before SQL column binding, so they may accept bool-convertible values with eq/ne. Only fields that have a corresponding ParseFilterProperty intercept should be listed here.

func (FieldConstraints) Allows added in v0.21.7

func (c FieldConstraints) Allows(name, operator string) bool

Allows reports whether operator is valid for the given field name.

func (FieldConstraints) AllowsValue

func (c FieldConstraints) AllowsValue(name, value string) bool

AllowsValue reports whether value is comparable against the given field. It is true for a field that declares no values, so a field carrying free text is unaffected.

func (FieldConstraints) IsVirtualBoolField added in v0.26.0

func (c FieldConstraints) IsVirtualBoolField(name string) bool

IsVirtualBoolField reports whether name is a virtual bool-backed field — one explicitly declared as virtual at construction time via NewFieldConstraints and therefore intercepted by ParseFilterProperty before any SQL column binding.

Only fields in the virtualBools registry return true. Using the presence of the "bool" operator as a proxy is incorrect: a real boolean column that only allows "bool" (not "eq"/"ne") is safe today, but the moment "eq" is added to it without a ParseFilterProperty intercept, the validator would silently accept bool/float64 values that produce a Postgres type-mismatch 500 at runtime.

func (FieldConstraints) WithUUIDs

func (c FieldConstraints) WithUUIDs(names ...string) FieldConstraints

WithUUIDs returns a copy of the constraints where each named field accepts only a UUID in its canonical 36-character form under eq and ne. Declare it for a field backed by a uuid column: without it a malformed value reaches the column and Postgres answers a type error the store reports as a 500 rather than a 400.

func (FieldConstraints) WithValues

func (c FieldConstraints) WithValues(entries map[string][]string) FieldConstraints

WithValues returns a copy of the constraints where each named field accepts only the given values under eq and ne. A field absent from entries keeps accepting any string, so declaring values is opt-in per field. Declare them for a field backed by a database enum: without them an unknown value reaches the column and Postgres answers a type error the store reports as a 500 rather than a 400.

type FieldSet added in v0.21.7

type FieldSet map[string]struct{}

FieldSet is a set of field names allowed for use as a database identifier in sort options.

func NewFieldSet added in v0.21.7

func NewFieldSet(names ...string) FieldSet

NewFieldSet returns a FieldSet containing the given names.

func (FieldSet) Allows added in v0.21.7

func (s FieldSet) Allows(name string) bool

Allows reports whether name is in the set.

type Filter

type Filter struct {
	Type   string `json:"type,omitempty"`
	Params any    `json:"params,omitempty"`
}

Filter is one node of a query filter: a tagged union whose Type picks the shape of Params. Unmarshal one rather than building it by hand, or Params holds a map instead of a params struct.

func (*Filter) UnmarshalJSON

func (f *Filter) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes Params into the struct named by Type. An unrecognized Type leaves Params nil rather than failing, so a filter the server does not know narrows nothing.

type FilterOperator

type FilterOperator struct {
	// Name is the filter operator (e.g., "and", "or").
	Name string `json:"name"`
}

FilterOperator represents a JSON representation of a filter operator in a query (e.g., "and", "or" in MongoDB queries).

type FilterProperty

type FilterProperty struct {
	// Name is the attribute to be observed in the operation.
	Name string `json:"name"`

	// Operator is the operation (e.g., "eq" for equal).
	Operator string `json:"operator"`

	// Value is the value used in the operation. (e.g., "eq" operations use Value to determine the value to be equal).
	Value any `json:"value"`
}

FilterProperty is a JSON representation of a property expression in a query.

Name is the attribute to be observed in the operation, Operator is the operation, and Value is the value used in the operation. While Name can be any string, the Operator must be supported by the implementation, and the Value is the value used in the operation.

Each operator has its own implementation, and one operator can have multiple implementations. For that reason, the operator must be converted to a useful value using build methods.

Examples: A FilterProperty with Operator "gt", Name "count", and Value 12 will filter documents with the attribute "count" greater than 12. Another FilterProperty with Operator "eq", Name "alias", and Value "foobar" will filter documents with the attribute "alias" equal to "foobar".

type Filters

type Filters struct {
	// Raw holds the raw data of the filter. It must be a base64url-encoded JSON
	// (RFC 4648 §5, unpadded); also accepts padded/unpadded standard base64.
	Raw string `query:"filter"`

	// Data stores the decoded filters; it's automatically populated with the Unmarshal method.
	Data []Filter
}

Filters represents a set of filters that can be applied to queries.

func NewFilters

func NewFilters() *Filters

NewFilters creates a new instance of Filters with an empty Data slice.

func (*Filters) Unmarshal

func (fs *Filters) Unmarshal() error

Unmarshal decodes and unmarshals the raw filters, populating the Data attribute. It rejects payloads larger than MaxFilterRawBytes before decode to keep a hostile caller from allocating large buffers at JSON decode time.

Both base64 alphabets are accepted, standard and URL-safe, with padding stripped first so either can be tried with its unpadded decoder.

type Paginator

type Paginator struct {
	// Page represents the current page number.
	Page int `query:"page"`

	// PerPage represents the number of items per page.
	PerPage int `query:"per_page"`
}

Paginator represents the paginator parameters in a query.

func NewPaginator

func NewPaginator() *Paginator

NewPaginator creates and returns a new Paginator instance with MinPage and DefaultPerPage.

func (*Paginator) Normalize

func (p *Paginator) Normalize()

Normalize ensures valid values for Page and PerPage in the pagination query. If query.PerPage is less than zero, it is set to `DefaultPerPage`. If query.Page is less than one, it is set to `MinPage`. The maximum allowed value for query.PerPage is `MaxPerPage`.

type Sorter

type Sorter struct {
	By       string `query:"sort_by"`
	Order    string `query:"order_by" validate:"omitempty,oneof=asc desc"`
	Tiebreak string // stable secondary sort column, set by service layer
}

Sorter represents the sorting order in a query.

func NewSorter

func NewSorter() *Sorter

NewSorter returns a sorter with no field and descending order, which is what a request that asked for no ordering gets.

func (*Sorter) Normalize

func (s *Sorter) Normalize()

Normalize ensures that the sorting order is valid. If an invalid order is provided, it defaults to descending order.

Jump to

Keyboard shortcuts

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