From 5ef9aad501f17b57107a35508a093211ecf2dbd8 Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sun, 31 Jul 2022 12:16:36 -0700 Subject: [PATCH] feat: add support for semantic search using operand --- .gitignore | 1 + assets/js/{search.js => full-text-search.js} | 2 +- assets/js/semantic-search.js | 35 ++++++++++++++++++++ assets/js/util.js | 28 +++++++++------- content/notes/config.md | 6 +++- data/config.yaml | 4 ++- layouts/partials/github.html | 2 +- layouts/partials/search.html | 8 ++++- 8 files changed, 69 insertions(+), 17 deletions(-) rename assets/js/{search.js => full-text-search.js} (97%) create mode 100644 assets/js/semantic-search.js diff --git a/.gitignore b/.gitignore index a7ccdb590..182026f9c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ resources content/.obsidian assets/indices/linkIndex.json assets/indices/contentIndex.json +linkmap diff --git a/assets/js/search.js b/assets/js/full-text-search.js similarity index 97% rename from assets/js/search.js rename to assets/js/full-text-search.js index d296e65d5..5f56101b1 100644 --- a/assets/js/search.js +++ b/assets/js/full-text-search.js @@ -56,6 +56,6 @@ } const allIds = new Set([...getByField("title"), ...getByField("content")]) const finalResults = [...allIds].map(formatForDisplay) - displayResults(finalResults) + displayResults(finalResults, true) }) })() diff --git a/assets/js/semantic-search.js b/assets/js/semantic-search.js new file mode 100644 index 000000000..a62d3d593 --- /dev/null +++ b/assets/js/semantic-search.js @@ -0,0 +1,35 @@ +const apiKey = "{{$.Site.Data.config.operandApiKey}}" + +async function searchContents(query) { + const response = await fetch('https://prod.operand.ai/v3/search/objects', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: apiKey, + }, + body: JSON.stringify({ + query, + max: 10 + }), + }); + return (await response.json()); +} + +function debounce(func, timeout = 300) { + let timer; + return (...args) => { + clearTimeout(timer) + timer = setTimeout(() => { func.apply(this, args); }, timeout) + }; +} + +registerHandlers(debounce((e) => { + term = e.target.value + searchContents(term) + .then((res) => res.results.map(entry => ({ + url: entry.object.metadata.url, + content: entry.snippet, + title: entry.object.title + }))) + .then(results => displayResults(results)) +})) diff --git a/assets/js/util.js b/assets/js/util.js index c4652387a..32e1568ec 100644 --- a/assets/js/util.js +++ b/assets/js/util.js @@ -108,13 +108,11 @@ const highlight = (content, term) => { } // Common utilities for search -const resultToHTML = ({ url, title, content, term }) => { - const text = removeMarkdown(content) - const resultTitle = highlight(title, term) - const resultText = highlight(text, term) +const resultToHTML = ({ url, title, content }) => { + const cleaned = removeMarkdown(content) return `` } @@ -183,7 +181,7 @@ const registerHandlers = (onInputFn) => { }) } -const displayResults = (finalResults) => { +const displayResults = (finalResults, extractHighlight = false) => { const results = document.getElementById("results-container") if (finalResults.length === 0) { results.innerHTML = `` } else { results.innerHTML = finalResults - .map((result) => - resultToHTML({ - ...result, - term, - }), + .map((result) => { + if (extractHighlight) { + return resultToHTML({ + url: result.url, + title: highlight(result.title, term), + content: highlight(result.content, term) + }) + } else { + return resultToHTML(result) + } + } ) .join("\n") const anchors = [...document.getElementsByClassName("result-card")] diff --git a/content/notes/config.md b/content/notes/config.md index 7ee27d672..bc509c2b2 100644 --- a/content/notes/config.md +++ b/content/notes/config.md @@ -54,9 +54,13 @@ enableRecentNotes: false # whether to display and 'edit' button next to the last edited field # that links to github -enableGitHubEdit: false +enableGitHubEdit: true GitHubLink: https://github.com/jackyzha0/quartz/tree/hugo/content +# whether to use Operand to power semantic search +enableSemanticSearch: true +operandApiKey: "1e47d93b-1468-45b7-98d5-7f733d5e45e2" + # page description used for SEO description: Host your second brain and digital garden for free. Quartz features extremely fast full-text search, diff --git a/data/config.yaml b/data/config.yaml index 1b9021ddf..23bba0f4f 100644 --- a/data/config.yaml +++ b/data/config.yaml @@ -10,8 +10,10 @@ enableSPA: true enableFooter: true enableContextualBacklinks: true enableRecentNotes: false -enableGitHubEdit: false +enableGitHubEdit: true GitHubLink: https://github.com/jackyzha0/quartz/tree/hugo/content +enableSemanticSearch: true +operandApiKey: "1e47d93b-1468-45b7-98d5-7f733d5e45e2" description: Host your second brain and digital garden for free. Quartz features extremely fast full-text search, Wikilink support, backlinks, local graph, tags, and link previews. diff --git a/layouts/partials/github.html b/layouts/partials/github.html index a7b3d13ec..21adb63e3 100644 --- a/layouts/partials/github.html +++ b/layouts/partials/github.html @@ -1,3 +1,3 @@ {{if $.Site.Data.config.enableGitHubEdit}} -Edit Source +Edit Source {{end}} diff --git a/layouts/partials/search.html b/layouts/partials/search.html index 5b0bbb7de..86c8613ee 100644 --- a/layouts/partials/search.html +++ b/layouts/partials/search.html @@ -6,7 +6,13 @@ +{{if $.Site.Data.config.enableSemanticSearch}} +{{ $js := resources.Get "js/semantic-search.js" | resources.ExecuteAsTemplate "js/semantic-search.js" . | resources.Fingerprint "md5" | resources.Minify }} + +{{else}} -{{ $js := resources.Get "js/search.js" | resources.Fingerprint "md5" | resources.Minify }} +{{ $js := resources.Get "js/full-text-search.js" | resources.Fingerprint "md5" | resources.Minify }} +{{end}} +