search

package
v1.2.0-rc2 Latest Latest
Warning

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

Go to latest
Published: Jun 10, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package search implements the DataSet search DSL on top of metaquery.

A search string ("john status:active, !inactive") is parsed into an AST and compiled against a Builder's output columns into a single composite metaquery.Filter, which is ANDed into the wrapped query's WHERE clause via Apply. Type-driven defaults mean a zero Config already searches text columns (case-insensitive contains) and exact-matches typed columns by target; the Config only customizes overrides and virtual ("named") searches.

Column kinds map to default predicates: plain text → contains (global); int/float → comparison/range (targeted); enum and other specialized text-like types (uuid, inet, ...) → exact match (targeted); bool → equality; time has no default (use TimeFn). "Plain text" is text/varchar/char/name/citext; any other DBType on a string column is treated as exact-match (see EnumFn).

Value operators

Comparison, range, and wildcard operators are not part of the grammar — they ride inside the value string (`<`, `>`, `..`, `*` are valid value characters) and are interpreted during compilation (operators.go). Numeric columns understand them by default: "score:>=90", "score:10..99", "score:..50". Use TimeFn for date columns and WildcardFn for glob text ("name:foo*"); both are Fns assigned via Config.Fields. ParseValueOp exposes the primitive for custom Fns.

Grammar

The lexer/parser are generated by ANTLR 4.13.2 from the .g4 files in grammar/, which are copies of the single source of truth in the sibling `dataset` project (../dataset/src/main/antlr4). Keeping one grammar for both the Kotlin and Go targets guarantees parse fidelity. Regenerate with `make antlr` after editing the grammar copies. The generated *.go files carry a "Code generated ... DO NOT EDIT." header and must never be hand-edited.

Safety

  • Target names resolve against the Builder's whitelisted output columns; an unresolved `field:` never reaches SQL as an identifier (it falls back to a global literal term).
  • All user values are bound parameters (? placeholders renumbered at Build), never interpolated into SQL text.

Index

Constants

View Source
const (
	DataSetSearchLexerESCAPED_CHAR     = 1
	DataSetSearchLexerESCAPE           = 2
	DataSetSearchLexerNEGATE           = 3
	DataSetSearchLexerANY              = 4
	DataSetSearchLexerSTRING           = 5
	DataSetSearchLexerTARGET_SEPARATOR = 6
	DataSetSearchLexerTERM_OR          = 7
	DataSetSearchLexerTERM_GROUP_START = 8
	DataSetSearchLexerTERM_GROUP_END   = 9
	DataSetSearchLexerWS               = 10
	DataSetSearchLexerESCAPED          = 11
)

DataSetSearchLexer tokens.

View Source
const (
	DataSetSearchParserEOF              = antlr.TokenEOF
	DataSetSearchParserESCAPED_CHAR     = 1
	DataSetSearchParserESCAPE           = 2
	DataSetSearchParserNEGATE           = 3
	DataSetSearchParserANY              = 4
	DataSetSearchParserSTRING           = 5
	DataSetSearchParserTARGET_SEPARATOR = 6
	DataSetSearchParserTERM_OR          = 7
	DataSetSearchParserTERM_GROUP_START = 8
	DataSetSearchParserTERM_GROUP_END   = 9
	DataSetSearchParserWS               = 10
	DataSetSearchParserESCAPED          = 11
)

DataSetSearchParser tokens.

View Source
const (
	DataSetSearchParserRULE_search             = 0
	DataSetSearchParserRULE_simpleTerm         = 1
	DataSetSearchParserRULE_andTerm            = 2
	DataSetSearchParserRULE_orTerm             = 3
	DataSetSearchParserRULE_term               = 4
	DataSetSearchParserRULE_termTarget         = 5
	DataSetSearchParserRULE_termValueGroup     = 6
	DataSetSearchParserRULE_simpleValue        = 7
	DataSetSearchParserRULE_termValue          = 8
	DataSetSearchParserRULE_andValue           = 9
	DataSetSearchParserRULE_orValue            = 10
	DataSetSearchParserRULE_unprotectedOrValue = 11
)

DataSetSearchParser rules.

View Source
const DataSetSearchLexerESCAPED_MODE = 1

DataSetSearchLexerESCAPED_MODE is the DataSetSearchLexer mode.

Variables

View Source
var DataSetSearchLexerLexerStaticData struct {
	ChannelNames           []string
	ModeNames              []string
	LiteralNames           []string
	SymbolicNames          []string
	RuleNames              []string
	PredictionContextCache *antlr.PredictionContextCache
	// contains filtered or unexported fields
}
View Source
var DataSetSearchParserParserStaticData struct {
	LiteralNames           []string
	SymbolicNames          []string
	RuleNames              []string
	PredictionContextCache *antlr.PredictionContextCache
	// contains filtered or unexported fields
}

Functions

func Apply

func Apply(b *metaquery.Builder, query string, cfg Config) (rendered string, err error)

Apply parses query, compiles it against b's output columns + cfg, and ANDs the resulting predicate into b's WHERE clause. It returns the normalized (auto-escaped) search string for DataSetResponse.searchRendered.

A zero Config gives type-driven defaults (text columns contains-searchable & global; int/float/bool exact-match & targeted). Use the same call for the partition field — apply partition first, then search; both AND together.

Compilation errors are returned directly; column/op validation errors from the underlying builder surface later at b.Build().

func Capabilities

func Capabilities(cols []metaquery.Column, cfg Config) map[string]Capability

Capabilities reports per-column search capability for cols under cfg, reusing the exact resolution the compiler uses. Columns that aren't searchable are absent from the map (look up yields the zero Capability). Named/virtual targets are not columns and are not included.

func DataSetSearchLexerInit

func DataSetSearchLexerInit()

DataSetSearchLexerInit initializes any static state used to implement DataSetSearchLexer. By default the static state used to implement the lexer is lazily initialized during the first call to NewDataSetSearchLexer(). You can call this function if you wish to initialize the static state ahead of time.

func DataSetSearchParserInit

func DataSetSearchParserInit()

DataSetSearchParserInit initializes any static state used to implement DataSetSearchParser. By default the static state used to implement the parser is lazily initialized during the first call to NewDataSetSearchParser(). You can call this function if you wish to initialize the static state ahead of time.

func InitEmptyAndTermContext

func InitEmptyAndTermContext(p *AndTermContext)

func InitEmptyAndValueContext

func InitEmptyAndValueContext(p *AndValueContext)

func InitEmptyOrTermContext

func InitEmptyOrTermContext(p *OrTermContext)

func InitEmptyOrValueContext

func InitEmptyOrValueContext(p *OrValueContext)

func InitEmptySearchContext

func InitEmptySearchContext(p *SearchContext)

func InitEmptySimpleTermContext

func InitEmptySimpleTermContext(p *SimpleTermContext)

func InitEmptySimpleValueContext

func InitEmptySimpleValueContext(p *SimpleValueContext)

func InitEmptyTermContext

func InitEmptyTermContext(p *TermContext)

func InitEmptyTermTargetContext

func InitEmptyTermTargetContext(p *TermTargetContext)

func InitEmptyTermValueContext

func InitEmptyTermValueContext(p *TermValueContext)

func InitEmptyTermValueGroupContext

func InitEmptyTermValueGroupContext(p *TermValueGroupContext)

func InitEmptyUnprotectedOrValueContext

func InitEmptyUnprotectedOrValueContext(p *UnprotectedOrValueContext)

Types

type AndTermContext

type AndTermContext struct {
	antlr.BaseParserRuleContext
	// contains filtered or unexported fields
}

func NewAndTermContext

func NewAndTermContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *AndTermContext

func NewEmptyAndTermContext

func NewEmptyAndTermContext() *AndTermContext

func (*AndTermContext) AllWS

func (s *AndTermContext) AllWS() []antlr.TerminalNode

func (*AndTermContext) EnterRule

func (s *AndTermContext) EnterRule(listener antlr.ParseTreeListener)

func (*AndTermContext) ExitRule

func (s *AndTermContext) ExitRule(listener antlr.ParseTreeListener)

func (*AndTermContext) GetParser

func (s *AndTermContext) GetParser() antlr.Parser

func (*AndTermContext) GetRuleContext

func (s *AndTermContext) GetRuleContext() antlr.RuleContext

func (*AndTermContext) IsAndTermContext

func (*AndTermContext) IsAndTermContext()

func (*AndTermContext) Term

func (s *AndTermContext) Term() ITermContext

func (*AndTermContext) ToStringTree

func (s *AndTermContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string

func (*AndTermContext) WS

type AndValueContext

type AndValueContext struct {
	antlr.BaseParserRuleContext
	// contains filtered or unexported fields
}

func NewAndValueContext

func NewAndValueContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *AndValueContext

func NewEmptyAndValueContext

func NewEmptyAndValueContext() *AndValueContext

func (*AndValueContext) AllWS

func (s *AndValueContext) AllWS() []antlr.TerminalNode

func (*AndValueContext) EnterRule

func (s *AndValueContext) EnterRule(listener antlr.ParseTreeListener)

func (*AndValueContext) ExitRule

func (s *AndValueContext) ExitRule(listener antlr.ParseTreeListener)

func (*AndValueContext) GetParser

func (s *AndValueContext) GetParser() antlr.Parser

func (*AndValueContext) GetRuleContext

func (s *AndValueContext) GetRuleContext() antlr.RuleContext

func (*AndValueContext) IsAndValueContext

func (*AndValueContext) IsAndValueContext()

func (*AndValueContext) TermValue

func (s *AndValueContext) TermValue() ITermValueContext

func (*AndValueContext) ToStringTree

func (s *AndValueContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string

func (*AndValueContext) WS

type BaseDataSetSearchParserListener

type BaseDataSetSearchParserListener struct{}

BaseDataSetSearchParserListener is a complete listener for a parse tree produced by DataSetSearchParser.

func (*BaseDataSetSearchParserListener) EnterAndTerm

func (s *BaseDataSetSearchParserListener) EnterAndTerm(ctx *AndTermContext)

EnterAndTerm is called when production andTerm is entered.

func (*BaseDataSetSearchParserListener) EnterAndValue

func (s *BaseDataSetSearchParserListener) EnterAndValue(ctx *AndValueContext)

EnterAndValue is called when production andValue is entered.

func (*BaseDataSetSearchParserListener) EnterEveryRule

EnterEveryRule is called when any rule is entered.

func (*BaseDataSetSearchParserListener) EnterOrTerm

func (s *BaseDataSetSearchParserListener) EnterOrTerm(ctx *OrTermContext)

EnterOrTerm is called when production orTerm is entered.

func (*BaseDataSetSearchParserListener) EnterOrValue

func (s *BaseDataSetSearchParserListener) EnterOrValue(ctx *OrValueContext)

EnterOrValue is called when production orValue is entered.

func (*BaseDataSetSearchParserListener) EnterSearch

func (s *BaseDataSetSearchParserListener) EnterSearch(ctx *SearchContext)

EnterSearch is called when production search is entered.

func (*BaseDataSetSearchParserListener) EnterSimpleTerm

func (s *BaseDataSetSearchParserListener) EnterSimpleTerm(ctx *SimpleTermContext)

EnterSimpleTerm is called when production simpleTerm is entered.

func (*BaseDataSetSearchParserListener) EnterSimpleValue

func (s *BaseDataSetSearchParserListener) EnterSimpleValue(ctx *SimpleValueContext)

EnterSimpleValue is called when production simpleValue is entered.

func (*BaseDataSetSearchParserListener) EnterTerm

func (s *BaseDataSetSearchParserListener) EnterTerm(ctx *TermContext)

EnterTerm is called when production term is entered.

func (*BaseDataSetSearchParserListener) EnterTermTarget

func (s *BaseDataSetSearchParserListener) EnterTermTarget(ctx *TermTargetContext)

EnterTermTarget is called when production termTarget is entered.

func (*BaseDataSetSearchParserListener) EnterTermValue

func (s *BaseDataSetSearchParserListener) EnterTermValue(ctx *TermValueContext)

EnterTermValue is called when production termValue is entered.

func (*BaseDataSetSearchParserListener) EnterTermValueGroup

func (s *BaseDataSetSearchParserListener) EnterTermValueGroup(ctx *TermValueGroupContext)

EnterTermValueGroup is called when production termValueGroup is entered.

func (*BaseDataSetSearchParserListener) EnterUnprotectedOrValue

func (s *BaseDataSetSearchParserListener) EnterUnprotectedOrValue(ctx *UnprotectedOrValueContext)

EnterUnprotectedOrValue is called when production unprotectedOrValue is entered.

func (*BaseDataSetSearchParserListener) ExitAndTerm

func (s *BaseDataSetSearchParserListener) ExitAndTerm(ctx *AndTermContext)

ExitAndTerm is called when production andTerm is exited.

func (*BaseDataSetSearchParserListener) ExitAndValue

func (s *BaseDataSetSearchParserListener) ExitAndValue(ctx *AndValueContext)

ExitAndValue is called when production andValue is exited.

func (*BaseDataSetSearchParserListener) ExitEveryRule

ExitEveryRule is called when any rule is exited.

func (*BaseDataSetSearchParserListener) ExitOrTerm

func (s *BaseDataSetSearchParserListener) ExitOrTerm(ctx *OrTermContext)

ExitOrTerm is called when production orTerm is exited.

func (*BaseDataSetSearchParserListener) ExitOrValue

func (s *BaseDataSetSearchParserListener) ExitOrValue(ctx *OrValueContext)

ExitOrValue is called when production orValue is exited.

func (*BaseDataSetSearchParserListener) ExitSearch

func (s *BaseDataSetSearchParserListener) ExitSearch(ctx *SearchContext)

ExitSearch is called when production search is exited.

func (*BaseDataSetSearchParserListener) ExitSimpleTerm

func (s *BaseDataSetSearchParserListener) ExitSimpleTerm(ctx *SimpleTermContext)

ExitSimpleTerm is called when production simpleTerm is exited.

func (*BaseDataSetSearchParserListener) ExitSimpleValue

func (s *BaseDataSetSearchParserListener) ExitSimpleValue(ctx *SimpleValueContext)

ExitSimpleValue is called when production simpleValue is exited.

func (*BaseDataSetSearchParserListener) ExitTerm

func (s *BaseDataSetSearchParserListener) ExitTerm(ctx *TermContext)

ExitTerm is called when production term is exited.

func (*BaseDataSetSearchParserListener) ExitTermTarget

func (s *BaseDataSetSearchParserListener) ExitTermTarget(ctx *TermTargetContext)

ExitTermTarget is called when production termTarget is exited.

func (*BaseDataSetSearchParserListener) ExitTermValue

func (s *BaseDataSetSearchParserListener) ExitTermValue(ctx *TermValueContext)

ExitTermValue is called when production termValue is exited.

func (*BaseDataSetSearchParserListener) ExitTermValueGroup

func (s *BaseDataSetSearchParserListener) ExitTermValueGroup(ctx *TermValueGroupContext)

ExitTermValueGroup is called when production termValueGroup is exited.

func (*BaseDataSetSearchParserListener) ExitUnprotectedOrValue

func (s *BaseDataSetSearchParserListener) ExitUnprotectedOrValue(ctx *UnprotectedOrValueContext)

ExitUnprotectedOrValue is called when production unprotectedOrValue is exited.

func (*BaseDataSetSearchParserListener) VisitErrorNode

func (s *BaseDataSetSearchParserListener) VisitErrorNode(node antlr.ErrorNode)

VisitErrorNode is called when an error node is visited.

func (*BaseDataSetSearchParserListener) VisitTerminal

func (s *BaseDataSetSearchParserListener) VisitTerminal(node antlr.TerminalNode)

VisitTerminal is called when a terminal node is visited.

type Capability

type Capability struct {
	Searchable bool // reachable by search (global term or field:value)
	Global     bool // matched by unqualified (free-text) terms
}

Capability describes how a column participates in search under a Config.

type Col

type Col struct {
	Column metaquery.Column
	// contains filtered or unexported fields
}

Col is the column handle passed to a search Fn. Its methods build metaquery.Filters bound to the column, so a custom search reads like dataset's `search { f, s -> f.eq(...) }`. For virtual (Named) searches Col is the zero value (Column.Name == ""); use Expr to build a raw predicate.

func (Col) Between

func (c Col) Between(lo, hi any) *metaquery.Filter

Between builds `<col> BETWEEN ? AND ?` (inclusive), with lo and hi bound as parameters.

func (Col) Contains

func (c Col) Contains(v string) *metaquery.Filter

Contains builds a case-insensitive substring match. Postgres uses ILIKE; SQLite uses LIKE (case-insensitive for ASCII by default). LIKE metacharacters in v are escaped so user input can't inject wildcards.

func (Col) Eq

func (c Col) Eq(v any) *metaquery.Filter

Comparison helpers. Each yields `<col> <op> ?` with v bound as a parameter.

func (Col) Expr

func (c Col) Expr(expr string, args ...any) *metaquery.Filter

Expr builds a raw predicate with ? placeholders (renumbered at Build). This is the escape hatch for virtual searches and computed predicates; the caller owns its SQL safety, but values should still be passed as args, not inlined.

func (Col) Ge

func (c Col) Ge(v any) *metaquery.Filter

func (Col) Gt

func (c Col) Gt(v any) *metaquery.Filter

func (Col) IsNotNull

func (c Col) IsNotNull() *metaquery.Filter

func (Col) IsNull

func (c Col) IsNull() *metaquery.Filter

IsNull / IsNotNull yield value-less predicates.

func (Col) Kind

func (c Col) Kind() string

Kind is the coarse type kind ("text"/"int"/"bool"/...) of the column.

func (Col) Le

func (c Col) Le(v any) *metaquery.Filter

func (Col) Lt

func (c Col) Lt(v any) *metaquery.Filter

func (Col) Name

func (c Col) Name() string

Name is the column's whitelisted name.

func (Col) Ne

func (c Col) Ne(v any) *metaquery.Filter

type Config

type Config struct {
	Fields map[string]Field // by column Name
	Named  map[string]Named // by virtual target name
}

Config customizes search compilation. The zero Config yields sensible type-driven behavior: text columns are contains-searchable and global; int, float and bool columns are exact-match and targeted-only; other kinds are not searched. Target and alias resolution is case-insensitive.

type Conjunction

type Conjunction int

Conjunction is how a term (or a value within a term) combines with the next.

const (
	And Conjunction = iota
	Or
)

type DataSetSearchLexer

type DataSetSearchLexer struct {
	*antlr.BaseLexer
	// contains filtered or unexported fields
}

func NewDataSetSearchLexer

func NewDataSetSearchLexer(input antlr.CharStream) *DataSetSearchLexer

NewDataSetSearchLexer produces a new lexer instance for the optional input antlr.CharStream.

type DataSetSearchParser

type DataSetSearchParser struct {
	*antlr.BaseParser
}

func NewDataSetSearchParser

func NewDataSetSearchParser(input antlr.TokenStream) *DataSetSearchParser

NewDataSetSearchParser produces a new parser instance for the optional input antlr.TokenStream.

func (*DataSetSearchParser) AndTerm

func (p *DataSetSearchParser) AndTerm() (localctx IAndTermContext)

func (*DataSetSearchParser) AndValue

func (p *DataSetSearchParser) AndValue() (localctx IAndValueContext)

func (*DataSetSearchParser) OrTerm

func (p *DataSetSearchParser) OrTerm() (localctx IOrTermContext)

func (*DataSetSearchParser) OrValue

func (p *DataSetSearchParser) OrValue() (localctx IOrValueContext)

func (*DataSetSearchParser) Search

func (p *DataSetSearchParser) Search() (localctx ISearchContext)

func (*DataSetSearchParser) SimpleTerm

func (p *DataSetSearchParser) SimpleTerm() (localctx ISimpleTermContext)

func (*DataSetSearchParser) SimpleValue

func (p *DataSetSearchParser) SimpleValue() (localctx ISimpleValueContext)

func (*DataSetSearchParser) Term

func (p *DataSetSearchParser) Term() (localctx ITermContext)

func (*DataSetSearchParser) TermTarget

func (p *DataSetSearchParser) TermTarget() (localctx ITermTargetContext)

func (*DataSetSearchParser) TermValue

func (p *DataSetSearchParser) TermValue() (localctx ITermValueContext)

func (*DataSetSearchParser) TermValueGroup

func (p *DataSetSearchParser) TermValueGroup() (localctx ITermValueGroupContext)

func (*DataSetSearchParser) UnprotectedOrValue

func (p *DataSetSearchParser) UnprotectedOrValue() (localctx IUnprotectedOrValueContext)

type DataSetSearchParserListener

type DataSetSearchParserListener interface {
	antlr.ParseTreeListener

	// EnterSearch is called when entering the search production.
	EnterSearch(c *SearchContext)

	// EnterSimpleTerm is called when entering the simpleTerm production.
	EnterSimpleTerm(c *SimpleTermContext)

	// EnterAndTerm is called when entering the andTerm production.
	EnterAndTerm(c *AndTermContext)

	// EnterOrTerm is called when entering the orTerm production.
	EnterOrTerm(c *OrTermContext)

	// EnterTerm is called when entering the term production.
	EnterTerm(c *TermContext)

	// EnterTermTarget is called when entering the termTarget production.
	EnterTermTarget(c *TermTargetContext)

	// EnterTermValueGroup is called when entering the termValueGroup production.
	EnterTermValueGroup(c *TermValueGroupContext)

	// EnterSimpleValue is called when entering the simpleValue production.
	EnterSimpleValue(c *SimpleValueContext)

	// EnterTermValue is called when entering the termValue production.
	EnterTermValue(c *TermValueContext)

	// EnterAndValue is called when entering the andValue production.
	EnterAndValue(c *AndValueContext)

	// EnterOrValue is called when entering the orValue production.
	EnterOrValue(c *OrValueContext)

	// EnterUnprotectedOrValue is called when entering the unprotectedOrValue production.
	EnterUnprotectedOrValue(c *UnprotectedOrValueContext)

	// ExitSearch is called when exiting the search production.
	ExitSearch(c *SearchContext)

	// ExitSimpleTerm is called when exiting the simpleTerm production.
	ExitSimpleTerm(c *SimpleTermContext)

	// ExitAndTerm is called when exiting the andTerm production.
	ExitAndTerm(c *AndTermContext)

	// ExitOrTerm is called when exiting the orTerm production.
	ExitOrTerm(c *OrTermContext)

	// ExitTerm is called when exiting the term production.
	ExitTerm(c *TermContext)

	// ExitTermTarget is called when exiting the termTarget production.
	ExitTermTarget(c *TermTargetContext)

	// ExitTermValueGroup is called when exiting the termValueGroup production.
	ExitTermValueGroup(c *TermValueGroupContext)

	// ExitSimpleValue is called when exiting the simpleValue production.
	ExitSimpleValue(c *SimpleValueContext)

	// ExitTermValue is called when exiting the termValue production.
	ExitTermValue(c *TermValueContext)

	// ExitAndValue is called when exiting the andValue production.
	ExitAndValue(c *AndValueContext)

	// ExitOrValue is called when exiting the orValue production.
	ExitOrValue(c *OrValueContext)

	// ExitUnprotectedOrValue is called when exiting the unprotectedOrValue production.
	ExitUnprotectedOrValue(c *UnprotectedOrValueContext)
}

DataSetSearchParserListener is a complete listener for a parse tree produced by DataSetSearchParser.

type Field

type Field struct {
	Search  Fn       // override the type-default predicate; nil keeps the default
	Scope   Scope    // global vs targeted-only participation
	Aliases []string // extra names usable as the field: target
	Disable bool     // exclude this column from search entirely
}

Field overrides how one result column (keyed by Name in Config.Fields) is searched. The zero Field keeps the type-driven defaults, so callers only set what differs.

type Fn

type Fn func(c Col, value string) (*metaquery.Filter, error)

Fn turns one search value into a predicate bound to a column. Return (nil, nil) to contribute nothing — e.g. an int column given an unparseable value drops that value rather than failing the whole search (mirrors dataset's `Condition?` null). A non-nil error aborts the whole search.

For column searches, c is the resolved Col; for Named (virtual) searches, c is the zero Col and the Fn builds a raw predicate via c.Expr.

func CompareFn

func CompareFn(parse func(string) (any, error)) Fn

CompareFn builds an Fn for an ordered column (int/float/time/...) that understands comparison (<, <=, >, >=, =, !=) and range (a..b, ..b, a..) operators. parse coerces an operand string to a bound value; a parse failure drops the value (returns nil filter, like the type defaults). A bare value is equality.

func EnumFn

func EnumFn() Fn

EnumFn builds an exact-match Fn for an enum (or other specialized text-like) column. On Postgres it compares the column cast to text — `col::text = $` — rather than casting the user's value to the enum type (`$::mood`), because an invalid enum input would raise a query error instead of simply not matching. On SQLite (no enum types) it is a plain `col = $`.

It is the type-driven default for text-kind columns whose DBType isn't a plain text type; assign it explicitly via Config.Fields for enum columns that sqlc emits as a custom Go type (which read as the "any" kind).

func TimeFn

func TimeFn(layouts ...string) Fn

TimeFn builds a comparison/range Fn for a time column, parsing operands with the given layouts (defaults: RFC3339 and date-only "2006-01-02"). Unlocks the time kind, which has no type-driven default (date formats are app-specific).

func WildcardFn

func WildcardFn() Fn

WildcardFn builds a text Fn where * is a glob and a leading = forces exact match: "foo*" prefix, "*foo" suffix, "*foo*"/"foo" contains, "=foo" exact. Wildcard metacharacters in the value are LIKE-escaped. Postgres uses ILIKE, SQLite LIKE.

type IAndTermContext

type IAndTermContext interface {
	antlr.ParserRuleContext

	// GetParser returns the parser.
	GetParser() antlr.Parser

	// Getter signatures
	Term() ITermContext
	AllWS() []antlr.TerminalNode
	WS(i int) antlr.TerminalNode

	// IsAndTermContext differentiates from other interfaces.
	IsAndTermContext()
}

IAndTermContext is an interface to support dynamic dispatch.

type IAndValueContext

type IAndValueContext interface {
	antlr.ParserRuleContext

	// GetParser returns the parser.
	GetParser() antlr.Parser

	// Getter signatures
	TermValue() ITermValueContext
	AllWS() []antlr.TerminalNode
	WS(i int) antlr.TerminalNode

	// IsAndValueContext differentiates from other interfaces.
	IsAndValueContext()
}

IAndValueContext is an interface to support dynamic dispatch.

type IOrTermContext

type IOrTermContext interface {
	antlr.ParserRuleContext

	// GetParser returns the parser.
	GetParser() antlr.Parser

	// Getter signatures
	TERM_OR() antlr.TerminalNode
	Term() ITermContext
	AllWS() []antlr.TerminalNode
	WS(i int) antlr.TerminalNode

	// IsOrTermContext differentiates from other interfaces.
	IsOrTermContext()
}

IOrTermContext is an interface to support dynamic dispatch.

type IOrValueContext

type IOrValueContext interface {
	antlr.ParserRuleContext

	// GetParser returns the parser.
	GetParser() antlr.Parser

	// Getter signatures
	TERM_OR() antlr.TerminalNode
	TermValue() ITermValueContext
	AllWS() []antlr.TerminalNode
	WS(i int) antlr.TerminalNode

	// IsOrValueContext differentiates from other interfaces.
	IsOrValueContext()
}

IOrValueContext is an interface to support dynamic dispatch.

type ISearchContext

type ISearchContext interface {
	antlr.ParserRuleContext

	// GetParser returns the parser.
	GetParser() antlr.Parser

	// Getter signatures
	EOF() antlr.TerminalNode
	SimpleTerm() ISimpleTermContext
	AllWS() []antlr.TerminalNode
	WS(i int) antlr.TerminalNode
	TERM_OR() antlr.TerminalNode
	AllAndTerm() []IAndTermContext
	AndTerm(i int) IAndTermContext
	AllOrTerm() []IOrTermContext
	OrTerm(i int) IOrTermContext

	// IsSearchContext differentiates from other interfaces.
	IsSearchContext()
}

ISearchContext is an interface to support dynamic dispatch.

type ISimpleTermContext

type ISimpleTermContext interface {
	antlr.ParserRuleContext

	// GetParser returns the parser.
	GetParser() antlr.Parser

	// Getter signatures
	Term() ITermContext
	AllWS() []antlr.TerminalNode
	WS(i int) antlr.TerminalNode

	// IsSimpleTermContext differentiates from other interfaces.
	IsSimpleTermContext()
}

ISimpleTermContext is an interface to support dynamic dispatch.

type ISimpleValueContext

type ISimpleValueContext interface {
	antlr.ParserRuleContext

	// GetParser returns the parser.
	GetParser() antlr.Parser

	// Getter signatures
	TermValue() ITermValueContext

	// IsSimpleValueContext differentiates from other interfaces.
	IsSimpleValueContext()
}

ISimpleValueContext is an interface to support dynamic dispatch.

type ITermContext

type ITermContext interface {
	antlr.ParserRuleContext

	// GetParser returns the parser.
	GetParser() antlr.Parser

	// Getter signatures
	TermValueGroup() ITermValueGroupContext
	TermTarget() ITermTargetContext
	TARGET_SEPARATOR() antlr.TerminalNode
	NEGATE() antlr.TerminalNode
	AllWS() []antlr.TerminalNode
	WS(i int) antlr.TerminalNode

	// IsTermContext differentiates from other interfaces.
	IsTermContext()
}

ITermContext is an interface to support dynamic dispatch.

type ITermTargetContext

type ITermTargetContext interface {
	antlr.ParserRuleContext

	// GetParser returns the parser.
	GetParser() antlr.Parser

	// Getter signatures
	ANY() antlr.TerminalNode
	STRING() antlr.TerminalNode

	// IsTermTargetContext differentiates from other interfaces.
	IsTermTargetContext()
}

ITermTargetContext is an interface to support dynamic dispatch.

type ITermValueContext

type ITermValueContext interface {
	antlr.ParserRuleContext

	// GetParser returns the parser.
	GetParser() antlr.Parser

	// Getter signatures
	STRING() antlr.TerminalNode
	ANY() antlr.TerminalNode
	ESCAPED_CHAR() antlr.TerminalNode
	NEGATE() antlr.TerminalNode

	// IsTermValueContext differentiates from other interfaces.
	IsTermValueContext()
}

ITermValueContext is an interface to support dynamic dispatch.

type ITermValueGroupContext

type ITermValueGroupContext interface {
	antlr.ParserRuleContext

	// GetParser returns the parser.
	GetParser() antlr.Parser

	// Getter signatures
	TERM_GROUP_START() antlr.TerminalNode
	TERM_GROUP_END() antlr.TerminalNode
	AllWS() []antlr.TerminalNode
	WS(i int) antlr.TerminalNode
	SimpleValue() ISimpleValueContext
	AllAndValue() []IAndValueContext
	AndValue(i int) IAndValueContext
	AllOrValue() []IOrValueContext
	OrValue(i int) IOrValueContext
	AllUnprotectedOrValue() []IUnprotectedOrValueContext
	UnprotectedOrValue(i int) IUnprotectedOrValueContext

	// IsTermValueGroupContext differentiates from other interfaces.
	IsTermValueGroupContext()
}

ITermValueGroupContext is an interface to support dynamic dispatch.

type IUnprotectedOrValueContext

type IUnprotectedOrValueContext interface {
	antlr.ParserRuleContext

	// GetParser returns the parser.
	GetParser() antlr.Parser

	// Getter signatures
	TERM_OR() antlr.TerminalNode
	TermValue() ITermValueContext

	// IsUnprotectedOrValueContext differentiates from other interfaces.
	IsUnprotectedOrValueContext()
}

IUnprotectedOrValueContext is an interface to support dynamic dispatch.

type Named

type Named struct {
	Search Fn   // required; receives the zero Col
	Global bool // participate in unqualified terms (default targeted-only)
}

Named is a virtual search target not bound to a result column (dataset's `search(name) { ... }` — e.g. "daysAgo", "onlyMine"). Search is required.

type OrTermContext

type OrTermContext struct {
	antlr.BaseParserRuleContext
	// contains filtered or unexported fields
}

func NewEmptyOrTermContext

func NewEmptyOrTermContext() *OrTermContext

func NewOrTermContext

func NewOrTermContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *OrTermContext

func (*OrTermContext) AllWS

func (s *OrTermContext) AllWS() []antlr.TerminalNode

func (*OrTermContext) EnterRule

func (s *OrTermContext) EnterRule(listener antlr.ParseTreeListener)

func (*OrTermContext) ExitRule

func (s *OrTermContext) ExitRule(listener antlr.ParseTreeListener)

func (*OrTermContext) GetParser

func (s *OrTermContext) GetParser() antlr.Parser

func (*OrTermContext) GetRuleContext

func (s *OrTermContext) GetRuleContext() antlr.RuleContext

func (*OrTermContext) IsOrTermContext

func (*OrTermContext) IsOrTermContext()

func (*OrTermContext) TERM_OR

func (s *OrTermContext) TERM_OR() antlr.TerminalNode

func (*OrTermContext) Term

func (s *OrTermContext) Term() ITermContext

func (*OrTermContext) ToStringTree

func (s *OrTermContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string

func (*OrTermContext) WS

type OrValueContext

type OrValueContext struct {
	antlr.BaseParserRuleContext
	// contains filtered or unexported fields
}

func NewEmptyOrValueContext

func NewEmptyOrValueContext() *OrValueContext

func NewOrValueContext

func NewOrValueContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *OrValueContext

func (*OrValueContext) AllWS

func (s *OrValueContext) AllWS() []antlr.TerminalNode

func (*OrValueContext) EnterRule

func (s *OrValueContext) EnterRule(listener antlr.ParseTreeListener)

func (*OrValueContext) ExitRule

func (s *OrValueContext) ExitRule(listener antlr.ParseTreeListener)

func (*OrValueContext) GetParser

func (s *OrValueContext) GetParser() antlr.Parser

func (*OrValueContext) GetRuleContext

func (s *OrValueContext) GetRuleContext() antlr.RuleContext

func (*OrValueContext) IsOrValueContext

func (*OrValueContext) IsOrValueContext()

func (*OrValueContext) TERM_OR

func (s *OrValueContext) TERM_OR() antlr.TerminalNode

func (*OrValueContext) TermValue

func (s *OrValueContext) TermValue() ITermValueContext

func (*OrValueContext) ToStringTree

func (s *OrValueContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string

func (*OrValueContext) WS

type Parsed

type Parsed struct {
	Search string // normalized input, suitable for DataSetResponse.searchRendered
	Terms  []Term
}

Parsed is the result of parsing a search string: the (possibly auto-escaped) normalized string and the ordered terms.

func Parse

func Parse(query string) (Parsed, error)

Parse parses a search string into terms, auto-escaping offending characters (by inserting a backslash before them) and retrying, up to maxRecover times. The returned Parsed.Search is the normalized (possibly escaped) string, which callers surface as DataSetResponse.searchRendered.

Faithful port of dataset's SearchParser.parse.

type Scope

type Scope int

Scope controls whether a column participates in unqualified ("global") terms.

const (
	// ScopeDefault: text columns are global, all others targeted-only.
	ScopeDefault  Scope = iota
	ScopeGlobal         // always searched in unqualified terms
	ScopeTargeted       // only matched via field:value
)

type SearchContext

type SearchContext struct {
	antlr.BaseParserRuleContext
	// contains filtered or unexported fields
}

func NewEmptySearchContext

func NewEmptySearchContext() *SearchContext

func NewSearchContext

func NewSearchContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *SearchContext

func (*SearchContext) AllAndTerm

func (s *SearchContext) AllAndTerm() []IAndTermContext

func (*SearchContext) AllOrTerm

func (s *SearchContext) AllOrTerm() []IOrTermContext

func (*SearchContext) AllWS

func (s *SearchContext) AllWS() []antlr.TerminalNode

func (*SearchContext) AndTerm

func (s *SearchContext) AndTerm(i int) IAndTermContext

func (*SearchContext) EOF

func (s *SearchContext) EOF() antlr.TerminalNode

func (*SearchContext) EnterRule

func (s *SearchContext) EnterRule(listener antlr.ParseTreeListener)

func (*SearchContext) ExitRule

func (s *SearchContext) ExitRule(listener antlr.ParseTreeListener)

func (*SearchContext) GetParser

func (s *SearchContext) GetParser() antlr.Parser

func (*SearchContext) GetRuleContext

func (s *SearchContext) GetRuleContext() antlr.RuleContext

func (*SearchContext) IsSearchContext

func (*SearchContext) IsSearchContext()

func (*SearchContext) OrTerm

func (s *SearchContext) OrTerm(i int) IOrTermContext

func (*SearchContext) SimpleTerm

func (s *SearchContext) SimpleTerm() ISimpleTermContext

func (*SearchContext) TERM_OR

func (s *SearchContext) TERM_OR() antlr.TerminalNode

func (*SearchContext) ToStringTree

func (s *SearchContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string

func (*SearchContext) WS

type SimpleTermContext

type SimpleTermContext struct {
	antlr.BaseParserRuleContext
	// contains filtered or unexported fields
}

func NewEmptySimpleTermContext

func NewEmptySimpleTermContext() *SimpleTermContext

func NewSimpleTermContext

func NewSimpleTermContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *SimpleTermContext

func (*SimpleTermContext) AllWS

func (s *SimpleTermContext) AllWS() []antlr.TerminalNode

func (*SimpleTermContext) EnterRule

func (s *SimpleTermContext) EnterRule(listener antlr.ParseTreeListener)

func (*SimpleTermContext) ExitRule

func (s *SimpleTermContext) ExitRule(listener antlr.ParseTreeListener)

func (*SimpleTermContext) GetParser

func (s *SimpleTermContext) GetParser() antlr.Parser

func (*SimpleTermContext) GetRuleContext

func (s *SimpleTermContext) GetRuleContext() antlr.RuleContext

func (*SimpleTermContext) IsSimpleTermContext

func (*SimpleTermContext) IsSimpleTermContext()

func (*SimpleTermContext) Term

func (s *SimpleTermContext) Term() ITermContext

func (*SimpleTermContext) ToStringTree

func (s *SimpleTermContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string

func (*SimpleTermContext) WS

type SimpleValueContext

type SimpleValueContext struct {
	antlr.BaseParserRuleContext
	// contains filtered or unexported fields
}

func NewEmptySimpleValueContext

func NewEmptySimpleValueContext() *SimpleValueContext

func NewSimpleValueContext

func NewSimpleValueContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *SimpleValueContext

func (*SimpleValueContext) EnterRule

func (s *SimpleValueContext) EnterRule(listener antlr.ParseTreeListener)

func (*SimpleValueContext) ExitRule

func (s *SimpleValueContext) ExitRule(listener antlr.ParseTreeListener)

func (*SimpleValueContext) GetParser

func (s *SimpleValueContext) GetParser() antlr.Parser

func (*SimpleValueContext) GetRuleContext

func (s *SimpleValueContext) GetRuleContext() antlr.RuleContext

func (*SimpleValueContext) IsSimpleValueContext

func (*SimpleValueContext) IsSimpleValueContext()

func (*SimpleValueContext) TermValue

func (s *SimpleValueContext) TermValue() ITermValueContext

func (*SimpleValueContext) ToStringTree

func (s *SimpleValueContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string

type Term

type Term struct {
	Target  string // "" = unqualified (global) term
	Negated bool
	Conj    Conjunction // relative to the next term
	Values  []TermValue
}

Term is one parsed search clause: an optional target ("field:"), a negation flag, the conjunction relative to the next term, and one or more values.

Mirrors dataset's com.iodesystems.db.search.model.Term so the two language targets stay structurally identical.

type TermContext

type TermContext struct {
	antlr.BaseParserRuleContext
	// contains filtered or unexported fields
}

func NewEmptyTermContext

func NewEmptyTermContext() *TermContext

func NewTermContext

func NewTermContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *TermContext

func (*TermContext) AllWS

func (s *TermContext) AllWS() []antlr.TerminalNode

func (*TermContext) EnterRule

func (s *TermContext) EnterRule(listener antlr.ParseTreeListener)

func (*TermContext) ExitRule

func (s *TermContext) ExitRule(listener antlr.ParseTreeListener)

func (*TermContext) GetParser

func (s *TermContext) GetParser() antlr.Parser

func (*TermContext) GetRuleContext

func (s *TermContext) GetRuleContext() antlr.RuleContext

func (*TermContext) IsTermContext

func (*TermContext) IsTermContext()

func (*TermContext) NEGATE

func (s *TermContext) NEGATE() antlr.TerminalNode

func (*TermContext) TARGET_SEPARATOR

func (s *TermContext) TARGET_SEPARATOR() antlr.TerminalNode

func (*TermContext) TermTarget

func (s *TermContext) TermTarget() ITermTargetContext

func (*TermContext) TermValueGroup

func (s *TermContext) TermValueGroup() ITermValueGroupContext

func (*TermContext) ToStringTree

func (s *TermContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string

func (*TermContext) WS

func (s *TermContext) WS(i int) antlr.TerminalNode

type TermTargetContext

type TermTargetContext struct {
	antlr.BaseParserRuleContext
	// contains filtered or unexported fields
}

func NewEmptyTermTargetContext

func NewEmptyTermTargetContext() *TermTargetContext

func NewTermTargetContext

func NewTermTargetContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *TermTargetContext

func (*TermTargetContext) ANY

func (*TermTargetContext) EnterRule

func (s *TermTargetContext) EnterRule(listener antlr.ParseTreeListener)

func (*TermTargetContext) ExitRule

func (s *TermTargetContext) ExitRule(listener antlr.ParseTreeListener)

func (*TermTargetContext) GetParser

func (s *TermTargetContext) GetParser() antlr.Parser

func (*TermTargetContext) GetRuleContext

func (s *TermTargetContext) GetRuleContext() antlr.RuleContext

func (*TermTargetContext) IsTermTargetContext

func (*TermTargetContext) IsTermTargetContext()

func (*TermTargetContext) STRING

func (s *TermTargetContext) STRING() antlr.TerminalNode

func (*TermTargetContext) ToStringTree

func (s *TermTargetContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string

type TermValue

type TermValue struct {
	Conj    Conjunction
	Value   string
	Negated bool
}

TermValue is a single value within a term, with its own negation and the conjunction relative to the previous value in the group.

type TermValueContext

type TermValueContext struct {
	antlr.BaseParserRuleContext
	// contains filtered or unexported fields
}

func NewEmptyTermValueContext

func NewEmptyTermValueContext() *TermValueContext

func NewTermValueContext

func NewTermValueContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *TermValueContext

func (*TermValueContext) ANY

func (*TermValueContext) ESCAPED_CHAR

func (s *TermValueContext) ESCAPED_CHAR() antlr.TerminalNode

func (*TermValueContext) EnterRule

func (s *TermValueContext) EnterRule(listener antlr.ParseTreeListener)

func (*TermValueContext) ExitRule

func (s *TermValueContext) ExitRule(listener antlr.ParseTreeListener)

func (*TermValueContext) GetParser

func (s *TermValueContext) GetParser() antlr.Parser

func (*TermValueContext) GetRuleContext

func (s *TermValueContext) GetRuleContext() antlr.RuleContext

func (*TermValueContext) IsTermValueContext

func (*TermValueContext) IsTermValueContext()

func (*TermValueContext) NEGATE

func (s *TermValueContext) NEGATE() antlr.TerminalNode

func (*TermValueContext) STRING

func (s *TermValueContext) STRING() antlr.TerminalNode

func (*TermValueContext) ToStringTree

func (s *TermValueContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string

type TermValueGroupContext

type TermValueGroupContext struct {
	antlr.BaseParserRuleContext
	// contains filtered or unexported fields
}

func NewEmptyTermValueGroupContext

func NewEmptyTermValueGroupContext() *TermValueGroupContext

func NewTermValueGroupContext

func NewTermValueGroupContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *TermValueGroupContext

func (*TermValueGroupContext) AllAndValue

func (s *TermValueGroupContext) AllAndValue() []IAndValueContext

func (*TermValueGroupContext) AllOrValue

func (s *TermValueGroupContext) AllOrValue() []IOrValueContext

func (*TermValueGroupContext) AllUnprotectedOrValue

func (s *TermValueGroupContext) AllUnprotectedOrValue() []IUnprotectedOrValueContext

func (*TermValueGroupContext) AllWS

func (*TermValueGroupContext) AndValue

func (*TermValueGroupContext) EnterRule

func (s *TermValueGroupContext) EnterRule(listener antlr.ParseTreeListener)

func (*TermValueGroupContext) ExitRule

func (s *TermValueGroupContext) ExitRule(listener antlr.ParseTreeListener)

func (*TermValueGroupContext) GetParser

func (s *TermValueGroupContext) GetParser() antlr.Parser

func (*TermValueGroupContext) GetRuleContext

func (s *TermValueGroupContext) GetRuleContext() antlr.RuleContext

func (*TermValueGroupContext) IsTermValueGroupContext

func (*TermValueGroupContext) IsTermValueGroupContext()

func (*TermValueGroupContext) OrValue

func (*TermValueGroupContext) SimpleValue

func (s *TermValueGroupContext) SimpleValue() ISimpleValueContext

func (*TermValueGroupContext) TERM_GROUP_END

func (s *TermValueGroupContext) TERM_GROUP_END() antlr.TerminalNode

func (*TermValueGroupContext) TERM_GROUP_START

func (s *TermValueGroupContext) TERM_GROUP_START() antlr.TerminalNode

func (*TermValueGroupContext) ToStringTree

func (s *TermValueGroupContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string

func (*TermValueGroupContext) UnprotectedOrValue

func (s *TermValueGroupContext) UnprotectedOrValue(i int) IUnprotectedOrValueContext

func (*TermValueGroupContext) WS

type UnprotectedOrValueContext

type UnprotectedOrValueContext struct {
	antlr.BaseParserRuleContext
	// contains filtered or unexported fields
}

func NewEmptyUnprotectedOrValueContext

func NewEmptyUnprotectedOrValueContext() *UnprotectedOrValueContext

func NewUnprotectedOrValueContext

func NewUnprotectedOrValueContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *UnprotectedOrValueContext

func (*UnprotectedOrValueContext) EnterRule

func (s *UnprotectedOrValueContext) EnterRule(listener antlr.ParseTreeListener)

func (*UnprotectedOrValueContext) ExitRule

func (s *UnprotectedOrValueContext) ExitRule(listener antlr.ParseTreeListener)

func (*UnprotectedOrValueContext) GetParser

func (s *UnprotectedOrValueContext) GetParser() antlr.Parser

func (*UnprotectedOrValueContext) GetRuleContext

func (s *UnprotectedOrValueContext) GetRuleContext() antlr.RuleContext

func (*UnprotectedOrValueContext) IsUnprotectedOrValueContext

func (*UnprotectedOrValueContext) IsUnprotectedOrValueContext()

func (*UnprotectedOrValueContext) TERM_OR

func (*UnprotectedOrValueContext) TermValue

func (*UnprotectedOrValueContext) ToStringTree

func (s *UnprotectedOrValueContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string

type ValueOp

type ValueOp struct {
	Op      metaquery.Op // "" when no comparison prefix
	Operand string
	Lo, Hi  string
	IsRange bool
}

ValueOp is a value string decomposed into an optional operator and operand(s).

"42"      -> {Operand: "42"}                 (no operator; caller defaults to =)
">=90"    -> {Op: OpGe, Operand: "90"}
"10..99"  -> {IsRange: true, Lo: "10", Hi: "99"}
"..99"    -> {IsRange: true, Hi: "99"}        (open low → <=)
"10.."    -> {IsRange: true, Lo: "10"}        (open high → >=)

func ParseValueOp

func ParseValueOp(raw string) ValueOp

ParseValueOp decomposes a raw value. Range (..) is detected first, then a leading comparison operator (two-char before one-char), else a plain operand.

Jump to

Keyboard shortcuts

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