include-search.go (2104B)
1 package main 2 3 import ( 4 "database/sql" 5 "fmt" 6 "net/http" 7 "net/url" 8 "strings" 9 ) 10 11 func searchHandler(w http.ResponseWriter, r *http.Request) { 12 query := strings.TrimSpace(r.URL.Query().Get("q")) 13 14 var files []File 15 var searchTitle string 16 17 if query != "" { 18 sqlPattern := "%" + strings.ReplaceAll(strings.ReplaceAll(strings.ToLower(query), "*", "%"), "?", "_") + "%" 19 20 rows, err := db.Query(` 21 SELECT f.id, f.filename, f.path, COALESCE(f.description, '') AS description, 22 c.name AS category, t.value AS tag 23 FROM files f 24 LEFT JOIN file_tags ft ON ft.file_id = f.id 25 LEFT JOIN tags t ON t.id = ft.tag_id 26 LEFT JOIN categories c ON c.id = t.category_id 27 WHERE LOWER(f.filename) LIKE ? OR LOWER(f.description) LIKE ? OR LOWER(t.value) LIKE ? 28 ORDER BY f.filename 29 `, sqlPattern, sqlPattern, sqlPattern) 30 if err != nil { 31 renderError(w, "Search failed: "+err.Error(), http.StatusInternalServerError) 32 return 33 } 34 defer rows.Close() 35 36 fileMap := make(map[int]*File) 37 for rows.Next() { 38 var id int 39 var filename, path, description, category, tag sql.NullString 40 41 if err := rows.Scan(&id, &filename, &path, &description, &category, &tag); err != nil { 42 renderError(w, "Failed to read search results: "+err.Error(), http.StatusInternalServerError) 43 return 44 } 45 46 f, exists := fileMap[id] 47 if !exists { 48 f = &File{ 49 ID: id, 50 Filename: filename.String, 51 Path: path.String, 52 EscapedFilename: url.PathEscape(filename.String), 53 Description: description.String, 54 Tags: make(map[string][]string), 55 } 56 fileMap[id] = f 57 } 58 59 if category.Valid && tag.Valid && tag.String != "" { 60 f.Tags[category.String] = append(f.Tags[category.String], tag.String) 61 } 62 } 63 64 for _, f := range fileMap { 65 files = append(files, *f) 66 } 67 68 searchTitle = fmt.Sprintf("Search Results for: %s", query) 69 } else { 70 searchTitle = "Search Files" 71 } 72 73 pageData := buildPageData(searchTitle, files) 74 pageData.Query = query 75 pageData.Files = files 76 renderTemplate(w, "search.html", pageData) 77 }