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 ed1b5d2  HIVE-29426: [Website] Improve search (#92)
ed1b5d2 is described below

commit ed1b5d22cef718b7c3ffc11a8202ac4bfde55722
Author: Simhadri Govindappa <[email protected]>
AuthorDate: Sat Jan 31 18:10:00 2026 +0530

    HIVE-29426: [Website] Improve search (#92)
    
    * HIVE-29426: [Website] Improve search
    
    Improves search results with better snippet generation using match indices 
and better fuzzy match for the search term.
    
    Also made some minor changes to the style of the results page.
    
    Lastly, added cache busting to force browsers to load the latest changes, 
instead of using old cached files during development.
---
 config.toml                              |   3 +
 themes/hive/layouts/_default/index.json  |   7 +-
 themes/hive/layouts/_default/search.html |  28 +++--
 themes/hive/layouts/partials/footer.html |   3 +-
 themes/hive/layouts/partials/head.html   |  13 ++-
 themes/hive/static/css/hive-theme.css    | 182 ++++++++++++++++++++++++++++++-
 themes/hive/static/js/search.js          |  96 ++++++++++++++--
 7 files changed, 299 insertions(+), 33 deletions(-)

diff --git a/config.toml b/config.toml
index 15f9fb9..0f41161 100644
--- a/config.toml
+++ b/config.toml
@@ -2,7 +2,10 @@ baseURL = 'https://hive.apache.org'
 languageCode = 'en-us'
 title = 'Hive Site'
 theme = 'hive'
+
+# Site version for cache busting
 [params]
+  version = "1.1.0"
   hiveLogo = "/images/hive.svg"
   logo = "/images/asf_logo.png"
   apacheURL = 'https://www.apache.org'
diff --git a/themes/hive/layouts/_default/index.json 
b/themes/hive/layouts/_default/index.json
index 91f95c9..f8a7700 100644
--- a/themes/hive/layouts/_default/index.json
+++ b/themes/hive/layouts/_default/index.json
@@ -3,14 +3,17 @@
 {{- $sc := newScratch -}}
 {{- if isset .Params "description" -}}
 {{- $sc.Add "ct" .Description  -}}
+{{- $sc.Add "ct" ". " -}}
 {{- end -}}
 
 {{- if isset .Params "about" -}}
 {{- range .Params.About.about_item }}
-{{- $sc.Add "ct" (print .title " " .subtitle " " .content) -}}
+{{- $sc.Add "ct" (print .title " " .subtitle " " .content " ") -}}
 {{- end -}}
 {{- end -}}
-{{- $sc.Add "ct" .Plain -}}
+{{- $plainContent := .Plain -}}
+{{- $plainContent = replaceRE "\\s{2,}" " " $plainContent -}}
+{{- $sc.Add "ct" $plainContent -}}
 {{- $content := $sc.Get "ct" }}
 
 {{ $date:= .PublishDate.Format "02"}}
diff --git a/themes/hive/layouts/_default/search.html 
b/themes/hive/layouts/_default/search.html
index d911947..2d0ce02 100644
--- a/themes/hive/layouts/_default/search.html
+++ b/themes/hive/layouts/_default/search.html
@@ -1,19 +1,23 @@
 {{ define "main" }}
 
-<main>
-    <div id="search-results"></div>
-    <div class="search-loading">Loading...</div>
+<main class="search-page">
+    <div class="container">
+        <div class="search-results-header">
+            <h1>Search Results</h1>
+            <div class="search-loading" style="display:none;">
+                <i class="fa fa-spinner fa-spin"></i> Searching...
+            </div>
+        </div>
+        <div id="search-results"></div>
+    </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>
+        <article class="search-result-item">
+            <h3 class="search-result-title">
+                <a href="${link}">${title}</a>
+            </h3>
+            <p class="search-result-snippet" id="summary-${key}">${snippet}</p>
+        </article>
     </script>
 
     <script src="{{ .Site.BaseURL }}/js/fuse.min.js"></script>
diff --git a/themes/hive/layouts/partials/footer.html 
b/themes/hive/layouts/partials/footer.html
index 2c05848..fa10c6f 100644
--- a/themes/hive/layouts/partials/footer.html
+++ b/themes/hive/layouts/partials/footer.html
@@ -52,4 +52,5 @@
     </div>
 
 </footer>
-<script src="{{ .Site.BaseURL }}/js/bootstrap.bundle.min.js"></script>
+<!-- JavaScript files with cache busting -->
+<script src="{{ .Site.BaseURL }}/js/bootstrap.bundle.min.js?v={{ 
.Site.Params.version }}"></script>
diff --git a/themes/hive/layouts/partials/head.html 
b/themes/hive/layouts/partials/head.html
index 613e870..c0e3970 100644
--- a/themes/hive/layouts/partials/head.html
+++ b/themes/hive/layouts/partials/head.html
@@ -24,12 +24,17 @@
         <meta name="viewport" content="width=device-width, initial-scale=1">
         <meta name="description" content="">
         <meta name="author" content="">
+        <!-- Cache control for development -->
+        <meta http-equiv="Cache-Control" content="no-cache, no-store, 
must-revalidate">
+        <meta http-equiv="Pragma" content="no-cache">
+        <meta http-equiv="Expires" content="0">
         <title>{{ .Title }}</title>
         <link rel="icon" href="/images/hive.svg" sizes="any" 
type="image/svg+xml">
-        <link rel="stylesheet" href="{{ .Site.BaseURL }}/css/hive-theme.css">
-        <link rel="stylesheet" href="{{ .Site.BaseURL 
}}/css/font-awesome.all.min.css">
-        <link rel="stylesheet" href="{{ .Site.BaseURL 
}}/css/bootstrap.min.css">
-        <link rel="stylesheet" href="{{ .Site.BaseURL }}/css/termynal.css">
+        <!-- CSS files with cache busting -->
+        <link rel="stylesheet" href="{{ .Site.BaseURL 
}}/css/hive-theme.css?v={{ .Site.Params.version }}">
+        <link rel="stylesheet" href="{{ .Site.BaseURL 
}}/css/font-awesome.all.min.css?v={{ .Site.Params.version }}">
+        <link rel="stylesheet" href="{{ .Site.BaseURL 
}}/css/bootstrap.min.css?v={{ .Site.Params.version }}">
+        <link rel="stylesheet" href="{{ .Site.BaseURL }}/css/termynal.css?v={{ 
.Site.Params.version }}">
         <link rel="apple-touch-icon" sizes="180x180" href="{{ .Site.BaseURL 
}}/images/apple-touch-icon.png">
         <link rel="icon" type="image/png" sizes="32x32" href="{{ .Site.BaseURL 
}}/images/favicon-32x32.png">
         <link rel="icon" type="image/png" sizes="16x16" href="{{ .Site.BaseURL 
}}/images/favicon-16x16.png">
diff --git a/themes/hive/static/css/hive-theme.css 
b/themes/hive/static/css/hive-theme.css
index 06c70f5..0a71804 100644
--- a/themes/hive/static/css/hive-theme.css
+++ b/themes/hive/static/css/hive-theme.css
@@ -302,7 +302,7 @@ features {
 
 .search-button {
     border: none;
-    background: #17a2b8;
+    background: #007bff;
     color: #ffffff;
     padding: 0.5rem 0.75rem;
     cursor: pointer;
@@ -310,7 +310,7 @@ features {
 }
 
 .search-button:hover {
-    background: #138496;
+    background: #0056b3;
 }
 
 .search-button i {
@@ -2704,3 +2704,181 @@ features.container {
 }
 
 
+
+/* ====================================== 
+   SEARCH RESULTS PAGE STYLES
+   ====================================== */
+
+.search-page {
+    max-width: 1400px;
+    margin: 0 auto;
+    padding: 2rem 3rem;
+    min-height: 60vh;
+}
+
+.search-results-header {
+    margin-bottom: 2.5rem;
+}
+
+.search-results-header h1 {
+    font-size: 2.5rem;
+    font-weight: 600;
+    color: #1a202c;
+    margin: 0 0 1rem 0;
+}
+
+.search-loading {
+    color: #206cd6;
+    font-size: 1rem;
+    padding: 1rem 0;
+    display: flex;
+    align-items: center;
+    gap: 0.75rem;
+}
+
+.search-loading i {
+    font-size: 1.2rem;
+}
+
+/* Search Results Empty State */
+.search-results-empty {
+    text-align: center;
+    padding: 3rem 2rem;
+    color: #6b7280;
+}
+
+.search-results-empty i {
+    font-size: 3rem;
+    color: #cbd5e0;
+    margin-bottom: 1rem;
+}
+
+/* Individual Search Result Item */
+.search-result-item {
+    background: white;
+    border: 1px solid #e2e8f0;
+    border-radius: 12px;
+    padding: 2rem 2.5rem;
+    margin-bottom: 2rem;
+    transition: all 0.3s ease;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+}
+
+.search-result-item:hover {
+    border-color: #206cd6;
+    box-shadow: 0 8px 20px rgba(32, 108, 214, 0.12);
+    transform: translateY(-3px);
+}
+
+.search-result-title {
+    font-size: 1.75rem;
+    font-weight: 600;
+    margin: 0 0 1rem 0;
+}
+
+.search-result-title a {
+    color: #206cd6;
+    text-decoration: none;
+    transition: color 0.2s ease;
+}
+
+.search-result-title a:hover {
+    color: #1a5ba8;
+    text-decoration: underline;
+}
+
+.search-result-snippet {
+    color: #4a5568;
+    font-size: 1.05rem;
+    line-height: 1.7;
+    margin: 0 0 1.25rem 0;
+}
+
+/* Highlight matched terms */
+.search-result-snippet mark {
+    background: #fef3c7;
+    color: #92400e;
+    padding: 0.1rem 0.2rem;
+    border-radius: 3px;
+    font-weight: 600;
+}
+
+.search-result-meta {
+    display: flex;
+    align-items: center;
+    gap: 1rem;
+    flex-wrap: wrap;
+    font-size: 0.85rem;
+    color: #6b7280;
+}
+
+.search-result-link {
+    color: #10b981;
+    font-family: 'Monaco', 'Courier New', monospace;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    max-width: 400px;
+}
+
+.search-result-tags {
+    display: flex;
+    align-items: center;
+    gap: 0.5rem;
+}
+
+.search-result-tags i {
+    color: #9ca3af;
+}
+
+.search-result-tags a {
+    color: #206cd6;
+    text-decoration: none;
+    padding: 0.25rem 0.5rem;
+    background: #eff6ff;
+    border-radius: 4px;
+    transition: all 0.2s ease;
+}
+
+.search-result-tags a:hover {
+    background: #dbeafe;
+    color: #1a5ba8;
+}
+
+/* Search Result Count */
+.search-results-count {
+    padding: 1rem 0;
+    color: #6b7280;
+    font-size: 0.95rem;
+    margin-bottom: 1rem;
+    border-bottom: 1px solid #e2e8f0;
+}
+
+/* Mobile Responsive */
+@media (max-width: 768px) {
+    .search-page {
+        padding: 1rem;
+    }
+    
+    .search-results-header h1 {
+        font-size: 1.75rem;
+    }
+    
+    .search-result-item {
+        padding: 1.25rem;
+    }
+    
+    .search-result-title {
+        font-size: 1.25rem;
+    }
+    
+    .search-result-link {
+        max-width: 200px;
+    }
+    
+    .search-result-meta {
+        flex-direction: column;
+        align-items: flex-start;
+        gap: 0.5rem;
+    }
+}
diff --git a/themes/hive/static/js/search.js b/themes/hive/static/js/search.js
index 6d1595d..f14e3ca 100644
--- a/themes/hive/static/js/search.js
+++ b/themes/hive/static/js/search.js
@@ -3,10 +3,13 @@ var fuseOptions = {
     shouldSort: true,
     includeMatches: true,
     includeScore: true,
-    tokenize: true,
+    threshold: 0.2,  // Stricter matching - lower value = more exact matches 
required
     location: 0,
-    distance: 100,
+    distance: 1000,  // Increased from 100 to search entire content
     minMatchCharLength: 1,
+    ignoreLocation: true,  // Search entire string, not just near location
+    isCaseSensitive: false,  // Case-insensitive search
+    findAllMatches: true,  // Find all matching items
     keys: [
         {name: "title", weight: 0.45},
         {name: "contents", weight: 0.4},
@@ -70,23 +73,92 @@ function populateResults(results) {
         var snippet = "";
         var snippetHighlights = [];
 
+        // Add both the full query and individual terms for highlighting
         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> "
-            });
+        
+        // Find the best matching snippet - search for where query terms 
actually appear
+        var matchIndex = -1;
+        var bestSnippet = "";
+        
+        // Split search query into words and find where they appear together
+        var searchTerms = 
searchQuery.toLowerCase().split(/\s+/).filter(function(term) { 
+            return term.length > 2; // Ignore short words
+        });
+        
+        // Add individual terms to highlights
+        searchTerms.forEach(function(term) {
+            snippetHighlights.push(term);
+        });
+        
+        if (searchTerms.length > 0) {
+            var contentsLower = contents.toLowerCase();
+            var bestScore = -1;
+            var bestPosition = -1;
+            
+            // Find the position where search terms are closest together
+            for (var i = 0; i < contents.length - 100; i += 50) {
+                var windowEnd = Math.min(i + 400, contents.length);
+                var window = contentsLower.substring(i, windowEnd);
+                var score = 0;
+                var foundTerms = 0;
+                
+                searchTerms.forEach(function(term) {
+                    var termIndex = window.indexOf(term);
+                    if (termIndex >= 0) {
+                        foundTerms++;
+                        // Prefer matches closer to the start of the window
+                        score += (200 - termIndex);
+                    }
+                });
+                
+                // Boost score if multiple terms found in this window
+                score *= foundTerms;
+                
+                if (score > bestScore && foundTerms > 0) {
+                    bestScore = score;
+                    bestPosition = i;
+                }
+            }
+            
+            if (bestPosition >= 0) {
+                matchIndex = bestPosition;
+                
+                // Extract snippet centered on this position
+                var start = Math.max(0, matchIndex - summaryInclude / 2);
+                var end = Math.min(contents.length, matchIndex + 
summaryInclude * 1.5);
+                
+                // Try to find sentence boundaries
+                var sentenceStart = contents.lastIndexOf('. ', matchIndex);
+                if (sentenceStart > start && sentenceStart < matchIndex && 
(matchIndex - sentenceStart) < 100) {
+                    start = sentenceStart + 2;
+                }
+                
+                var sentenceEnd = contents.indexOf('. ', end - 50);
+                if (sentenceEnd > matchIndex && sentenceEnd <= end && 
(sentenceEnd - matchIndex) < 200) {
+                    end = sentenceEnd + 1;
+                }
+                
+                bestSnippet = contents.substring(start, end).trim();
+                
+                // Add ellipsis
+                var needsStartEllipsis = start > 0;
+                var needsEndEllipsis = end < contents.length;
+                snippet = (needsStartEllipsis ? '&hellip; ' : '') + 
+                         bestSnippet + 
+                         (needsEndEllipsis ? ' &hellip;' : '');
+            }
+        }
+        
+        // Fallback if no match found
+        if (!snippet) {
+            snippet = contents.substring(0, summaryInclude * 2).trim() + 
'&hellip;';
         }
 
+        //replace values
         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;

Reply via email to