vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software: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/index.go +19−2
| @@ -176,6 +176,22 @@ CREATE TABLE IF NOT EXISTS artifacts ( | |||
| 176 | 176 | oid TEXT NOT NULL, | |
| 177 | 177 | size INTEGER NOT NULL DEFAULT 0, | |
| 178 | 178 | PRIMARY KEY (repo_id, tag, name) | |
| 179 | ); | ||
| 180 | |||
| 181 | -- search is the universal search surface: one row for every issue, change | ||
| 182 | -- request and comment, with the text FTS5 tokenizes. The first four columns are | ||
| 183 | -- UNINDEXED because they identify the result rather than match it; title and | ||
| 184 | -- body are what a query looks at. The build must include SQLite's FTS5 module | ||
| 185 | -- (see README.md), or Open fails here with that reason. | ||
| 186 | CREATE VIRTUAL TABLE IF NOT EXISTS search USING fts5( | ||
| 187 | repo_id UNINDEXED, | ||
| 188 | kind UNINDEXED, | ||
| 189 | entity_id UNINDEXED, | ||
| 190 | parent_id UNINDEXED, | ||
| 191 | title, | ||
| 192 | body, | ||
| 193 | author UNINDEXED, | ||
| 194 | tokenize = 'unicode61 remove_diacritics 2' | ||
| 179 | 195 | );`); err != nil { | |
| 180 | 196 | db.Close() | |
| 181 | 197 | return nil, err | |
| @@ -192,7 +208,7 @@ CREATE TABLE IF NOT EXISTS artifacts ( | |||
| 192 | 208 | // and leaves every repo with an empty issues_sha, which makes the next read | |
| 193 | 209 | // rebuild that repo from git. A stale row is worse than a rebuild, and a rebuild is the | |
| 194 | 210 | // operation the index already has to survive. | |
| 195 | const schemaVersion = 4 | ||
| 211 | const schemaVersion = 5 | ||
| 196 | 212 | ||
| 197 | 213 | func (s *Store) migrate() error { | |
| 198 | 214 | var v int | |
| @@ -212,6 +228,7 @@ DELETE FROM request_closes; | |||
| 212 | 228 | DELETE FROM request_approvals; | |
| 213 | 229 | DELETE FROM comments; | |
| 214 | 230 | DELETE FROM artifacts; | |
| 231 | DELETE FROM search; | ||
| 215 | 232 | UPDATE repos SET issues_sha = '';`); err != nil { | |
| 216 | 233 | return err | |
| 217 | 234 | } | |
| @@ -256,7 +273,7 @@ func (s *Store) Rebuild(st *repo.Store) (int, error) { | |||
| 256 | 273 | // longer exists must not survive it. | |
| 257 | 274 | for _, table := range []string{ | |
| 258 | 275 | "issues", "issue_labels", "issue_assignees", "requests", "request_closes", | |
| 259 | "request_approvals", "comments", "artifacts", "entity_numbers", | ||
| 276 | "request_approvals", "comments", "artifacts", "search", "entity_numbers", | ||
| 260 | 277 | } { | |
| 261 | 278 | if _, err := tx.Exec(`DELETE FROM ` + table); err != nil { | |
| 262 | 279 | return 0, err | |