Use ids internally and for entry links instead of the full text title

This commit is contained in:
ChaoticByte 2025-07-11 23:20:27 +02:00
parent bebf4fc986
commit d3d1519f23
No known key found for this signature in database
4 changed files with 49 additions and 35 deletions

View file

@ -13,12 +13,12 @@ import (
) )
type Database struct { type Database struct {
Keys []string Titles map[string]string
Entries map[string]string Entries map[string]string
matcher *search.Matcher matcher *search.Matcher
} }
func (db *Database) search(query string) []string { // returns keys (entry names) func (db *Database) searchForIds(query string) []string { // returns keys (entry names)
results := []string{} results := []string{}
// compile patterns // compile patterns
queryPatterns := []*search.Pattern{} queryPatterns := []*search.Pattern{}
@ -26,16 +26,18 @@ func (db *Database) search(query string) []string { // returns keys (entry names
queryPatterns = append(queryPatterns, db.matcher.CompileString(q)) queryPatterns = append(queryPatterns, db.matcher.CompileString(q))
} }
// search // search
for _, k := range db.Keys { for k, v := range db.Entries {
// title (k) // title (k)
if strings.Contains(k, query) || strings.Contains(query, k) { titleLower := strings.ToLower(db.Titles[k])
queryLower := strings.ToLower(query)
if strings.Contains(titleLower, queryLower) {
results = append(results, k) results = append(results, k)
continue continue
} }
// content body // content body
patternsFound := 0 patternsFound := 0
for _, p := range queryPatterns { for _, p := range queryPatterns {
if s, _ := p.IndexString(db.Entries[k]); s != -1 { if s, _ := p.IndexString(v); s != -1 {
patternsFound++ // this pattern was found patternsFound++ // this pattern was found
} }
} }
@ -44,6 +46,7 @@ func (db *Database) search(query string) []string { // returns keys (entry names
results = append(results, k) results = append(results, k)
} }
} }
slices.Sort(results)
return results return results
} }
@ -58,8 +61,10 @@ func BuildDB(directory string) Database {
entriesDirFs := os.DirFS(directory) entriesDirFs := os.DirFS(directory)
files, err := fs.Glob(entriesDirFs, "*.txt") files, err := fs.Glob(entriesDirFs, "*.txt")
if err != nil { logger.Panicln(err) } if err != nil { logger.Panicln(err) }
titles := []string{} titles := map[string]string{}
for _, f := range files { for _, f := range files {
k := f[:len(f)-4] // remove ".txt"
k = strings.ReplaceAll(k, "|", "_") // we don't want | because it is used in the search protocol
fileData, err := os.ReadFile(directory + "/" + f) fileData, err := os.ReadFile(directory + "/" + f)
if err != nil { logger.Panicln(err) } if err != nil { logger.Panicln(err) }
content := string(fileData) content := string(fileData)
@ -80,14 +85,13 @@ func BuildDB(directory string) Database {
if len(body) < 1 { if len(body) < 1 {
body = " " body = " "
} }
titles = append(titles, title) titles[k] = title
entries[title] = body entries[k] = body
} else { } else {
title := f[:len(f)-4] // remove ".txt" titles[k] = k
titles = append(titles, title) entries[k] = content
entries[title] = content
} }
} }
matcher := search.New(ContentLanguage, search.IgnoreCase, search.IgnoreDiacritics) matcher := search.New(ContentLanguage, search.IgnoreCase, search.IgnoreDiacritics)
return Database{Keys: titles, Entries: entries, matcher: matcher} return Database{Titles: titles, Entries: entries, matcher: matcher}
} }

17
main.go
View file

@ -18,7 +18,7 @@ var db Database
type TemplateData struct { type TemplateData struct {
Title string Title string
SiteDescription string SiteDescription string
TOC []string TOC map[string]string
EntryTitle string EntryTitle string
Entry string Entry string
Footer []template.HTML Footer []template.HTML
@ -33,10 +33,10 @@ func loadTemplate() {
func handleApplication(w http.ResponseWriter, req *http.Request) { func handleApplication(w http.ResponseWriter, req *http.Request) {
var entry string var entry string
var err error var err error
entryName := path.Base(req.URL.Path) entryId := path.Base(req.URL.Path)
if entryName != "/" { if entryId != "/" {
// load entry // load entry
entry = db.Entries[entryName] entry = db.Entries[entryId]
if entry == "" { // redirect if entry doesn't exist (or is empty) if entry == "" { // redirect if entry doesn't exist (or is empty)
http.Redirect(w, req, "/", http.StatusTemporaryRedirect) http.Redirect(w, req, "/", http.StatusTemporaryRedirect)
} }
@ -44,10 +44,10 @@ func handleApplication(w http.ResponseWriter, req *http.Request) {
err = appTemplate.ExecuteTemplate( err = appTemplate.ExecuteTemplate(
w, "app", w, "app",
TemplateData{ TemplateData{
TOC: db.Keys, TOC: db.Titles,
Entry: entry, Entry: entry,
Title: MainTitle, Title: MainTitle,
EntryTitle: entryName, EntryTitle: db.Titles[entryId],
Footer: FooterContent, Footer: FooterContent,
SiteDescription: SiteDescription, SiteDescription: SiteDescription,
}) })
@ -60,7 +60,10 @@ func handleSearchAPI(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
} }
results := db.search(searchQuery) results := db.searchForIds(searchQuery)
for i, r := range results {
results[i] = r + "|" + db.Titles[r]
}
w.Write([]byte(strings.Join(results, "\n"))) w.Write([]byte(strings.Join(results, "\n")))
} }

View file

@ -25,8 +25,8 @@
<input type="text" id="search-box" placeholder="search"> <input type="text" id="search-box" placeholder="search">
<div id="search-results" class="hidden"></div> <div id="search-results" class="hidden"></div>
<div class="toc" id="toc"> <div class="toc" id="toc">
{{- range .TOC -}} {{- range $k, $title := .TOC -}}
<div><a href="{{ . }}">{{ . }}</a></div> <div><a href="{{ $k }}">{{ $title }}</a></div>
{{- end -}} {{- end -}}
</div> </div>
</div> </div>

View file

@ -7,30 +7,31 @@
/** /**
* @param {string} results * @param {string} results
*/ */
function updateSearchResults(results) { function showSearchResults(results) {
if (results.length > 0) {
searchResults.innerHTML = ""; searchResults.innerHTML = "";
for (let i = 0; i < results.length; i++) { if (results.length > 0) {
results.forEach(r => {
let resultElem = document.createElement("div"); let resultElem = document.createElement("div");
let resultAnchor = document.createElement("a"); let resultAnchor = document.createElement("a");
resultAnchor.href = results[i]; // we should be at /, so this is right resultAnchor.href = r[0]; // we should be at /, so this is right
resultAnchor.innerText = results[i]; resultAnchor.innerText = r[1];
resultElem.appendChild(resultAnchor); resultElem.appendChild(resultAnchor);
searchResults.appendChild(resultElem); searchResults.appendChild(resultElem);
});
} }
toc.classList.add("hidden"); toc.classList.add("hidden");
searchResults.classList.remove("hidden"); searchResults.classList.remove("hidden");
} else { }
function hideSearchResults() {
toc.classList.remove("hidden"); toc.classList.remove("hidden");
searchResults.classList.add("hidden"); searchResults.classList.add("hidden");
} }
}
async function handleSearchInput() { async function handleSearchInput() {
// get search query // get search query
const query = searchBox.value; const query = searchBox.value;
if (query == "") { if (query == "") {
updateSearchResults([]); hideSearchResults();
return return
} }
// make request // make request
@ -38,8 +39,14 @@
if (!response.ok) { if (!response.ok) {
throw new Error(`Search API returned status code ${response.status}`); throw new Error(`Search API returned status code ${response.status}`);
} }
let result = await response.text(); let results_raw = await response.text();
updateSearchResults(result.split('\n')); let results = [];
if (results_raw.length > 0) {
results_raw.split('\n').forEach(r => {
results.push(r.split("|", 2));
});
}
showSearchResults(results);
} }
searchBox.addEventListener("input", handleSearchInput); searchBox.addEventListener("input", handleSearchInput);