cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm
leaflet
readability
golang
1package handlers
2
3import (
4 "context"
5 "database/sql"
6 "fmt"
7
8 "github.com/stormlightlabs/noteleaf/internal/repo"
9 "github.com/stormlightlabs/noteleaf/internal/store"
10)
11
12// DocumentHandler provides operations for document search
13type DocumentHandler struct {
14 db *sql.DB
15 repos *repo.Repositories
16 engine *repo.SearchEngine
17}
18
19// NewDocumentHandler creates a new document handler
20func NewDocumentHandler() (*DocumentHandler, error) {
21 db, err := store.NewDatabase()
22 if err != nil {
23 return nil, fmt.Errorf("failed to initialize database: %w", err)
24 }
25
26 repos := repo.NewRepositories(db.DB)
27 engine := repo.NewSearchEngine(repos.Documents)
28
29 return &DocumentHandler{
30 db: db.DB,
31 repos: repos,
32 engine: engine,
33 }, nil
34}
35
36// RebuildIndex rebuilds the search index from notes
37func (h *DocumentHandler) RebuildIndex(ctx context.Context) error {
38 if err := h.repos.Documents.DeleteAll(ctx); err != nil {
39 return fmt.Errorf("failed to clear documents: %w", err)
40 }
41
42 if err := h.repos.Documents.RebuildFromNotes(ctx, h.repos.Notes); err != nil {
43 return fmt.Errorf("failed to rebuild from notes: %w", err)
44 }
45
46 if err := h.engine.Rebuild(ctx); err != nil {
47 return fmt.Errorf("failed to rebuild search index: %w", err)
48 }
49
50 fmt.Println("Search index rebuilt successfully")
51 return nil
52}
53
54// Search performs a TF-IDF search and displays results
55func (h *DocumentHandler) Search(ctx context.Context, query string, limit int) error {
56 if err := h.engine.Rebuild(ctx); err != nil {
57 return fmt.Errorf("failed to rebuild index: %w", err)
58 }
59
60 results, docs, err := h.engine.SearchWithScores(ctx, query, limit)
61 if err != nil {
62 return fmt.Errorf("search failed: %w", err)
63 }
64
65 if len(results) == 0 {
66 fmt.Println("No results found")
67 return nil
68 }
69
70 fmt.Printf("Found %d results:\n\n", len(results))
71 for i, doc := range docs {
72 score := results[i].Score
73 fmt.Printf("%d. [Score: %.2f] %s\n", i+1, score, doc.Title)
74 fmt.Printf(" %s\n\n", truncate(doc.Body, 100))
75 }
76
77 return nil
78}
79
80func truncate(s string, maxLen int) string {
81 if len(s) <= maxLen {
82 return s
83 }
84 return s[:maxLen] + "..."
85}