Go · 2576 bytes Raw Blame History
1 // SPDX-License-Identifier: AGPL-3.0-or-later
2
3 // Package search owns S28's search surface. Postgres FTS
4 // (`tsvector` + `pg_trgm`) backs everything; no external search
5 // engine. Visibility scoping flows through `policy.VisibilityPredicate`
6 // so every query is gated by the same rule the rest of the runtime
7 // uses.
8 //
9 // Entry points are:
10 //
11 // SearchRepos / SearchIssues / SearchUsers / SearchCode — per-type
12 // queries returning slices of result rows + the total count.
13 // ParseQuery — splits a user query string into the FTS query plus
14 // operator filters (repo:, is:, author:, state:).
15 package search
16
17 import (
18 "errors"
19 "log/slog"
20 "time"
21
22 "github.com/jackc/pgx/v5/pgxpool"
23 )
24
25 // Deps wires the package against the rest of the runtime.
26 type Deps struct {
27 Pool *pgxpool.Pool
28 Logger *slog.Logger
29 }
30
31 // Errors surfaced to handlers.
32 var (
33 ErrEmptyQuery = errors.New("search: query is empty")
34 )
35
36 // PageSize is the per-type result count for the full results page.
37 // Quick-dropdown uses a smaller cap (see QuickResultsLimit).
38 const PageSize = 20
39
40 // QuickResultsLimit is the per-type cap for the top-bar quick
41 // dropdown. Same shape as GitHub's: tight for keystroke speed.
42 const QuickResultsLimit = 5
43
44 // MaxQueryBytes caps incoming query strings. Tighter than the
45 // markdown 1 MiB ceiling because no legitimate search ever needs
46 // even 1 KiB.
47 const MaxQueryBytes = 256
48
49 // RepoResult is one row from SearchRepos.
50 type RepoResult struct {
51 ID int64
52 OwnerUsername string
53 Name string
54 Description string
55 Visibility string
56 StarCount int64
57 UpdatedAt time.Time
58 Rank float64
59 }
60
61 // IssueResult is one row from SearchIssues.
62 type IssueResult struct {
63 ID int64
64 RepoID int64
65 OwnerUsername string
66 RepoName string
67 Number int64
68 Title string
69 State string
70 Kind string // "issue" | "pr"
71 AuthorName string
72 UpdatedAt time.Time
73 Rank float64
74 }
75
76 // UserResult is one row from SearchUsers.
77 type UserResult struct {
78 ID int64
79 Username string
80 DisplayName string
81 Bio string
82 Rank float64
83 }
84
85 // CodeResult is one row from SearchCode. Either Path or Content
86 // (or both) is populated depending on which subquery hit.
87 type CodeResult struct {
88 RepoID int64
89 OwnerUsername string
90 RepoName string
91 RefName string
92 Path string
93 // PreviewLine is a single line of content extracted near the
94 // match, when content matched. Empty for path-only hits.
95 PreviewLine string
96 Rank float64
97 }
98