filter

package
v0.23.0 Latest Latest
Warning

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

Go to latest
Published: Jul 14, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Index

Constants

View Source
const MaxSearchTerms = 8

MaxSearchTerms bounds the number of tokens a single ?q= value expands to. A search term is one whitespace-delimited token; beyond this cap, extra tokens are dropped. Keeps statement size bounded against an adversarial input.

Variables

This section is empty.

Functions

func ApplySortToQuery

func ApplySortToQuery(qb *query.QueryBuilder, sorts []ParsedSort)

applySortToQuery applies parsed sorts to a query builder.

func ApplyToCountQuery

func ApplyToCountQuery(cb *query.CountBuilder, filters []ParsedFilter)

applyFiltersToCountQuery applies parsed filters to a count builder.

func ApplyToQuery

func ApplyToQuery(qb *query.QueryBuilder, filters []ParsedFilter)

applyFiltersToQuery applies parsed filters to a query builder.

Types

type Condition added in v0.18.0

type Condition struct {
	SQL  string
	Args []any
}

Condition is a single WHERE fragment (SQL + args) produced by SearchConditions. Convert to a hook.WhereClause and append via qb.Where(c.SQL, c.Args…) — the query builder wraps each Where in parens and AND-composes them, so multi-field search clauses combine safely with owner/tenant/soft-delete scopes.

func BuildPredicate added in v0.20.0

func BuildPredicate(p *Predicate) Condition

BuildPredicate compiles a validated predicate tree into one WHERE Condition: a fully-parenthesized SQL string with sequential $N placeholders in depth-first order and a matching, same-order Args slice. Field names are interpolated (they came from the schema allow-list in ParseWhere); every value is a bound arg. Hand the result to qb.Where(c.SQL, c.Args...) ONCE — the builder wraps it in its own parens and AND-joins it to the framework scopes, so the user's boolean logic can never escape to widen a scope.

The $N numbers are positional only; core/query.renumberPlaceholders rewrites them left-to-right and advances by len(Args), so the sole invariant is that placeholders appear in the same order as Args — which building both in one DFS pass guarantees.

func SearchConditions added in v0.18.0

func SearchConditions(fields []string, term string) []Condition

SearchConditions builds a slice of AND-composed search conditions from a free-text term over the given DB column names. Each whitespace- delimited token produces one Condition whose SQL is a parenthesized OR-group: (LOWER(f1) LIKE $1 ESCAPE '\' OR LOWER(f2) LIKE $2 ESCAPE '\'). Every Condition must match (AND); within one Condition, any field may match (OR). A blank/whitespace-only term returns nil (no conditions).

Case contract: LOWER() is ASCII-only on SQLite and locale-aware on Postgres, so matching is ASCII-case-insensitive everywhere. Unicode case folding is a Postgres bonus. The token itself is lowercased before building the LIKE pattern so the comparison is consistent across dialects.

LIKE metacharacters (%, _, \) in each token are escaped via the existing escaper so they match literally — a user searching for "50%" finds rows containing "50%", not every row with any character sequence. The args are ordered to match the $N placeholders left-to-right; the crud query builder renumbers $N on Build.

type FilterOp

type FilterOp string

FilterOp represents a comparison operator for query filtering.

const (
	OpEq   FilterOp = "eq"
	OpGt   FilterOp = "gt"
	OpLt   FilterOp = "lt"
	OpGte  FilterOp = "gte"
	OpLte  FilterOp = "lte"
	OpLike FilterOp = "like"
	OpIn   FilterOp = "in"
)

type ParsedFilter

type ParsedFilter struct {
	Field string
	Op    FilterOp
	Value string
}

ParsedFilter represents a single parsed filter from query parameters.

func ParseFilters

func ParseFilters(r *http.Request, fields []schema.Field) ([]ParsedFilter, error)

ParseFilters extracts filters from query parameters based on entity fields. Supported patterns:

?field=value        → equals
?field_gt=value     → greater than
?field_lt=value     → less than
?field_gte=value    → greater than or equal
?field_lte=value    → less than or equal
?field_like=value   → LIKE (contains)
?field_in=v1,v2,v3  → IN

Only fields present in the schema are accepted. Hidden fields are excluded from the allow-list (mirroring ParseSort): building a WHERE predicate on a column the caller can't read turns row-count/result changes into a value-disclosure oracle — an attacker could probe a Hidden column (e.g. a password hash) via ?password_hash_like=… and exfiltrate it prefix by prefix. A Hidden field name is treated as an unknown filter param and never produces a ParsedFilter.

type ParsedSort

type ParsedSort struct {
	Field string
	Desc  bool
}

ParsedSort represents sort direction for a field.

func ParseSort

func ParseSort(r *http.Request, fields []schema.Field) ([]ParsedSort, error)

ParseSort extracts sort information from query parameters. Supported: ?sort=field (ascending), ?sort=-field (descending).

Hidden fields are excluded from the allow-list: sorting by a hidden column reveals row ordering by a value the caller can't read, which is an information-disclosure path. Unknown fields fail closed with a 400-shaped error rather than being silently ignored — silent drop turns probe attempts into "the API works the same with or without this param" oracles that mask broken client code.

type Predicate added in v0.20.0

type Predicate struct {
	// Leaf fields (Children == nil):
	Field  string
	Op     FilterOp
	Value  string   // for scalar ops
	Values []string // for OpIn

	// Group fields (Children != nil):
	Or       bool // false = AND, true = OR
	Children []Predicate
}

Predicate is a node in a boolean filter tree: either a LEAF (one field/op/value comparison) or a GROUP (AND/OR of child predicates). It is the parsed, validated form of a `?where=<json>` request — every field has already been checked against the entity's schema allow-list and every operator against the supported set, so BuildPredicate may interpolate field names trusting they are safe while binding all values as placeholders.

Groups let callers express nested boolean logic the flat query-param filters cannot — e.g. `status = A OR (priority = high AND assignee = me)`. The whole tree compiles to ONE parenthesized WHERE clause that the query builder AND-composes with the framework's owner/tenant/ soft-delete scopes; a user OR-group can never widen past those scopes because each is a separate, individually-parenthesized clause.

func ParseWhere added in v0.20.0

func ParseWhere(raw string, fields []schema.Field) (*Predicate, error)

ParseWhere parses a `?where=<json>` predicate tree and validates every leaf field against the schema allow-list (Hidden fields excluded, same value-disclosure-oracle rationale as ParseFilters) and every operator against the supported set. Returns (nil, nil) when raw is empty. Any unknown field, unknown operator, malformed JSON, empty/ambiguous node, or a tree that exceeds the depth/node bounds returns an error — the caller maps it to 400. On success the tree is safe for BuildPredicate to compile.

Jump to

Keyboard shortcuts

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