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) + '&hellip;';
+
+        //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, ' ');
+}

Reply via email to