Add support for retrieval of snippet matches.

This commit is contained in:
Sergio Martínez Portela 2025-02-23 22:50:34 +01:00
parent 41c02acaca
commit cd30f9b9c9
4 changed files with 69 additions and 5 deletions

View File

@ -12,4 +12,4 @@ cd ../../_gen/notes/
set -x set -x
exec docker run -it --rm -p $PORT:80 -e PORT=80 -e DB_PATH=/db.sqlite3 -v `pwd`/db.sqlite3:/db.sqlite3:ro search-server exec docker run -it --rm -p $PORT:80 -e SNIPPET_SIZE=128 -e PORT=80 -e DB_PATH=/db.sqlite3 -v `pwd`/db.sqlite3:/db.sqlite3:ro search-server

View File

@ -33,6 +33,22 @@ func main() {
port = port_num port = port_num
} }
snippet_size := 128
snippet_size_str, ok := os.LookupEnv("SNIPPET_SIZE")
if ok {
snippet_size_num, err := strconv.Atoi(snippet_size_str)
if err != nil {
log.Fatal(err)
os.Exit(1)
}
if (snippet_size_num < 64) {
log.Fatal("Environment variale $SNIPPET_SIZE must be >= 64.")
os.Exit(1)
}
snippet_size = snippet_size_num
}
db, err := sql.Open("sqlite3", database_path) db, err := sql.Open("sqlite3", database_path)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -64,7 +80,7 @@ func main() {
query := c.Query("q") query := c.Query("q")
stm, err := db.Prepare("SELECT note_id, title, top_level_title, is_done, is_todo FROM note_search(?)") stm, err := db.Prepare("SELECT note_id, highlight(note_search, 1, '<span class=\"match\">', '</span>'), top_level_title, is_done, is_todo, snippet(note_search, 2, '<span class=\"match\">', '</span>', '<span class=\"search-result-break\"></span>', ?) FROM note_search(?)")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -77,7 +93,7 @@ func main() {
results := make([]map[string]string, 0) results := make([]map[string]string, 0)
rows, err := stm.Query(query) rows, err := stm.Query(snippet_size, query)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
c.JSON(500, gin.H{ c.JSON(500, gin.H{
@ -93,6 +109,7 @@ func main() {
var note_top_level_title string var note_top_level_title string
var note_is_done string var note_is_done string
var note_is_todo string var note_is_todo string
var note_highlight string
err = rows.Scan( err = rows.Scan(
&note_id, &note_id,
@ -100,6 +117,7 @@ func main() {
&note_top_level_title, &note_top_level_title,
&note_is_done, &note_is_done,
&note_is_todo, &note_is_todo,
&note_highlight,
) )
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -116,6 +134,7 @@ func main() {
item["top_level_title"] = note_top_level_title item["top_level_title"] = note_top_level_title
item["is_done"] = note_is_done item["is_done"] = note_is_done
item["is_todo"] = note_is_todo item["is_todo"] = note_is_todo
item["highlight"] = note_highlight
results = append(results, item) results = append(results, item)
} }

View File

@ -1,4 +1,9 @@
function _codigoparallevar_enable_search_box(selector, options) { function _codigoparallevar_enable_search_box(selector, options) {
const unescape = (str, tag) => {
const prev = tag.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
return str.replaceAll(prev, tag);
}
const element = document.querySelector(selector); const element = document.querySelector(selector);
if ('placeholder' in options) { if ('placeholder' in options) {
element.setAttribute('placeholder', options.placeholder); element.setAttribute('placeholder', options.placeholder);
@ -94,14 +99,32 @@ function _codigoparallevar_enable_search_box(selector, options) {
const resultTitle = document.createElement('h2'); const resultTitle = document.createElement('h2');
resultTitle.innerText = `${note.title} (${note.top_level_title})`; resultTitle.innerText = `${note.title} (${note.top_level_title})`;
resultTitle.innerHTML = unescape(unescape(
resultTitle.innerHTML,
'</span>'), '<span class="match">'
);
if (note.is_todo === "1") { if (note.is_todo === "1") {
resultTitle.setAttribute('class', 'is-todo'); resultCard.setAttribute('class', 'is-todo');
} }
else if (note.is_done === "1") { else if (note.is_done === "1") {
resultTitle.setAttribute('class', 'is-done'); resultCard.setAttribute('class', 'is-done');
} }
resultContents.appendChild(resultTitle); resultContents.appendChild(resultTitle);
if (note.highlight) {
const resultBody = document.createElement('p');
resultBody.innerText = note.highlight;
resultBody.innerHTML = unescape(
unescape(
unescape(
resultBody.innerHTML,
'</span>'),
'<span class="search-result-break">'),
'<span class="match">');
resultContents.appendChild(resultBody);
}
resultCard.appendChild(resultContents); resultCard.appendChild(resultContents);
resultsList.appendChild(resultCard); resultsList.appendChild(resultCard);
} }

View File

@ -126,13 +126,30 @@ body nav input {
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.25); box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.25);
} }
.results-box ul li a {
text-decoration: none;
}
.results-box ul li h2 { .results-box ul li h2 {
font-size: 110%; font-size: 110%;
padding: 1.25ex; padding: 1.25ex;
display: block; display: block;
margin: 0; margin: 0;
text-decoration: underline;
} }
.results-box ul li p {
padding: 1.25ex;
color: black;
}
.results-box ul li span.match {
text-decoration: underline red;
}
.results-box ul li .search-result-break::before {
content: '…';
color: #777;
}
.results-box li h2.is-todo::before { .results-box li h2.is-todo::before {
content: 'TODO'; content: 'TODO';
display: inline-block; display: inline-block;
@ -659,6 +676,11 @@ tr.__table-separator {
.results-box ul li h2 { .results-box ul li h2 {
color: white; color: white;
} }
.results-box ul li p {
padding: 1.25ex;
color: white;
}
.results-box-container .results-box input:focus { .results-box-container .results-box input:focus {
border-bottom: 1px solid #fff; border-bottom: 1px solid #fff;
} }