Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Autocomplete #97

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions _includes/javascript.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,13 @@
<script type="text/javascript" async
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.6/MathJax.js?config=TeX-MML-AM_CHTML"></script>
<script src="/assets/js/sidebar.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lunr.js/2.3.6/lunr.min.js" integrity="sha256-M/Awbb/BYh+Rh0aGjpQid26p1b2OBsrk2k9yAvQxPV0=" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
var link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = 'https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css';
document.getElementsByTagName('HEAD')[0].appendChild(link);
</script>
<script src="/assets/js/search.js"></script>
2 changes: 1 addition & 1 deletion _includes/navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<a class="nav-link mx-1" href="https://lists.lfenergy.org/g/powsybl" target="_blank" rel="noopener" aria-label="Mailing Lists" data-toggle="tooltip" data-placement="bottom" title="Mailing Lists"><i class="fas fa-envelope"></i></a>
<a class="nav-link mx-1 d-block d-md-none" href="/search"><i class="fas fa-search"></i></a>
</ul>
<form class="form-inline" action="/search" method="get">
<form id="search-form" class="form-inline" action="/search" method="get">
<input id="search-box" name="query" class="form-control d-none d-md-block ml-3" type="text" placeholder="Search" aria-label="Search" />
</form>
</nav>
5 changes: 5 additions & 0 deletions assets/css/powsybl/_navbar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@
.content {
margin-top: $navbar-height;
}

/* navbar has z-index: 1030 */
ul.ui-autocomplete {
z-index: 1040;
}
111 changes: 109 additions & 2 deletions assets/js/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@
}
}

function displaySearchException(searchException) {
var searchResults = document.getElementById('search-results');
var displayString = "<h2>ERROR</h2>";
if (searchException.name == "QueryParseError") {
displayString += "Your query contains an error.<br>" + searchException + "<br>For a description of the query syntax and query special characters ('*', ':', '^', '~', '+', '-'), please visit <a href='https://lunrjs.com/guides/searching.html'>https://lunrjs.com/guides/searching.html</a>";
} else {
displayString += searchException;
}
searchResults.innerHTML = displayString;
}

function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split('&');
Expand All @@ -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);

Expand All @@ -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(); }
});
});

})();
10 changes: 4 additions & 6 deletions search.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,17 @@ title: Search
---

<div id="search-results"></div>

<script>
window.store = {
{% for page in site.pages %}
{% if page.title.size > 0 and page.content.size > 0 and page.layout == "default" %}
"{{ page.url | slugify }}": {
"url": "{{ page.url }}",
"title": "{{ page.title | xml_escape }}",
"content": {{ page.content | strip_html | strip_newlines | jsonify }}
}
{% unless forloop.last %},{% endunless %}
"content": {{ page.content | strip_html | jsonify }}
},
{% endif %}
{% endfor %}
};
</script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/lunr.js/2.3.6/lunr.min.js" integrity="sha256-M/Awbb/BYh+Rh0aGjpQid26p1b2OBsrk2k9yAvQxPV0=" crossorigin="anonymous"></script>
<script src="/assets/js/search.js"></script>