filterquery

package
v1.7.4 Latest Latest
Warning

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

Go to latest
Published: Aug 9, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package filterquery parses, validates, and emits boolean filter expressions (AIP-160-derived grammar) as parameterized SQL. It is schema-agnostic: adopters supply a FieldRegistry; the package knows nothing about any caller's tables. See docs/superpowers/specs/ 2026-07-25-filter-query-language-design.md.

Grammar sharp edges, by design:

  • One negation per term: NOT NOT x and -NOT x are parse errors.
  • A leading "-" starts a negation, so unquoted negative values do not parse; quote them (price="-5").
  • A quoted field name ("name":x) parses, but validation still requires an exact registered field and emission uses registry constants.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Compile

func Compile(q string, reg *Registry, d Dialect, start int) (string, []any, error)

Compile is the one-call pipeline for callers that know the placeholder offset up front. Handlers that discover the offset later should use Parse and defer Emit.

Types

type And

type And struct {
	Terms []Node
	At    int
}

And is a conjunction — explicit (expression level) or implicit (sequence level). The two levels share a node type; precedence is settled at parse.

func (*And) Pos

func (n *And) Pos() int

type Bare

type Bare struct {
	Text string
	At   int
}

Bare is an unqualified term (AIP-160 global restriction). The v1 registry rejects these at validation; the parser still produces them so the rejection error can name the term precisely.

func (*Bare) Pos

func (n *Bare) Pos() int

type Comparison

type Comparison struct {
	Field  string // dotted path, e.g. "label" or "metrics.latency"
	Op     string // one of ":", "=", "!=", "<", "<=", ">", ">="
	Raw    string // value text (unescaped contents for quoted strings)
	Quoted bool   // value was a quoted string
	Value  any    // coerced value, set by Validate

	At int
	// contains filtered or unexported fields
}

Comparison is `field op value`. Value holds the coerced value and is set by Registry.Validate; before validation only Raw is meaningful. A comparison may emit only through the registry that validated it.

func (*Comparison) Pos

func (n *Comparison) Pos() int

type Dialect

type Dialect interface {
	Placeholder(n int) string
}

Dialect abstracts SQL placeholder style. Task 4 adds the built-in Postgres adapter; defining the interface here keeps EmitCtx compilable.

type EmitCtx

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

EmitCtx hands FieldSpec.Emit sequential placeholders and collects args.

func (*EmitCtx) PH

func (e *EmitCtx) PH(arg any) string

PH records arg and returns its placeholder (e.g. "$4").

type ErrKind

type ErrKind int

ErrKind classifies an *Error for the HTTP layer (all map to 400 today; the distinction exists for metrics and future client hints).

const (
	ErrParse ErrKind = iota
	ErrValidate
	ErrCap
)

type Error

type Error struct {
	Kind ErrKind
	Pos  int
	Msg  string
}

Error is a positioned filter-language error. Pos is a 0-based byte offset into the input; -1 when not position-specific. The displayed column is therefore byte-based: with multibyte input before the error position it overcounts runes. Inputs are short (capped) and the column is a hint, so this is accepted rather than paying for rune indexing.

func (*Error) Error

func (e *Error) Error() string

type Expr

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

Expr is a parsed + validated filter, ready to emit at any placeholder offset. Safe for concurrent Emit calls: the AST is immutable after Parse, and Emit constructs a fresh EmitCtx per call.

func Parse

func Parse(q string, reg *Registry) (*Expr, error)

Parse lexes, parses, and validates q. Empty/whitespace-only q yields a nil Expr and nil error (no constraint).

func (*Expr) Emit

func (e *Expr) Emit(d Dialect, start int) (string, []any, error)

Emit renders the expression with placeholders numbered from start.

func (*Expr) Empty

func (e *Expr) Empty() bool

Empty reports a nil (no-constraint) Expr.

type FieldSpec

type FieldSpec struct {
	Name string
	Ops  []string // allowed operators, e.g. []string{":"}
	// Coerce converts the raw value text into the typed value stored on
	// Comparison.Value. quoted reports that the input was a quoted string.
	Coerce func(raw string, quoted bool) (any, error)
	// Emit renders one boolean predicate. Placeholders and args are bound
	// through EmitCtx.PH. Fields binding no value (boolean flags) simply
	// don't call PH.
	Emit func(c *Comparison, e *EmitCtx) (string, error)
}

FieldSpec describes one filterable field: which operators it accepts, how raw values coerce, and how a validated comparison emits SQL. Coerce and Emit must be deterministic and safe for concurrent calls: emission re-coerces a private comparison copy on every call.

type Node

type Node interface {
	Pos() int
}

Node is one AST node. Pos is the 0-based byte offset of the node's first character in the input, for error messages.

type Not

type Not struct {
	X  Node
	At int
}

func (*Not) Pos

func (n *Not) Pos() int

type Or

type Or struct {
	Terms []Node
	At    int
}

func (*Or) Pos

func (n *Or) Pos() int

type PostgresDialect

type PostgresDialect struct{}

PostgresDialect uses $n placeholders.

func (PostgresDialect) Placeholder

func (PostgresDialect) Placeholder(n int) string

type Registry

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

Registry is the adopter-supplied field set. Construct once at startup; safe for concurrent use after construction. Note the concurrency guarantee covers the REGISTRY, not a shared AST: Validate coerces Comparison values in place, so a single parsed AST must not be validated (or compiled) concurrently — parse per request. The Expr flow (Parse once, then concurrent Expr.Emit) is immutable and is the supported shared path.

func NewRegistry

func NewRegistry(specs ...FieldSpec) (*Registry, error)

func (*Registry) Emit

func (r *Registry) Emit(n Node, d Dialect, start int) (string, []any, error)

Emit renders a validated AST as a parenthesized boolean SQL fragment plus its bound args. Placeholders are numbered from start (1-based index of the next free parameter in the caller's query).

func (*Registry) Names

func (r *Registry) Names() []string

Names returns sorted field names, for error messages and docs.

func (*Registry) Validate

func (r *Registry) Validate(n Node) error

Validate checks the AST against the registry and coerces every value in place (Comparison.Value). Bare terms are rejected here — the grammar accepts them, the v1 vocabulary does not. Because coercion mutates the nodes, do not Validate the same AST from concurrent goroutines; see the Registry doc comment.

Jump to

Keyboard shortcuts

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