Help us shape the future of Go! Take the 2026 Go Developer Survey

search

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: May 10, 2026 License: AGPL-3.0 Imports: 8 Imported by: 0

Documentation

Overview

Package search owns S28's search surface. Postgres FTS (`tsvector` + `pg_trgm`) backs everything; no external search engine. Visibility scoping flows through `policy.VisibilityPredicate` so every query is gated by the same rule the rest of the runtime uses.

Entry points are:

SearchRepos / SearchIssues / SearchUsers / SearchCode — per-type
    queries returning slices of result rows + the total count.
ParseQuery — splits a user query string into the FTS query plus
    operator filters (repo:, is:, author:, state:).

Index

Constants

View Source
const MaxQueryBytes = 256

MaxQueryBytes caps incoming query strings. Tighter than the markdown 1 MiB ceiling because no legitimate search ever needs even 1 KiB.

View Source
const PageSize = 20

PageSize is the per-type result count for the full results page. Quick-dropdown uses a smaller cap (see QuickResultsLimit).

View Source
const QuickResultsLimit = 5

QuickResultsLimit is the per-type cap for the top-bar quick dropdown. Same shape as GitHub's: tight for keystroke speed.

Variables

View Source
var (
	ErrEmptyQuery = errors.New("search: query is empty")
)

Errors surfaced to handlers.

Functions

This section is empty.

Types

type CodeResult

type CodeResult struct {
	RepoID        int64
	OwnerUsername string
	RepoName      string
	RefName       string
	Path          string
	// PreviewLine is a single line of content extracted near the
	// match, when content matched. Empty for path-only hits.
	PreviewLine string
	Rank        float64
}

CodeResult is one row from SearchCode. Either Path or Content (or both) is populated depending on which subquery hit.

func SearchCode

func SearchCode(ctx context.Context, deps Deps, actor policy.Actor, q ParsedQuery, limit, offset int) ([]CodeResult, int64, error)

SearchCode runs a code search across paths and content. Visibility gates the underlying repo set; only repos the actor can read appear.

We run two unioned subqueries:

paths   — `tsv @@ plainto_tsquery(...)` on the indexed path
          string. Always populated (size cap doesn't apply).
content — `content_tsv @@ plainto_tsquery(...)` OR
          `content_trgm % $tsText` (trigram similarity for
          camelCase / snake_case identifiers).

Path matches always rank above content matches at equal ts_rank. Within content matches, ts_rank dominates trigram similarity.

type Deps

type Deps struct {
	Pool   *pgxpool.Pool
	Logger *slog.Logger
}

Deps wires the package against the rest of the runtime.

type IssueResult

type IssueResult struct {
	ID            int64
	RepoID        int64
	OwnerUsername string
	RepoName      string
	Number        int64
	Title         string
	State         string
	Kind          string // "issue" | "pr"
	AuthorName    string
	UpdatedAt     time.Time
	Rank          float64
}

IssueResult is one row from SearchIssues.

func SearchIssues

func SearchIssues(ctx context.Context, deps Deps, actor policy.Actor, q ParsedQuery, kindFilter string, limit, offset int) ([]IssueResult, int64, error)

SearchIssues runs an issue search visible to actor. Issues inherit visibility from their repo via the same predicate.

Ranking: `ts_rank_cd * state_weight` where open weighs 1.5× over closed (the spec doesn't pin a number; 1.5 is a sensible default that surfaces actionable issues first without burying the closed history).

kindFilter is optional: pass "issue", "pr", or "" for both. The dropdown / Issues tab passes "issue"; the PR tab would pass "pr".

type ParsedQuery

type ParsedQuery struct {
	Text         string // free-text query (what tsvector matches against)
	Phrase       string // when a quoted phrase was supplied; empty when not
	RepoFilter   *RepoFilter
	StateFilter  string // "open" | "closed" | ""
	AuthorFilter string // username or empty
}

ParsedQuery is the result of running a raw user-typed query through ParseQuery. The free-text portion is what flows into `plainto_tsquery` / `phraseto_tsquery`; the operator fields are what compose the SQL `WHERE` filters.

`RepoFilter` carries the `owner/name` pair when the user typed `repo:owner/name`; both halves must be present for the filter to take effect (a bare `repo:foo` without slash is treated as free text).

func ParseQuery

func ParseQuery(raw string) ParsedQuery

ParseQuery splits a raw query string into the free-text portion + recognised operators. v1 supports `repo:`, `is:`, `state:`, `author:`. `is:` and `state:` are aliases.

A quoted run of tokens becomes the Phrase field (one quoted span per query in v1). The Text field excludes quoted phrases and operator tokens.

Operators and free-text tokens are space-delimited; the parser is intentionally tolerant — unknown prefixes (e.g. `language:Go`) fall through as free text so future operator additions don't break old queries.

func (ParsedQuery) HasContent

func (p ParsedQuery) HasContent() bool

HasContent reports whether the parsed query contains anything searchable (free text, phrase, or any operator).

type RepoFilter

type RepoFilter struct {
	Owner string
	Name  string
}

RepoFilter splits the `repo:owner/name` operator value.

type RepoResult

type RepoResult struct {
	ID            int64
	OwnerUsername string
	Name          string
	Description   string
	Visibility    string
	StarCount     int64
	UpdatedAt     time.Time
	Rank          float64
}

RepoResult is one row from SearchRepos.

func SearchRepos

func SearchRepos(ctx context.Context, deps Deps, actor policy.Actor, q ParsedQuery, limit, offset int) ([]RepoResult, int64, error)

SearchRepos runs a repo search visible to actor. limit / offset drive paging.

Ranking: `ts_rank_cd * (1 + ln(1 + star_count)) * recency_decay` where recency_decay is `1 / (1 + days_since_update / 30)`. The whole rank computation lives in SQL so Postgres can short-circuit on the GIN index.

type UserResult

type UserResult struct {
	ID          int64
	Username    string
	DisplayName string
	Bio         string
	Rank        float64
}

UserResult is one row from SearchUsers.

func SearchUsers

func SearchUsers(ctx context.Context, deps Deps, q ParsedQuery, limit, offset int) ([]UserResult, int64, error)

SearchUsers runs a user search. No visibility predicate — user profiles are public by definition; suspended/deleted accounts are excluded so they don't taint search results (matches the spec pitfall: "search on suspended user content").

Jump to

Keyboard shortcuts

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