diff --git a/_includes/javascript.html b/_includes/javascript.html index ad25c0a5..d25c1e75 100644 --- a/_includes/javascript.html +++ b/_includes/javascript.html @@ -9,3 +9,13 @@ + + + + diff --git a/_includes/navbar.html b/_includes/navbar.html index 6b51efd4..d94b3aef 100644 --- a/_includes/navbar.html +++ b/_includes/navbar.html @@ -14,7 +14,7 @@ -
+
diff --git a/assets/css/powsybl/_navbar.scss b/assets/css/powsybl/_navbar.scss index 63b9b9ff..7688e974 100644 --- a/assets/css/powsybl/_navbar.scss +++ b/assets/css/powsybl/_navbar.scss @@ -11,3 +11,8 @@ .content { margin-top: $navbar-height; } + +/* navbar has z-index: 1030 */ +ul.ui-autocomplete { + z-index: 1040; +} diff --git a/assets/js/search.js b/assets/js/search.js index 06cc4aaa..70c81fef 100644 --- a/assets/js/search.js +++ b/assets/js/search.js @@ -17,6 +17,17 @@ } } + function displaySearchException(searchException) { + var searchResults = document.getElementById('search-results'); + var displayString = "

ERROR

"; + if (searchException.name == "QueryParseError") { + displayString += "Your query contains an error.
" + searchException + "
For a description of the query syntax and query special characters ('*', ':', '^', '~', '+', '-'), please visit https://lunrjs.com/guides/searching.html"; + } else { + displayString += searchException; + } + searchResults.innerHTML = displayString; + } + function getQueryVariable(variable) { var query = window.location.search.substring(1); var vars = query.split('&'); @@ -32,6 +43,21 @@ var searchTerm = getQueryVariable('query'); + // Initalize lunr with the fields it will be searching on. I've given title + // a boost of 10 to indicate matches on this field are more important. + var idx = lunr(function () { + this.field('id'); + this.field('title', { boost: 10 }); + this.field('content'); + + for (var key in window.store) { // Add the data to lunr + this.add({ + 'id': key, + 'title': window.store[key].title, + 'content': window.store[key].content + }); + } + }); if (searchTerm) { document.getElementById('search-box').setAttribute("value", searchTerm); @@ -51,7 +77,88 @@ } }); - var results = idx.search(searchTerm); // Get lunr to perform a search - displaySearchResults(results, window.store); // We'll write this in the next section + try { + var results = idx.search(searchTerm); // Get lunr to perform a search + displaySearchResults(results, window.store); // We'll write this in the next section + } catch (searchException) { + displaySearchException(searchException); + } } + + $(document).ready(function() { + const storeUnstemmed = function(builder) { + + // Define a pipeline function that keeps the unstemmed word + const pipelineFunction = function(token) { + token.metadata['unstemmed'] = token.toString(); + return token; + }; + + // Register the pipeline function so the index can be serialised + lunr.Pipeline.registerFunction(pipelineFunction, 'storeUnstemmed'); + + // Add the pipeline function to both the indexing pipeline and the searching pipeline + builder.pipeline.before(lunr.stemmer, pipelineFunction); + + // Whitelist the unstemmed metadata key + builder.metadataWhitelist.push('unstemmed'); + }; + + lunr.tokenizer.separator = /\W+/ + const index = lunr(function() { + this.use(storeUnstemmed); + this.field('id'); + this.field('title', { boost: 10 }); + this.field('content'); + + for (var key in window.store) { // Add the data to lunr + this.add({ + 'id': key, + 'title': window.store[key].title, + 'content': window.store[key].content + }); + } + }); + const autoComplete = function (searchTerm) { + const results = index.query(function(q) { + // exact matches should have the highest boost + q.term(searchTerm, { boost : 100 }) + // wildcard matches should be boosted slightly + q.term(searchTerm, { + boost : 10, + usePipeline : true, + wildcard : lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING + }) + // finally, try a fuzzy search, without any boost + q.term(searchTerm, { boost : 1, usePipeline : false, editDistance : 1 }) + }); + if (!results.length) { + return ""; + } + return results.map(function(v, i, a) { // extract unstemmed terms + const unstemmedTerms = {}; + Object.keys(v.matchData.metadata).forEach(function(term) { + Object.keys(v.matchData.metadata[term]).forEach(function(field) { + v.matchData.metadata[term][field].unstemmed.forEach(function(word) { + unstemmedTerms[word] = true; + }); + }); + }); + return Object.keys(unstemmedTerms); + }).reduce(function(a, b) { // flatten + return a.concat(b); + }).filter(function(v, i, a) { // uniq + return a.indexOf(v) === i; + }).slice(0, 10); + } + + $( "#search-box" ).autocomplete({ + source: function(request, response) { + var results = autoComplete(request.term); + response(results); + }, + select: function(event, ui) { $("#search-box").val(ui.item.value); $("#search-form").submit(); } + }); + }); + })(); diff --git a/search.md b/search.md index e367edd4..75918630 100644 --- a/search.md +++ b/search.md @@ -4,19 +4,17 @@ title: Search ---
- - -