Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Add a search table over the whole metadata branch

6762a520b7af700dfebd7f613e1a610a42631139 · Keenan Nemetz · 10d ago · parent b739915c

Add a search table over the whole metadata branch

Search is the other thing the derived rows are for: one document per
issue, change request, and comment, with the title, the body and the
author. FTS5 tokenizes them, so a query ranks by relevance instead of
scanning every body, and `#42` or a ULID prefix is answered from the
numbering table first so the entity someone named comes first.

The documents are maintained by the same refresh as the list rows: an
entity's text is replaced with the entity, a thread's comments are
replaced with the thread, and a deletion removes both. A comment row
carries the parent's title and id, so a result can say what it is on and
link to the fragment without a second read.

The store never decides visibility: Search takes the repository ids the
caller may show, and returns nothing outside them.

This needs SQLite's FTS5 module. The build tag that enables it in the
driver's bundled copy is added to the build commands in the next commit;
a build without it fails at index.Open with that reason.

forge/index/refresh.go +4−4

@@ -106,7 +106,7 @@ func (s *Store) IndexRepo(m *repo.Meta) error {
106 106 if err := insertIssue(tx, m.ID, iss); err != nil {
107 107 return err
108 108 }
109 if err := replaceComments(tx, m.ID, commentKindIssue, iss.ID, iss.Comments); err != nil {
109 if err := replaceComments(tx, m.ID, commentKindIssue, iss.ID, iss.Title, iss.Comments); err != nil {
110 110 return err
111 111 }
112 112 }
@@ -114,7 +114,7 @@ func (s *Store) IndexRepo(m *repo.Meta) error {
114 114 if err := insertRequest(tx, m.ID, req); err != nil {
115 115 return err
116 116 }
117 if err := replaceComments(tx, m.ID, commentKindRequest, req.ID, req.Comments); err != nil {
117 if err := replaceComments(tx, m.ID, commentKindRequest, req.ID, req.Title, req.Comments); err != nil {
118 118 return err
119 119 }
120 120 }
@@ -212,7 +212,7 @@ func (s *Store) indexFrom(m *repo.Meta, from, to string) error {
212 212 if err := insertIssue(tx, m.ID, iss); err != nil {
213 213 return err
214 214 }
215 if err := replaceComments(tx, m.ID, commentKindIssue, iss.ID, iss.Comments); err != nil {
215 if err := replaceComments(tx, m.ID, commentKindIssue, iss.ID, iss.Title, iss.Comments); err != nil {
216 216 return err
217 217 }
218 218 }
@@ -231,7 +231,7 @@ func (s *Store) indexFrom(m *repo.Meta, from, to string) error {
231 231 if err := insertRequest(tx, m.ID, req); err != nil {
232 232 return err
233 233 }
234 if err := replaceComments(tx, m.ID, commentKindRequest, req.ID, req.Comments); err != nil {
234 if err := replaceComments(tx, m.ID, commentKindRequest, req.ID, req.Title, req.Comments); err != nil {
235 235 return err
236 236 }
237 237 }