builder

package
v0.14.1 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2025 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package builder provides cross-database query building utilities. This package implements vendor-specific SQL generation and identifier handling for PostgreSQL, Oracle, and other database backends.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type DeleteQueryBuilder added in v0.13.0

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

DeleteQueryBuilder provides a type-safe interface for building DELETE queries with Filter API support.

func (*DeleteQueryBuilder) Limit added in v0.13.0

Limit sets the maximum number of rows to delete. Note: LIMIT in DELETE is not standard SQL and may not be supported by all databases.

func (*DeleteQueryBuilder) OrderBy added in v0.13.0

func (dqb *DeleteQueryBuilder) OrderBy(orderBys ...string) dbtypes.DeleteQueryBuilder

OrderBy adds ORDER BY clauses to the DELETE statement. Note: ORDER BY in DELETE is not standard SQL and may not be supported by all databases.

func (*DeleteQueryBuilder) ToSQL added in v0.13.0

func (dqb *DeleteQueryBuilder) ToSQL() (sql string, args []any, err error)

ToSQL generates the final SQL query and arguments.

func (*DeleteQueryBuilder) Where added in v0.13.0

Where adds a filter to the WHERE clause. Multiple calls to Where() will be combined with AND logic.

type Filter added in v0.13.0

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

Filter represents a composable WHERE clause filter that can be combined with AND/OR/NOT operators. Filters are created through FilterFactory methods and maintain vendor-specific quoting rules.

Note: Filter does NOT apply placeholder formatting (?, $1, :1) - that's handled by squirrel's StatementBuilder when the full query is built. This ensures proper sequential numbering across multiple WHERE clauses.

func (Filter) ToSQL added in v0.13.0

func (f Filter) ToSQL() (sql string, args []any, err error)

ToSQL is a convenience method with idiomatic Go naming (uppercase SQL). It delegates to ToSql() for actual implementation.

func (Filter) ToSql added in v0.13.0

func (f Filter) ToSql() (sql string, args []any, err error)

ToSql generates the SQL fragment and arguments for this filter. This method implements the squirrel.Sqlizer interface (inherited by dbtypes.Filter).

type FilterFactory added in v0.13.0

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

FilterFactory provides methods for creating type-safe filters with automatic vendor-specific quoting. Obtain a FilterFactory through QueryBuilder.Filter().

func (*FilterFactory) And added in v0.13.0

func (ff *FilterFactory) And(filters ...dbtypes.Filter) dbtypes.Filter

And combines multiple filters with AND logic. Returns a filter that matches when ALL provided filters match. Nil filters are treated as no-ops and skipped.

Example:

f := qb.Filter()
filter := f.And(
    f.Eq("status", "active"),
    f.Gt("age", 18),
)

func (*FilterFactory) Between added in v0.13.0

func (ff *FilterFactory) Between(column string, lowerBound, upperBound any) dbtypes.Filter

Between creates a BETWEEN filter (column BETWEEN lowerBound AND upperBound). Column names are automatically quoted according to database vendor rules.

func (*FilterFactory) Eq added in v0.13.0

func (ff *FilterFactory) Eq(column string, value any) dbtypes.Filter

Eq creates an equality filter (column = value). Column names are automatically quoted according to database vendor rules.

func (*FilterFactory) Gt added in v0.13.0

func (ff *FilterFactory) Gt(column string, value any) dbtypes.Filter

Gt creates a greater-than filter (column > value). Column names are automatically quoted according to database vendor rules.

func (*FilterFactory) Gte added in v0.13.0

func (ff *FilterFactory) Gte(column string, value any) dbtypes.Filter

Gte creates a greater-than-or-equal filter (column >= value). Column names are automatically quoted according to database vendor rules.

func (*FilterFactory) In added in v0.13.0

func (ff *FilterFactory) In(column string, values any) dbtypes.Filter

In creates an IN filter (column IN (values...)). Accepts both slices and scalar values. Scalars are automatically wrapped in a slice. Column names are automatically quoted according to database vendor rules.

Examples:

f.In("status", []string{"active", "pending"})  // IN with multiple values
f.In("status", "active")                       // IN with single value (wrapped automatically)

func (*FilterFactory) Like added in v0.13.0

func (ff *FilterFactory) Like(column, pattern string) dbtypes.Filter

Like creates a case-insensitive LIKE filter. This uses vendor-specific case-insensitive logic: - PostgreSQL: Uses ILIKE operator - Oracle: Uses UPPER() function on both column and value - Other vendors: Uses standard LIKE

func (*FilterFactory) Lt added in v0.13.0

func (ff *FilterFactory) Lt(column string, value any) dbtypes.Filter

Lt creates a less-than filter (column < value). Column names are automatically quoted according to database vendor rules.

func (*FilterFactory) Lte added in v0.13.0

func (ff *FilterFactory) Lte(column string, value any) dbtypes.Filter

Lte creates a less-than-or-equal filter (column <= value). Column names are automatically quoted according to database vendor rules.

func (*FilterFactory) Not added in v0.13.0

func (ff *FilterFactory) Not(filter dbtypes.Filter) dbtypes.Filter

Not negates a filter. Returns a filter that matches when the provided filter does NOT match.

Example:

f := qb.Filter()
filter := f.Not(f.Eq("status", "deleted"))

func (*FilterFactory) NotEq added in v0.13.0

func (ff *FilterFactory) NotEq(column string, value any) dbtypes.Filter

NotEq creates a not-equal filter (column <> value). Column names are automatically quoted according to database vendor rules.

func (*FilterFactory) NotIn added in v0.13.0

func (ff *FilterFactory) NotIn(column string, values any) dbtypes.Filter

NotIn creates a NOT IN filter (column NOT IN (values...)). Accepts both slices and scalar values. Scalars are automatically wrapped in a slice. Column names are automatically quoted according to database vendor rules.

Examples:

f.NotIn("status", []string{"deleted", "banned"})  // NOT IN with multiple values
f.NotIn("status", "deleted")                      // NOT IN with single value (wrapped automatically)

func (*FilterFactory) NotNull added in v0.13.0

func (ff *FilterFactory) NotNull(column string) dbtypes.Filter

NotNull creates an IS NOT NULL filter. Column names are automatically quoted according to database vendor rules.

func (*FilterFactory) Null added in v0.13.0

func (ff *FilterFactory) Null(column string) dbtypes.Filter

Null creates an IS NULL filter. Column names are automatically quoted according to database vendor rules.

func (*FilterFactory) Or added in v0.13.0

func (ff *FilterFactory) Or(filters ...dbtypes.Filter) dbtypes.Filter

Or combines multiple filters with OR logic. Returns a filter that matches when ANY provided filter matches. Nil filters are treated as no-ops and skipped.

Example:

f := qb.Filter()
filter := f.Or(
    f.Eq("status", "active"),
    f.Eq("role", "admin"),
)

func (*FilterFactory) Raw added in v0.13.0

func (ff *FilterFactory) Raw(condition string, args ...any) dbtypes.Filter

Raw creates a filter from raw SQL with manual placeholder handling.

WARNING: This method bypasses all identifier quoting and SQL injection protection. It is the caller's responsibility to:

  • Properly quote any identifiers (especially Oracle reserved words like "number", "level", "size")
  • Ensure the SQL fragment is valid for the target database
  • Never concatenate user input directly into the condition string

Use this method ONLY when the type-safe methods cannot express your condition. For Oracle, remember to quote reserved words: Raw(`"number" = ?`, value)

Examples:

f.Raw(`"number" = ?`, accountNumber)  // Oracle reserved word
f.Raw(`ROWNUM <= ?`, 10)              // Oracle-specific syntax
f.Raw(`ST_Distance(location, ?) < ?`, point, radius) // Spatial queries

type JoinFilter added in v0.13.0

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

JoinFilter represents a composable JOIN ON condition that compares columns to other columns. JoinFilters are created through JoinFilterFactory methods and maintain vendor-specific quoting rules.

Unlike Filter (which compares columns to values with placeholders), JoinFilter compares columns to other columns directly in the SQL (e.g., "users.id = profiles.user_id").

func (JoinFilter) ToSQL added in v0.13.0

func (jf JoinFilter) ToSQL() (sql string, args []any, err error)

ToSQL is a convenience method with idiomatic Go naming (uppercase SQL). It delegates to ToSql() for actual implementation.

func (JoinFilter) ToSql added in v0.13.0

func (jf JoinFilter) ToSql() (sql string, args []any, err error)

ToSql generates the SQL fragment for this join condition. This method implements the squirrel.Sqlizer interface (inherited by dbtypes.JoinFilter).

type JoinFilterFactory added in v0.13.0

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

JoinFilterFactory provides methods for creating type-safe JOIN ON filters with automatic vendor-specific quoting. Obtain a JoinFilterFactory through QueryBuilder.JoinFilter().

func (*JoinFilterFactory) And added in v0.13.0

func (jff *JoinFilterFactory) And(filters ...dbtypes.JoinFilter) dbtypes.JoinFilter

And combines multiple join filters with AND logic. Returns a filter that matches when ALL provided filters match. Nil filters are treated as no-ops and skipped.

Example:

jf := qb.JoinFilter()
filter := jf.And(
    jf.EqColumn("users.id", "profiles.user_id"),
    jf.GtColumn("profiles.created_at", "users.created_at"),
)

func (*JoinFilterFactory) EqColumn added in v0.13.0

func (jff *JoinFilterFactory) EqColumn(leftColumn, rightColumn string) dbtypes.JoinFilter

EqColumn creates an equality join condition (leftColumn = rightColumn). Column names are automatically quoted according to database vendor rules.

Example:

jf.EqColumn("users.id", "profiles.user_id")  // users.id = profiles.user_id

func (*JoinFilterFactory) GtColumn added in v0.13.0

func (jff *JoinFilterFactory) GtColumn(leftColumn, rightColumn string) dbtypes.JoinFilter

GtColumn creates a greater-than join condition (leftColumn > rightColumn). Column names are automatically quoted according to database vendor rules.

func (*JoinFilterFactory) GteColumn added in v0.13.0

func (jff *JoinFilterFactory) GteColumn(leftColumn, rightColumn string) dbtypes.JoinFilter

GteColumn creates a greater-than-or-equal join condition (leftColumn >= rightColumn). Column names are automatically quoted according to database vendor rules.

func (*JoinFilterFactory) LtColumn added in v0.13.0

func (jff *JoinFilterFactory) LtColumn(leftColumn, rightColumn string) dbtypes.JoinFilter

LtColumn creates a less-than join condition (leftColumn < rightColumn). Column names are automatically quoted according to database vendor rules.

func (*JoinFilterFactory) LteColumn added in v0.13.0

func (jff *JoinFilterFactory) LteColumn(leftColumn, rightColumn string) dbtypes.JoinFilter

LteColumn creates a less-than-or-equal join condition (leftColumn <= rightColumn). Column names are automatically quoted according to database vendor rules.

func (*JoinFilterFactory) NotEqColumn added in v0.13.0

func (jff *JoinFilterFactory) NotEqColumn(leftColumn, rightColumn string) dbtypes.JoinFilter

NotEqColumn creates an inequality join condition (leftColumn != rightColumn). Column names are automatically quoted according to database vendor rules.

func (*JoinFilterFactory) Or added in v0.13.0

Or combines multiple join filters with OR logic. Returns a filter that matches when ANY provided filter matches. Nil filters are treated as no-ops and skipped.

Example:

jf := qb.JoinFilter()
filter := jf.Or(
    jf.EqColumn("users.primary_email", "contacts.email"),
    jf.EqColumn("users.secondary_email", "contacts.email"),
)

func (*JoinFilterFactory) Raw added in v0.13.0

func (jff *JoinFilterFactory) Raw(condition string, args ...any) dbtypes.JoinFilter

Raw creates a join filter from raw SQL with manual placeholder handling.

WARNING: This method bypasses all identifier quoting and SQL injection protection. It is the caller's responsibility to:

  • Properly quote any identifiers (especially Oracle reserved words)
  • Ensure the SQL fragment is valid for the target database
  • Never concatenate user input directly into the condition string

Use this method ONLY when the type-safe methods cannot express your JOIN condition.

Examples:

jf.Raw(`users.id = profiles.user_id AND profiles."type" = ?`, "primary")  // Mixed column comparison + value
jf.Raw(`ST_Distance(users.location, stores.location) < 1000`)            // Spatial functions

type QueryBuilder

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

QueryBuilder provides vendor-specific SQL query building. It wraps squirrel.StatementBuilderType with database-specific customizations for placeholder formats, identifier quoting, and function generation.

func NewQueryBuilder

func NewQueryBuilder(vendor dbtypes.Vendor) *QueryBuilder

NewQueryBuilder creates a new query builder for the specified database vendor. It configures placeholder formats and prepares for vendor-specific SQL generation.

func (*QueryBuilder) BuildBooleanValue

func (qb *QueryBuilder) BuildBooleanValue(value bool) any

BuildBooleanValue converts a Go boolean to the appropriate database representation

func (*QueryBuilder) BuildCaseInsensitiveLike

func (qb *QueryBuilder) BuildCaseInsensitiveLike(column, value string) squirrel.Sqlizer

BuildCaseInsensitiveLike creates a case-insensitive LIKE expression. The implementation varies by database vendor.

func (*QueryBuilder) BuildCurrentTimestamp

func (qb *QueryBuilder) BuildCurrentTimestamp() string

BuildCurrentTimestamp returns the current timestamp function for the database vendor

func (*QueryBuilder) BuildUUIDGeneration

func (qb *QueryBuilder) BuildUUIDGeneration() string

BuildUUIDGeneration returns the UUID generation function for the database vendor

func (*QueryBuilder) BuildUpsert

func (qb *QueryBuilder) BuildUpsert(table string, conflictColumns []string, insertColumns, updateColumns map[string]any) (query string, args []any, err error)

BuildUpsert creates an UPSERT/MERGE query using Oracle's MERGE statement. Oracle uses MERGE INTO ... USING ... ON ... WHEN MATCHED ... WHEN NOT MATCHED syntax.

func (*QueryBuilder) Delete

func (qb *QueryBuilder) Delete(table string) dbtypes.DeleteQueryBuilder

Delete creates a DELETE query builder for the specified table with Filter API support. The returned DeleteQueryBuilder provides type-safe filtering. Table names are automatically quoted according to database vendor rules to handle reserved words.

Example:

f := qb.Filter()
query := qb.Delete("users").Where(f.And(
    f.Eq("status", "deleted"),
    f.Lt("deleted_at", threshold),
))

func (*QueryBuilder) Eq added in v0.11.0

func (qb *QueryBuilder) Eq(column string, value any) squirrel.Eq

Eq creates an equality condition with proper column quoting for the database vendor

func (*QueryBuilder) EscapeIdentifier

func (qb *QueryBuilder) EscapeIdentifier(identifier string) string

EscapeIdentifier escapes a database identifier (table/column name) according to vendor rules

func (*QueryBuilder) Filter added in v0.13.0

func (qb *QueryBuilder) Filter() dbtypes.FilterFactory

Filter returns a FilterFactory for creating composable WHERE clause filters. The factory provides type-safe methods (Eq, Lt, Gt, etc.) that automatically handle vendor-specific column quoting, as well as composition methods (And, Or, Not).

Example:

f := qb.Filter()
query := qb.Select("*").From("users").Where(f.And(
    f.Eq("status", "active"),
    f.Gt("age", 18),
))

func (*QueryBuilder) Gt added in v0.11.0

func (qb *QueryBuilder) Gt(column string, value any) squirrel.Gt

Gt creates a greater-than condition with proper column quoting for the database vendor

func (*QueryBuilder) GtOrEq added in v0.11.0

func (qb *QueryBuilder) GtOrEq(column string, value any) squirrel.GtOrEq

GtOrEq creates a greater-than-or-equal condition with proper column quoting for the database vendor

func (*QueryBuilder) Insert

func (qb *QueryBuilder) Insert(table string) squirrel.InsertBuilder

Insert creates an INSERT query builder for the specified table

func (*QueryBuilder) InsertWithColumns

func (qb *QueryBuilder) InsertWithColumns(table string, columns ...string) squirrel.InsertBuilder

InsertWithColumns creates an INSERT query builder with pre-specified columns. It applies vendor-specific column quoting to the provided column list.

func (*QueryBuilder) JoinFilter added in v0.13.0

func (qb *QueryBuilder) JoinFilter() dbtypes.JoinFilterFactory

JoinFilter returns a JoinFilterFactory for creating composable JOIN ON conditions. The factory provides type-safe methods (EqColumn, LtColumn, GtColumn, etc.) for comparing columns to other columns (not values) with automatic vendor-specific quoting.

Example:

jf := qb.JoinFilter()
query := qb.Select("*").From("users").JoinOn("profiles", jf.And(
    jf.EqColumn("users.id", "profiles.user_id"),
    jf.GtColumn("profiles.created_at", "users.created_at"),
))

func (*QueryBuilder) Lt added in v0.11.0

func (qb *QueryBuilder) Lt(column string, value any) squirrel.Lt

Lt creates a less-than condition with proper column quoting for the database vendor

func (*QueryBuilder) LtOrEq added in v0.11.0

func (qb *QueryBuilder) LtOrEq(column string, value any) squirrel.LtOrEq

LtOrEq creates a less-than-or-equal condition with proper column quoting for the database vendor

func (*QueryBuilder) NotEq added in v0.11.0

func (qb *QueryBuilder) NotEq(column string, value any) squirrel.NotEq

NotEq creates a not-equal condition with proper column quoting for the database vendor

func (*QueryBuilder) Select

func (qb *QueryBuilder) Select(columns ...string) *SelectQueryBuilder

Select creates a SELECT query builder with vendor-specific column quoting. For Oracle, it applies identifier quoting to handle reserved words appropriately.

func (*QueryBuilder) Update

func (qb *QueryBuilder) Update(table string) dbtypes.UpdateQueryBuilder

Update creates an UPDATE query builder for the specified table with Filter API support. The returned UpdateQueryBuilder provides type-safe filtering and vendor-specific column quoting. Table names are automatically quoted according to database vendor rules to handle reserved words.

Example:

f := qb.Filter()
query := qb.Update("users").
    Set("status", "active").
    Set("updated_at", time.Now()).
    Where(f.Eq("id", 123))

func (*QueryBuilder) Vendor

func (qb *QueryBuilder) Vendor() string

Vendor returns the database vendor string

type SelectQueryBuilder added in v0.11.0

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

SelectQueryBuilder provides a type-safe interface for building SELECT queries with proper identifier quoting and vendor-specific optimizations.

func (*SelectQueryBuilder) CrossJoinOn added in v0.13.0

func (sqb *SelectQueryBuilder) CrossJoinOn(table string) dbtypes.SelectQueryBuilder

CrossJoinOn adds a CROSS JOIN clause to the query. Cross joins do not have ON conditions, so no JoinFilter is needed. The table name is automatically quoted according to vendor rules.

func (*SelectQueryBuilder) From added in v0.11.0

From specifies the table(s) to select from Table names are automatically quoted according to database vendor rules to handle reserved words.

func (*SelectQueryBuilder) GroupBy added in v0.11.0

func (sqb *SelectQueryBuilder) GroupBy(groupBys ...string) dbtypes.SelectQueryBuilder

GroupBy adds a GROUP BY clause to the query Column names are automatically quoted according to database vendor rules to handle reserved words. SQL expressions and functions (like COUNT(*)) are preserved without quoting.

func (*SelectQueryBuilder) Having added in v0.11.0

func (sqb *SelectQueryBuilder) Having(pred any, rest ...any) dbtypes.SelectQueryBuilder

Having adds a HAVING clause to the query

func (*SelectQueryBuilder) InnerJoinOn added in v0.13.0

func (sqb *SelectQueryBuilder) InnerJoinOn(table string, filter dbtypes.JoinFilter) dbtypes.SelectQueryBuilder

InnerJoinOn adds a type-safe INNER JOIN clause to the query using JoinFilter. The table name is automatically quoted according to vendor rules.

func (*SelectQueryBuilder) JoinOn added in v0.13.0

JoinOn adds a type-safe JOIN clause to the query using JoinFilter for column comparisons. The table name is automatically quoted according to vendor rules.

Example:

jf := qb.JoinFilter()
query.JoinOn("profiles", jf.EqColumn("users.id", "profiles.user_id"))

func (*SelectQueryBuilder) LeftJoinOn added in v0.13.0

func (sqb *SelectQueryBuilder) LeftJoinOn(table string, filter dbtypes.JoinFilter) dbtypes.SelectQueryBuilder

LeftJoinOn adds a type-safe LEFT JOIN clause to the query using JoinFilter. The table name is automatically quoted according to vendor rules.

func (*SelectQueryBuilder) Limit added in v0.11.0

Limit sets the LIMIT for the query

func (*SelectQueryBuilder) Offset added in v0.11.0

Offset sets the OFFSET for the query

func (*SelectQueryBuilder) OrderBy added in v0.11.0

func (sqb *SelectQueryBuilder) OrderBy(orderBys ...string) dbtypes.SelectQueryBuilder

OrderBy adds an ORDER BY clause to the query Column names are automatically quoted according to database vendor rules to handle reserved words. SQL expressions and functions (like COUNT(*)) are preserved without quoting.

func (*SelectQueryBuilder) Paginate added in v0.11.0

func (sqb *SelectQueryBuilder) Paginate(limit, offset uint64) dbtypes.SelectQueryBuilder

Paginate applies pagination to the query with vendor-specific syntax. Use limit=0 for no limit (with offset only), offset=0 for no offset (limit only). Oracle 12c+ will use OFFSET...FETCH syntax, others use LIMIT/OFFSET.

func (*SelectQueryBuilder) RightJoinOn added in v0.13.0

func (sqb *SelectQueryBuilder) RightJoinOn(table string, filter dbtypes.JoinFilter) dbtypes.SelectQueryBuilder

RightJoinOn adds a type-safe RIGHT JOIN clause to the query using JoinFilter. The table name is automatically quoted according to vendor rules.

func (*SelectQueryBuilder) ToSQL added in v0.11.0

func (sqb *SelectQueryBuilder) ToSQL() (sql string, args []any, err error)

ToSQL generates the final SQL query string and arguments. For Oracle, pagination uses OFFSET...FETCH syntax; for others, uses LIMIT/OFFSET.

func (*SelectQueryBuilder) Where added in v0.11.0

Where adds a filter to the WHERE clause. Multiple calls to Where() will be combined with AND logic.

Create filters using the FilterFactory obtained from QueryBuilder.Filter():

Simple condition:

f := qb.Filter()
query.Where(f.Eq("status", "active"))

Multiple conditions with AND:

f := qb.Filter()
query.Where(f.And(
    f.Eq("status", "active"),
    f.Gt("age", 18),
))

OR conditions:

f := qb.Filter()
query.Where(f.Or(
    f.Eq("status", "active"),
    f.Eq("role", "admin"),
))

Complex nested logic:

f := qb.Filter()
query.Where(f.And(
    f.Or(
        f.Eq("status", "active"),
        f.Eq("status", "pending"),
    ),
    f.Gt("balance", 1000),
))

type UpdateQueryBuilder added in v0.13.0

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

UpdateQueryBuilder provides a type-safe interface for building UPDATE queries with Filter API support and vendor-specific column quoting.

func (*UpdateQueryBuilder) Set added in v0.13.0

func (uqb *UpdateQueryBuilder) Set(column string, value any) dbtypes.UpdateQueryBuilder

Set sets a column to a value in the UPDATE statement. Column names are automatically quoted according to database vendor rules.

func (*UpdateQueryBuilder) SetMap added in v0.13.0

func (uqb *UpdateQueryBuilder) SetMap(clauses map[string]any) dbtypes.UpdateQueryBuilder

SetMap sets multiple columns to values in the UPDATE statement. Column names are automatically quoted according to database vendor rules.

func (*UpdateQueryBuilder) ToSQL added in v0.13.0

func (uqb *UpdateQueryBuilder) ToSQL() (sql string, args []any, err error)

ToSQL generates the final SQL query and arguments.

func (*UpdateQueryBuilder) Where added in v0.13.0

Where adds a filter to the WHERE clause. Multiple calls to Where() will be combined with AND logic.

Jump to

Keyboard shortcuts

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