internal/store/search.go
66 lines · 2298 bytes
1package store
2
3// Search across repositories, for the `search` command and the web's
4// /search page. Visibility is the same rule everywhere: public plus what
5// the user reaches, so an anonymous caller (user id 0) sees only public
6// rows rather than a different query.
7
8// visibleCond admits public repositories and anything the user reaches.
9const visibleCond = `(r.visibility = 'public' OR ` + reachableCond + `)`
10
11// VisibleRepos returns every repository the user may read, public ones
12// included, ordered by owner then name.
13func (s *Store) VisibleRepos(userID int64) ([]Repo, error) {
14 rows, err := s.DB.Query(repoSelect+" WHERE "+visibleCond+" ORDER BY 4, r.name", userID)
15 if err != nil {
16 return nil, err
17 }
18 defer rows.Close()
19 var out []Repo
20 for rows.Next() {
21 r, err := scanRepo(rows)
22 if err != nil {
23 return nil, err
24 }
25 out = append(out, r)
26 }
27 return out, rows.Err()
28}
29
30// SearchIssues returns issues matching q in title or body, newest
31// activity first.
32func (s *Store) SearchIssues(userID int64, q string, limit int) ([]DashboardItem, error) {
33 return s.searchQuery("issues", "issue_fts", userID, q, limit)
34}
35
36// SearchMRs is SearchIssues for merge requests.
37func (s *Store) SearchMRs(userID int64, q string, limit int) ([]DashboardItem, error) {
38 return s.searchQuery("merge_requests", "mr_fts", userID, q, limit)
39}
40
41func (s *Store) searchQuery(table, index string, userID int64, q string, limit int) ([]DashboardItem, error) {
42 rows, err := s.DB.Query(`
43 SELECT COALESCE(u.username, o.name) || '/' || r.name,
44 x.number, x.title, au.username, x.state, x.updated_at
45 FROM `+table+` x
46 JOIN repos r ON r.id = x.repo_id
47 LEFT JOIN users u ON r.owner_kind = 'user' AND u.id = r.owner_id
48 LEFT JOIN orgs o ON r.owner_kind = 'org' AND o.id = r.owner_id
49 JOIN users au ON au.id = x.author_id
50 WHERE x.id IN (SELECT rowid FROM `+index+` WHERE `+index+` MATCH ?2)
51 AND `+visibleCond+`
52 ORDER BY x.updated_at DESC LIMIT ?3`, userID, FTSQuery(q), limit)
53 if err != nil {
54 return nil, err
55 }
56 defer rows.Close()
57 var out []DashboardItem
58 for rows.Next() {
59 var d DashboardItem
60 if err := rows.Scan(&d.RepoPath, &d.Number, &d.Title, &d.Author, &d.State, &d.UpdatedAt); err != nil {
61 return nil, err
62 }
63 out = append(out, d)
64 }
65 return out, rows.Err()
66}