Merge pull request #17 from koirand/search

Imprement search engine by ngram
Close #5
This commit is contained in:
Kazuki Koide 2019-03-26 01:05:07 +09:00 committed by GitHub
commit b4e0b7c0c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 3556 additions and 29 deletions

View file

@ -22,6 +22,6 @@
{{ partial "footer.html" . }} {{ partial "footer.html" . }}
<script src="/js/jquery-3.3.1.min.js"></script> <script src="/js/jquery-3.3.1.min.js"></script>
<script src="/js/jquery.mark.es6.min.js"></script> <script src="/js/jquery.mark.es6.min.js"></script>
<script src="/js/fuse.min.js"></script> <script src="/js/lunr.js"></script>
<script src="/js/search.js"></script> <script src="/js/search.js"></script>
{{ end }} {{ end }}

File diff suppressed because one or more lines are too long

3471
static/js/lunr.js Executable file

File diff suppressed because it is too large Load diff

View file

@ -1,25 +1,87 @@
var fuse var lunrIndex
var lunrResult
var pagesIndex
var bigramTokeniser = function (obj, metadata) {
if (obj == null || obj == undefined) {
return []
}
if (Array.isArray(obj)) {
return obj.map(function (t) {
return new lunr.Token(
lunr.utils.asString(t).toLowerCase(),
lunr.utils.clone(metadata)
)
})
}
var str = obj.toString().trim().toLowerCase(),
tokens = []
for(var i = 0; i <= str.length - 2; i++) {
var tokenMetadata = lunr.utils.clone(metadata) || {}
tokenMetadata["position"] = [i, i + 2]
tokenMetadata["index"] = tokens.length
tokens.push(
new lunr.Token (
str.slice(i, i + 2),
tokenMetadata
)
)
}
return tokens
}
var queryNgramSeparator = function (query) {
var str = query.toString().trim().toLowerCase(),
tokens = []
for(var i = 0; i <= str.length - 2; i++) {
tokens.push(str.slice(i, i + 2))
}
return tokens.join(' ')
}
/** /**
* Preparation for searching * Preparation for using lunr.js
*/ */
function initSearch () { function initLunr () {
$.getJSON('index.json').done(function (index) { $.getJSON('index.json').done(function (index) {
var options = { pagesIndex = index
shouldSort: true, lunrIndex = lunr(function () {
tokenize: true, this.tokenizer = bigramTokeniser
matchAllTokens: true, this.pipeline.reset()
threshold: 0.3, this.ref('ref')
minMatchCharLength: 5, this.field('title', { boost: 10 })
keys: ['title', 'body'] this.field('body')
this.metadataWhitelist = ['position']
for (var page of pagesIndex) {
this.add(page)
} }
fuse = new Fuse(index, options) })
}).fail(function (jqxhr, textStatus, error) { }).fail(function (jqxhr, textStatus, error) {
var err = textStatus + ', ' + error var err = textStatus + ', ' + error
console.error('Error getting Hugo index flie:', err) console.error('Error getting Hugo index flie:', err)
}) })
} }
/**
* Searching pages using lunr
* @param {String} query Query string for searching
* @return {Object[]} Array of search results
*/
function search (query) {
lunrResult = lunrIndex.search(queryNgramSeparator(query))
return lunrResult.map(function (result) {
return pagesIndex.filter(function (page) {
return page.ref === result.ref
})[0]
})
}
/** /**
* Setup UI for Search * Setup UI for Search
*/ */
@ -33,7 +95,7 @@ function initUI () {
// Event when chenging query // Event when chenging query
$('#searchBoxInput').keyup(function () { $('#searchBoxInput').keyup(function () {
var $searchResults = $('#searchResults') var $searchResults = $('#searchResults')
var query = $(this).val().trim() var query = $(this).val()
// Icon switching // Icon switching
if (query.length) { if (query.length) {
@ -51,7 +113,7 @@ function initUI () {
} }
// Display search results // Display search results
renderResults(fuse.search(query)) renderResults(search(query))
$searchResults.show() $searchResults.show()
}) })
@ -66,7 +128,7 @@ function initUI () {
function renderResults (results) { function renderResults (results) {
var $searchResults = $('#searchResults') var $searchResults = $('#searchResults')
var query = $('#searchBoxInput').val() var query = $('#searchBoxInput').val()
var SUMMARY_INCLUDE = 50 var BODY_LENGTH = 100
var MAX_PAGES = 10 var MAX_PAGES = 10
// Clear search result // Clear search result
@ -81,10 +143,13 @@ function renderResults (results) {
// Only show the ten first results // Only show the ten first results
results.slice(0, MAX_PAGES).forEach(function (result, idx) { results.slice(0, MAX_PAGES).forEach(function (result, idx) {
var $searchResultPage = $('<div class="searchResultPage">') var $searchResultPage = $('<div class="searchResultPage">')
var matchPosition = result.body.indexOf(query.split(' ')[0]) var metadata = lunrResult[idx].matchData.metadata
var bodyStartPosition = matchPosition - SUMMARY_INCLUDE > 0 ? matchPosition - SUMMARY_INCLUDE : 0 var matchPosition = metadata[Object.keys(metadata)[0]].body ? metadata[Object.keys(metadata)[0]].body.position[0][0] : 0
var bodyStartPosition = (matchPosition - (BODY_LENGTH / 2) > 0) ? matchPosition - (BODY_LENGTH / 2) : 0
$searchResultPage.append('<a class="searchResultTitle" href="' + result.ref + '">' + result.title + '</a>') $searchResultPage.append('<a class="searchResultTitle" href="' + result.ref + '">' + result.title + '</a>')
$searchResultPage.append('<div class="searchResultBody">' + result.body.substr(bodyStartPosition, SUMMARY_INCLUDE * 2) + '</div>')
$searchResultPage.append('<div class="searchResultBody">' + result.body.substr(bodyStartPosition, BODY_LENGTH) + '</div>')
$searchResults.append($searchResultPage) $searchResults.append($searchResultPage)
// Highlight keyword // Highlight keyword
@ -92,7 +157,7 @@ function renderResults (results) {
}) })
} }
initSearch() initLunr()
$(function () { $(function () {
initUI() initUI()