This is an automated email from the ASF dual-hosted git repository.
simhadrig pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hive-site.git
The following commit(s) were added to refs/heads/main by this push:
new 1e477f1 HIVE-28809: Add search feature to hive website (#23)
(Simhadri Govindappa reviewed by Ayush Saxena)
1e477f1 is described below
commit 1e477f16c5f6cf1004943e8242d4c1664ff1727a
Author: Simhadri Govindappa <[email protected]>
AuthorDate: Sat Mar 8 23:55:47 2025 +0530
HIVE-28809: Add search feature to hive website (#23) (Simhadri Govindappa
reviewed by Ayush Saxena)
---
config.toml | 3 +
content/search/_index.md | 7 ++
themes/hive/layouts/_default/index.json | 20 +++++
themes/hive/layouts/_default/search.html | 25 ++++++
themes/hive/layouts/partials/menu.html | 6 ++
themes/hive/static/css/hive-theme.css | 62 +++++++++++++-
themes/hive/static/js/search.js | 136 +++++++++++++++++++++++++++++++
7 files changed, 258 insertions(+), 1 deletion(-)
diff --git a/config.toml b/config.toml
index 21304fc..24cd5fb 100644
--- a/config.toml
+++ b/config.toml
@@ -58,3 +58,6 @@ theme = 'hive'
cbo =
"https://cwiki.apache.org/confluence/display/Hive/Cost-based+optimization+in+Hive"
llap = "https://cwiki.apache.org/confluence/display/Hive/LLAP"
iceberg = "https://iceberg.apache.org/docs/latest/hive/"
+
+[outputs]
+ home = ["HTML", "RSS", "JSON"]
\ No newline at end of file
diff --git a/content/search/_index.md b/content/search/_index.md
new file mode 100644
index 0000000..cafa080
--- /dev/null
+++ b/content/search/_index.md
@@ -0,0 +1,7 @@
+---
+title: "Search"
+date: 2024-12-25T17:12:03+05:30
+sitemap:
+priority : 0.1
+layout: "search"
+---
diff --git a/themes/hive/layouts/_default/index.json
b/themes/hive/layouts/_default/index.json
new file mode 100644
index 0000000..91f95c9
--- /dev/null
+++ b/themes/hive/layouts/_default/index.json
@@ -0,0 +1,20 @@
+{{- $.Scratch.Add "index" slice -}}
+{{- range site.RegularPages -}}
+{{- $sc := newScratch -}}
+{{- if isset .Params "description" -}}
+{{- $sc.Add "ct" .Description -}}
+{{- end -}}
+
+{{- if isset .Params "about" -}}
+{{- range .Params.About.about_item }}
+{{- $sc.Add "ct" (print .title " " .subtitle " " .content) -}}
+{{- end -}}
+{{- end -}}
+{{- $sc.Add "ct" .Plain -}}
+{{- $content := $sc.Get "ct" }}
+
+{{ $date:= .PublishDate.Format "02"}}
+{{- $.Scratch.Add "index" (dict "title" .Title "date" $date "tags"
.Params.tags "image" .Params.image "categories"
+.Params.categories "contents" $content "permalink" .Permalink) -}}
+{{- end -}}
+{{- $.Scratch.Get "index" | jsonify -}}
\ No newline at end of file
diff --git a/themes/hive/layouts/_default/search.html
b/themes/hive/layouts/_default/search.html
new file mode 100644
index 0000000..032c74b
--- /dev/null
+++ b/themes/hive/layouts/_default/search.html
@@ -0,0 +1,25 @@
+{{ define "main" }}
+
+<main>
+ <div id="search-results"></div>
+ <div class="search-loading">Loading...</div>
+
+ <script id="search-result-template" type="text/x-js-template">
+ <div id="summary-${key}">
+ <h3><a href="${link}">${title}</a></h3>
+ <p>${snippet}</p>
+ <p>
+ <small>
+ ${ isset tags }Tags: ${tags}<br>${ end }
+ </small>
+ </p>
+ </div>
+ </script>
+
+ <script
src="https://cdnjs.cloudflare.com/ajax/libs/fuse.js/6.6.2/fuse.min.js"
integrity="sha512-Nqw1tH3mpavka9cQCc5zWWEZNfIPdOYyQFjlV1NvflEtQ0/XI6ZQ+H/D3YgJdqSUJlMLAPRj/oXlaHCFbFCjoQ=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
+
+ <script
src="https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js"
integrity="sha512-5CYOlHXGh6QpOFA/TeTylKLWfB3ftPsde7AnmhuitiTX4K5SqCLBeKro6sPS8ilsz1Q4NRx3v8Ko2IBiszzdww=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
+ <script src="{{ .Site.BaseURL }}/js/search.js"></script>
+</main>
+
+{{ end }}
diff --git a/themes/hive/layouts/partials/menu.html
b/themes/hive/layouts/partials/menu.html
index 1a8cfcb..360efde 100644
--- a/themes/hive/layouts/partials/menu.html
+++ b/themes/hive/layouts/partials/menu.html
@@ -108,6 +108,12 @@
<li><a class="dropdown-item" href="{{
.Site.Params.apache.apacheUrl }}">Website</a></li>
</ul>
</li>
+ <li>
+ <form action="/search" method="GET" class="search-bar">
+ <input type="search" name="q" id="search-query"
placeholder="Search..." class="search-input">
+ <button type="submit"
class="search-button">Search</button>
+ </form>
+ </li>
</ul>
</div>
</div>
diff --git a/themes/hive/static/css/hive-theme.css
b/themes/hive/static/css/hive-theme.css
index 4e1d25a..b641834 100644
--- a/themes/hive/static/css/hive-theme.css
+++ b/themes/hive/static/css/hive-theme.css
@@ -268,6 +268,64 @@ p,
padding-bottom:0rem;
}
+/* General Styling */
+.search-bar {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 5px;
+ border: 2px solid #000000;
+ border-radius: 8px;
+ background-color: #000000;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+/* Input Styling */
+.search-input {
+ flex-grow: 1;
+ padding: 8px 12px;
+ border: none;
+ border-radius: 4px;
+ outline: none;
+ font-size: 16px;
+ background-color: #f4f4f4;
+ color: #333;
+ transition: background-color 0.2s ease, box-shadow 0.2s ease;
+}
+
+.search-input:focus {
+ background-color: #ffffff;
+ box-shadow: 0 2px 6px rgba(0, 123, 255, 0.4);
+}
+
+/* Button Styling */
+.search-button {
+ padding: 8px 16px;
+ font-size: 16px;
+ font-weight: bold;
+ border: none;
+ border-radius: 4px;
+ background-color: #007bff;
+ color: white;
+ cursor: pointer;
+ transition: background-color 0.3s ease, box-shadow 0.2s ease;
+}
+
+.search-button:hover {
+ background-color: #0056b3;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+}
+
+.search-button:active {
+ background-color: #004080;
+}
+
+/* Additional Styling for Dark Theme Feel */
+.search-bar {
+ color: #000;
+}
+
+
/* https://github.com/mrmrs/fluidity */
img,
@@ -353,4 +411,6 @@ row{
span{
word-wrap: break-word;
-}
\ No newline at end of file
+}
+
+
diff --git a/themes/hive/static/js/search.js b/themes/hive/static/js/search.js
new file mode 100644
index 0000000..6d1595d
--- /dev/null
+++ b/themes/hive/static/js/search.js
@@ -0,0 +1,136 @@
+var summaryInclude = 180;
+var fuseOptions = {
+ shouldSort: true,
+ includeMatches: true,
+ includeScore: true,
+ tokenize: true,
+ location: 0,
+ distance: 100,
+ minMatchCharLength: 1,
+ keys: [
+ {name: "title", weight: 0.45},
+ {name: "contents", weight: 0.4},
+ {name: "tags", weight: 0.1},
+ {name: "categories", weight: 0.05}
+ ]
+};
+
+// =============================
+// Search
+// =============================
+
+var inputBox = document.getElementById('search-query');
+if (inputBox !== null) {
+ var searchQuery = param("q");
+ if (searchQuery) {
+ inputBox.value = searchQuery || "";
+ executeSearch(searchQuery, false);
+ } else {
+ document.getElementById('search-results').innerHTML = '<p
class="search-results-empty">Please enter a word or phrase above, or see <a
href="/tags/">all tags</a>.</p>';
+ }
+}
+
+function executeSearch(searchQuery) {
+
+ show(document.querySelector('.search-loading'));
+
+ fetch('/index.json').then(function (response) {
+ if (response.status !== 200) {
+ console.log('Looks like there was a problem. Status Code: ' +
response.status);
+ return;
+ }
+ // Examine the text in the response
+ response.json().then(function (pages) {
+ var fuse = new Fuse(pages, fuseOptions);
+ var result = fuse.search(searchQuery);
+ if (result.length > 0) {
+ populateResults(result);
+ } else {
+ document.getElementById('search-results').innerHTML = '<p
class=\"search-results-empty\">No matches found</p>';
+ }
+ hide(document.querySelector('.search-loading'));
+ })
+ .catch(function (err) {
+ console.log('Fetch Error :-S', err);
+ });
+ });
+}
+
+function populateResults(results) {
+
+ var searchQuery = document.getElementById("search-query").value;
+ var searchResults = document.getElementById("search-results");
+
+ // pull template from hugo template definition
+ var templateDefinition =
document.getElementById("search-result-template").innerHTML;
+
+ results.forEach(function (value, key) {
+
+ var contents = value.item.contents;
+ var snippet = "";
+ var snippetHighlights = [];
+
+ snippetHighlights.push(searchQuery);
+ snippet = contents.substring(0, summaryInclude * 2) + '…';
+
+ //replace values
+ var tags = ""
+ if (value.item.tags) {
+ value.item.tags.forEach(function (element) {
+ tags = tags + "<a href='/tags/" + element + "'>" + "#" +
element + "</a> "
+ });
+ }
+
+ var output = render(templateDefinition, {
+ key: key,
+ title: value.item.title,
+ link: value.item.permalink,
+ tags: tags,
+ categories: value.item.categories,
+ snippet: snippet
+ });
+ searchResults.innerHTML += output;
+
+ snippetHighlights.forEach(function (snipvalue, snipkey) {
+ var instance = new Mark(document.getElementById('summary-' + key));
+ instance.mark(snipvalue);
+ });
+
+ });
+}
+
+function render(templateString, data) {
+ var conditionalMatches, conditionalPattern, copy;
+ conditionalPattern = /\$\{\s*isset ([a-zA-Z]*) \s*\}(.*)\$\{\s*end\s*}/g;
+ //since loop below depends on re.lastInxdex, we use a copy to capture any
manipulations whilst inside the loop
+ copy = templateString;
+ while ((conditionalMatches = conditionalPattern.exec(templateString)) !==
null) {
+ if (data[conditionalMatches[1]]) {
+ //valid key, remove conditionals, leave contents.
+ copy = copy.replace(conditionalMatches[0], conditionalMatches[2]);
+ } else {
+ //not valid, remove entire section
+ copy = copy.replace(conditionalMatches[0], '');
+ }
+ }
+ templateString = copy;
+ //now any conditionals removed we can do simple substitution
+ var key, find, re;
+ for (key in data) {
+ find = '\\$\\{\\s*' + key + '\\s*\\}';
+ re = new RegExp(find, 'g');
+ templateString = templateString.replace(re, data[key]);
+ }
+ return templateString;
+}
+
+// Helper Functions
+function show(elem) {
+ elem.style.display = 'block';
+}
+function hide(elem) {
+ elem.style.display = 'none';
+}
+function param(name) {
+ return decodeURIComponent((location.search.split(name + '=')[1] ||
'').split('&')[0]).replace(/\+/g, ' ');
+}