feat(search): add transactional rebuild and index optimization

This commit is contained in:
eezd committed 2026-10-25 13:21:09 +08:00
1 parent 39dfe8b285
commit ee95b9a181
3 files changed
+169 -19

No files matched your search

+99
View File
@@ -0,0 +1,99 @@
package search
import (
"context"
"database/sql"
"encoding/json"
"rawmark/internal/vault"
)
func insert(tx *sql.Tx, p, rev string, content []byte) (int64, error) {
metadata, err := json.Marshal(vault.Analyze(p, content))
if err != nil {
return 0, err
}
result, err := tx.Exec(`INSERT INTO docs(path,rev,metadata) VALUES (?,?,?)`, p, rev, string(metadata))
if err != nil {
return 0, err
}
id, err := result.LastInsertId()
if err != nil {
return 0, err
}
_, err = tx.Exec(`INSERT INTO docs_fts(rowid,path,body) VALUES (?,?,?)`, id, p, string(content))
return id, err
}
// Rebuild 与后台同步互斥,失败时事务和已知快照均保持原状。
func (x *Index) Rebuild(ctx context.Context) error {
x.work.Lock()
defer x.work.Unlock()
x.mu.Lock()
version := x.pendingVersion
x.mu.Unlock()
entries, err := x.v.Tree()
if err != nil {
return err
}
tx, err := x.db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
if _, err = tx.ExecContext(ctx, `DELETE FROM docs_fts; DELETE FROM docs`); err != nil {
return err
}
next := map[string]doc{}
for _, entry := range entries {
if entry.Dir || !vault.IsNote(entry.Path) {
continue
}
if err = ctx.Err(); err != nil {
return err
}
content, rev, err := x.v.Read(entry.Path)
if err != nil {
return err
}
id, err := insert(tx, entry.Path, rev, content)
if err != nil {
return err
}
next[entry.Path] = doc{id: id, rev: rev}
}
if err = tx.Commit(); err != nil {
return err
}
x.known = next
x.mu.Lock()
if version == x.pendingVersion {
x.pending = nil
}
x.ready = true
x.lastError = nil
x.mu.Unlock()
return nil
}
func (x *Index) Optimize(ctx context.Context) error {
x.work.Lock()
defer x.work.Unlock()
for _, stmt := range []string{`INSERT INTO docs_fts(docs_fts) VALUES ('optimize')`, `PRAGMA wal_checkpoint(TRUNCATE)`, `VACUUM`} {
if _, err := x.db.ExecContext(ctx, stmt); err != nil {
return err
}
}
return nil
}
func (x *Index) syncPending() error {
x.work.Lock()
defer x.work.Unlock()
x.mu.Lock()
files := x.pending
x.pending = nil
x.mu.Unlock()
if files == nil {
return nil
}
return x.syncLocked(files)
}