You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by ki...@apache.org on 2023/04/07 11:09:33 UTC

[jena-site] branch main updated (cd14cb23a -> dd32b8a44)

This is an automated email from the ASF dual-hosted git repository.

kinow pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/jena-site.git


    from cd14cb23a add list search from jena.markmail.org excluding messages from jena-commits
     new 7905e130d Add basic search with Fuse.js (search engine), Mark.js (word highlighter) and Hugo (search index)
     new 4b3a1e52c Filter search results with uniq, add comments to search options, allow pages to be excluded from search
     new dd32b8a44 Display score, tweak search parameters, more comments

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 config.toml                  |   3 +
 layouts/_default/baseof.html |  10 ++-
 layouts/_default/index.json  |   7 ++
 layouts/_default/search.html | 200 +++++++++++++++++++++++++++++++++++++++++++
 source/search/__index.md     |   8 ++
 static/js/fuse.min.js        |   9 ++
 static/js/mark.min.js        |   7 ++
 7 files changed, 243 insertions(+), 1 deletion(-)
 create mode 100644 layouts/_default/index.json
 create mode 100644 layouts/_default/search.html
 create mode 100644 source/search/__index.md
 create mode 100644 static/js/fuse.min.js
 create mode 100644 static/js/mark.min.js


[jena-site] 03/03: Display score, tweak search parameters, more comments

Posted by ki...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kinow pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena-site.git

commit dd32b8a44ec71ba8bc94424da9e55d2b82b5f12e
Author: Bruno P. Kinoshita <ki...@users.noreply.github.com>
AuthorDate: Sun Feb 12 18:05:10 2023 +0100

    Display score, tweak search parameters, more comments
---
 layouts/_default/baseof.html |  1 +
 layouts/_default/search.html | 59 ++++++++++++++++++++++++++++----------------
 2 files changed, 39 insertions(+), 21 deletions(-)

diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html
index 497ee4b2c..df7a90076 100644
--- a/layouts/_default/baseof.html
+++ b/layouts/_default/baseof.html
@@ -185,6 +185,7 @@
 <script type="text/javascript">
 (function() {
     'use strict'
+    // Active menu items.
     /*
      * Find the link in the menu that corresponds to the currently open page.
      * NOTE: We call .querySelectorAll because we may have more than one link
diff --git a/layouts/_default/search.html b/layouts/_default/search.html
index ba98c9870..191b5e04e 100644
--- a/layouts/_default/search.html
+++ b/layouts/_default/search.html
@@ -7,7 +7,8 @@
   <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 class="pb-0 mb-0">${snippet}</p>
+      <p class="opacity-50 pt-0 mt-0"><small>Score: ${score}</small></p>
       <p>
         <small>
           ${ isset tags }Tags: ${tags}<br>${ end }
@@ -23,35 +24,51 @@
       const summaryInclude = 180;
       // See: https://fusejs.io/api/options.html
       const fuseOptions = {
-        // Whether to sort the result list, by score.
-        shouldSort: true,
+        // Indicates whether comparisons should be case sensitive.
+        isCaseSensitive: false,
+        // Whether the score should be included in the result set.
+        // A score of 0 indicates a perfect match, while a score of 1 indicates a complete mismatch.
+        includeScore: true,
         // Whether the matches should be included in the result set.
         // When true, each record in the result set will include the indices of the matched characters.
         // These can consequently be used for highlighting purposes.
         includeMatches: true,
-        // Whether the score should be included in the result set.
-        // A score of 0 indicates a perfect match, while a score of 1 indicates a complete mismatch.
-        includeScore: true,
-        // Determines approximately where in the text is the pattern expected to be found.
-        location: 0,
-        // Determines how close the match must be to the fuzzy location (specified by location).
-        // An exact letter match which is distance characters away from the fuzzy location would
-        // score as a complete mismatch.
-        // A distance of 0 requires the match be at the exact location specified.
-        // A distance of 1000 would require a perfect match to be within 800 characters of the
-        // location to be found using a threshold of 0.8.
-        distance: 100,
         // Only the matches whose length exceeds this value will be returned.
         // (For instance, if you want to ignore single character matches in the result, set it to 2).
         minMatchCharLength: 2,
+        // Whether to sort the result list, by score.
+        shouldSort: true,
         // List of keys that will be searched.
         // This supports nested paths, weighted search, searching in arrays of strings and objects.
         keys: [
-          {name: "title", weight: 0.5},
-          {name: "contents", weight: 0.4},
-          {name: "tags", weight: 0.95},
-          {name: "categories", weight: 0.05}
+          {name: "title", weight: 0.8},
+          {name: "contents", weight: 0.7},
+          // {name: "tags", weight: 0.95},
+          // {name: "categories", weight: 0.05}
         ],
+        // --- Fuzzy Matching Options
+        // Determines approximately where in the text is the pattern expected to be found.
+        location: 0,
+        // At what point does the match algorithm give up.
+        // A threshold of 0.0 requires a perfect match (of both letters and location),
+        // a threshold of 1.0 would match anything.
+        threshold: 0.2,
+        // Determines how close the match must be to the fuzzy location (specified by location).
+        // An exact letter match which is distance characters away from the fuzzy location would
+        // score as a complete mismatch. A distance of 0 requires the match be at the exact
+        // location specified. A distance of 1000 would require a perfect match to be within 800
+        // characters of the location to be found using a threshold of 0.8.
+        distance: 100,
+        // When true, search will ignore location and distance, so it won't matter where in
+        // the string the pattern appears.
+        //
+        // NOTE: These settings are used to calculate the Fuzziness Score (Bitap algorithm) in Fuse.js.
+        //       It calculates threshold (default 0.6) * distance (default (100), which gives 60 by
+        //       default, meaning it will search for the query-term within 60 characters from the location
+        //       (default 0). Since Jena docs may have very long text that includes the query term anywhere
+        //       we disable it with ignoreLocation: true.
+        //       For more: https://fusejs.io/concepts/scoring-theory.html#scoring-theory
+        ignoreLocation: true,
       };
 
       // =============================
@@ -106,7 +123,6 @@
         const templateDefinition = document.getElementById("search-result-template").innerHTML;
 
         results.forEach(function (value, key) {
-
           const contents = value.item.contents;
           let snippet = "";
           const snippetHighlights = [];
@@ -128,7 +144,8 @@
             link: value.item.permalink,
             tags: tags,
             categories: value.item.categories,
-            snippet: snippet
+            snippet: snippet,
+            score: value.score
           });
           searchResults.innerHTML += output;
 


[jena-site] 01/03: Add basic search with Fuse.js (search engine), Mark.js (word highlighter) and Hugo (search index)

Posted by ki...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kinow pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena-site.git

commit 7905e130de1df841d6700c472fb0b4502da26db9
Author: Bruno P. Kinoshita <ki...@users.noreply.github.com>
AuthorDate: Sun Feb 12 09:34:25 2023 +0100

    Add basic search with Fuse.js (search engine), Mark.js (word highlighter) and Hugo (search index)
---
 config.toml                  |   3 +
 layouts/_default/baseof.html |   9 ++-
 layouts/_default/index.json  |   5 ++
 layouts/_default/search.html | 164 +++++++++++++++++++++++++++++++++++++++++++
 source/search/__index.md     |   7 ++
 static/js/fuse.min.js        |   9 +++
 static/js/mark.min.js        |   7 ++
 7 files changed, 203 insertions(+), 1 deletion(-)

diff --git a/config.toml b/config.toml
index ce2da183d..4ab62692b 100644
--- a/config.toml
+++ b/config.toml
@@ -44,3 +44,6 @@ repositorySourceBranch = "main"
 
 siteRepositoryUrl = "https://github.com/apache/jena-site"
 siteRepositorySourceBranch = "main"
+
+[outputs]
+home = ["HTML", "RSS", "JSON"]
diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html
index 276a62ad6..497ee4b2c 100644
--- a/layouts/_default/baseof.html
+++ b/layouts/_default/baseof.html
@@ -101,7 +101,14 @@
                     </ul>
                 </li>
             </ul>
-
+            <form class="d-flex" role="search" action="/search" method="GET">
+                <div class="input-group">
+                    <input class="form-control border-end-0 border m-0" type="search" name="q" id="search-query" placeholder="Search...." aria-label="Search">
+                    <button class="btn btn-outline-secondary border-start-0 border" type="submit">
+                        <i class="bi-search"></i>
+                    </button>
+                </div>
+            </form>
             <ul class="navbar-nav ms-auto">
                 <li id="ask" class="nav-item"><a class="nav-link" href="/help_and_support/index.html"><span class="bi-patch-question"></span> Ask</a></li>
 
diff --git a/layouts/_default/index.json b/layouts/_default/index.json
new file mode 100644
index 000000000..ae08324d8
--- /dev/null
+++ b/layouts/_default/index.json
@@ -0,0 +1,5 @@
+{{- $.Scratch.Add "index" slice -}}
+{{- range .Site.RegularPages -}}
+{{- $.Scratch.Add "index" (dict "title" .Title "tags" .Params.tags "categories" .Params.categories "contents" .Plain "permalink" .Permalink) -}}
+{{- end -}}
+{{- $.Scratch.Get "index" | jsonify -}}
diff --git a/layouts/_default/search.html b/layouts/_default/search.html
new file mode 100644
index 000000000..79af2183c
--- /dev/null
+++ b/layouts/_default/search.html
@@ -0,0 +1,164 @@
+{{ define "main" }}
+<!-- Source: https://makewithhugo.com/add-search-to-a-hugo-site/ -->
+<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="/js/fuse.min.js" type="text/javascript" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
+  <script src="/js/mark.min.js" type="text/javascript" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
+  <script type="text/javascript">
+    (function() {
+      const summaryInclude = 180;
+      const 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
+      // =============================
+
+      const inputBox = document.getElementById('search-query');
+      if (inputBox !== null) {
+        const 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) {
+            const fuse = new Fuse(pages, fuseOptions);
+            const 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) {
+
+        const searchQuery = document.getElementById("search-query").value;
+        const searchResults = document.getElementById("search-results");
+
+        // pull template from hugo template definition
+        const templateDefinition = document.getElementById("search-result-template").innerHTML;
+
+        results.forEach(function (value, key) {
+
+          const contents = value.item.contents;
+          let snippet = "";
+          const snippetHighlights = [];
+
+          snippetHighlights.push(searchQuery);
+          snippet = contents.substring(0, summaryInclude * 2) + '&hellip;';
+
+          //replace values
+          let tags = ""
+          if (value.item.tags) {
+            value.item.tags.forEach(function (element) {
+              tags = tags + "<a href='/tags/" + element + "'>" + "#" + element + "</a> "
+            });
+          }
+
+          const 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) {
+            const instance = new Mark(document.getElementById('summary-' + key));
+            instance.mark(snipvalue);
+          });
+
+        });
+      }
+
+      function render(templateString, data) {
+        let 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
+        let 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, ' ');
+      }
+
+    })()
+  </script>
+</main>
+
+{{ end }}
diff --git a/source/search/__index.md b/source/search/__index.md
new file mode 100644
index 000000000..ec17b86dc
--- /dev/null
+++ b/source/search/__index.md
@@ -0,0 +1,7 @@
+---
+title: "Search Results"
+sitemap:
+priority : 0.1
+layout: "search"
+slug: index
+---
diff --git a/static/js/fuse.min.js b/static/js/fuse.min.js
new file mode 100644
index 000000000..d9da0b3c0
--- /dev/null
+++ b/static/js/fuse.min.js
@@ -0,0 +1,9 @@
+/**
+ * Fuse.js v6.6.2 - Lightweight fuzzy-search (http://fusejs.io)
+ *
+ * Copyright (c) 2022 Kiro Risk (http://kiro.me)
+ * All Rights Reserved. Apache Software License 2.0
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ */
+var e,t;e=this,t=function(){"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var n=1;n<arguments.length;n++){var r=null!=arguments[n]?arguments[n]:{};n%2?e(Object(r),!0).forEach((function(e){c(t,e,r[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDe [...]
diff --git a/static/js/mark.min.js b/static/js/mark.min.js
new file mode 100644
index 000000000..1eea05338
--- /dev/null
+++ b/static/js/mark.min.js
@@ -0,0 +1,7 @@
+/*!***************************************************
+* mark.js v8.11.1
+* https://markjs.io/
+* Copyright (c) 2014–2018, Julian Kühnel
+* Released under the MIT license https://git.io/vwTVl
+*****************************************************/
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Mark=t()}(this,function(){"use strict";var e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},t=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},n=function(){ [...]


[jena-site] 02/03: Filter search results with uniq, add comments to search options, allow pages to be excluded from search

Posted by ki...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kinow pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena-site.git

commit 4b3a1e52c691db4457df7f2e40ab6949f9de9079
Author: Bruno P. Kinoshita <ki...@users.noreply.github.com>
AuthorDate: Sun Feb 12 15:23:22 2023 +0100

    Filter search results with uniq, add comments to search options, allow pages to be excluded from search
---
 layouts/_default/index.json  |  4 +++-
 layouts/_default/search.html | 29 ++++++++++++++++++++++++-----
 source/search/__index.md     |  1 +
 3 files changed, 28 insertions(+), 6 deletions(-)

diff --git a/layouts/_default/index.json b/layouts/_default/index.json
index ae08324d8..f66614c96 100644
--- a/layouts/_default/index.json
+++ b/layouts/_default/index.json
@@ -1,5 +1,7 @@
 {{- $.Scratch.Add "index" slice -}}
 {{- range .Site.RegularPages -}}
+{{- if not .Params.excludeSearch -}}
 {{- $.Scratch.Add "index" (dict "title" .Title "tags" .Params.tags "categories" .Params.categories "contents" .Plain "permalink" .Permalink) -}}
 {{- end -}}
-{{- $.Scratch.Get "index" | jsonify -}}
+{{- end -}}
+{{- $.Scratch.Get "index" | uniq | jsonify -}}
diff --git a/layouts/_default/search.html b/layouts/_default/search.html
index 79af2183c..ba98c9870 100644
--- a/layouts/_default/search.html
+++ b/layouts/_default/search.html
@@ -21,20 +21,37 @@
   <script type="text/javascript">
     (function() {
       const summaryInclude = 180;
+      // See: https://fusejs.io/api/options.html
       const fuseOptions = {
+        // Whether to sort the result list, by score.
         shouldSort: true,
+        // Whether the matches should be included in the result set.
+        // When true, each record in the result set will include the indices of the matched characters.
+        // These can consequently be used for highlighting purposes.
         includeMatches: true,
+        // Whether the score should be included in the result set.
+        // A score of 0 indicates a perfect match, while a score of 1 indicates a complete mismatch.
         includeScore: true,
-        tokenize: true,
+        // Determines approximately where in the text is the pattern expected to be found.
         location: 0,
+        // Determines how close the match must be to the fuzzy location (specified by location).
+        // An exact letter match which is distance characters away from the fuzzy location would
+        // score as a complete mismatch.
+        // A distance of 0 requires the match be at the exact location specified.
+        // A distance of 1000 would require a perfect match to be within 800 characters of the
+        // location to be found using a threshold of 0.8.
         distance: 100,
-        minMatchCharLength: 1,
+        // Only the matches whose length exceeds this value will be returned.
+        // (For instance, if you want to ignore single character matches in the result, set it to 2).
+        minMatchCharLength: 2,
+        // List of keys that will be searched.
+        // This supports nested paths, weighted search, searching in arrays of strings and objects.
         keys: [
-          {name: "title", weight: 0.45},
+          {name: "title", weight: 0.5},
           {name: "contents", weight: 0.4},
-          {name: "tags", weight: 0.1},
+          {name: "tags", weight: 0.95},
           {name: "categories", weight: 0.05}
-        ]
+        ],
       };
 
       // =============================
@@ -83,6 +100,8 @@
         const searchQuery = document.getElementById("search-query").value;
         const searchResults = document.getElementById("search-results");
 
+        searchResults.innerHTML += `<p>Search returned ${results.length} matches.</p>`;
+
         // pull template from hugo template definition
         const templateDefinition = document.getElementById("search-result-template").innerHTML;
 
diff --git a/source/search/__index.md b/source/search/__index.md
index ec17b86dc..23e5b539c 100644
--- a/source/search/__index.md
+++ b/source/search/__index.md
@@ -4,4 +4,5 @@ sitemap:
 priority : 0.1
 layout: "search"
 slug: index
+excludeSearch: True
 ---