Use ids internally and for entry links instead of the full text title
This commit is contained in:
parent
bebf4fc986
commit
d3d1519f23
4 changed files with 49 additions and 35 deletions
28
database.go
28
database.go
|
@ -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
17
main.go
|
@ -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")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -7,30 +7,31 @@
|
||||||
/**
|
/**
|
||||||
* @param {string} results
|
* @param {string} results
|
||||||
*/
|
*/
|
||||||
function updateSearchResults(results) {
|
function showSearchResults(results) {
|
||||||
|
searchResults.innerHTML = "";
|
||||||
if (results.length > 0) {
|
if (results.length > 0) {
|
||||||
searchResults.innerHTML = "";
|
results.forEach(r => {
|
||||||
for (let i = 0; i < results.length; i++) {
|
|
||||||
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");
|
|
||||||
searchResults.classList.remove("hidden");
|
|
||||||
} else {
|
|
||||||
toc.classList.remove("hidden");
|
|
||||||
searchResults.classList.add("hidden");
|
|
||||||
}
|
}
|
||||||
|
toc.classList.add("hidden");
|
||||||
|
searchResults.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
function hideSearchResults() {
|
||||||
|
toc.classList.remove("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);
|
||||||
|
|
Reference in a new issue