package db type SearchPage struct { Path string Title string Content string } type SearchResult struct { Path string Title string Snippet string } func (d *DB) IndexPage(p SearchPage) error { tx, err := d.db.Begin() if err != nil { return err } defer tx.Rollback() if _, err = tx.Exec(`DELETE FROM pages_fts WHERE path = ?`, p.Path); err != nil { return err } if _, err = tx.Exec( `INSERT INTO pages_fts (path, title, content) VALUES (?, ?, ?)`, p.Path, p.Title, p.Content, ); err != nil { return err } return tx.Commit() } func (d *DB) UnindexPage(path string) error { _, err := d.db.Exec(`DELETE FROM pages_fts WHERE path = ?`, path) return err } // Search runs a full-text query and returns up to 20 results with snippets. func (d *DB) Search(query string) ([]SearchResult, error) { rows, err := d.db.Query(` SELECT path, title, snippet(pages_fts, 2, '', '', '...', 20) FROM pages_fts WHERE pages_fts MATCH ? ORDER BY rank LIMIT 20 `, query) if err != nil { return nil, err } defer rows.Close() var results []SearchResult for rows.Next() { var r SearchResult if err := rows.Scan(&r.Path, &r.Title, &r.Snippet); err != nil { return nil, err } results = append(results, r) } return results, rows.Err() }