Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Integrate lunrjs with a Hugo (gohugo.io) site.
const fs = require('fs').promises;
const {promisify} = require('util');
const frontMatterParser = require('parser-front-matter');
const parse = promisify(frontMatterParser.parse.bind(frontMatterParser));
async function loadPostsWithFrontMatter(postsDirectoryPath) {
const postNames = await fs.readdir(postsDirectoryPath);
const posts = await Promise.all(
postNames.map(async fileName => {
const fileContent = await fs.readFile(
`${postsDirectoryPath}/${fileName}`,
'utf8'
);
const {content, data} = await parse(fileContent);
return {
content: content.slice(0, 3000),
...data
};
})
);
return posts;
}
const lunrjs = require('lunr');
function makeIndex(posts) {
return lunrjs(function() {
this.ref('title');
this.field('title');
this.field('content');
this.field('tags');
posts.forEach(p => {
this.add(p);
});
});
}
async function run() {
const posts = await loadPostsWithFrontMatter(`${__dirname}/content/post`);
const index = makeIndex(posts);
console.log(JSON.stringify(index));
}
run()
.then(() => process.exit(0))
.catch(error => {
console.error(error.stack);
process.exit(1);
});
node ./build-lunrjs-index.js > static/search-index.json
{
"devDependencies": {
"lunr": "^2.3.6",
"parser-front-matter": "^1.6.4"
}
}
<form method="get" action="">
<input id="search" name="q" type="text" />
<button type="submit" class="button">Search</button>
<a href="/search">Clear</a>
</form>
<div id="#app"></div>
<script src="https://unpkg.com/lunr/lunr.js"></script>
<!-- Generate a list of posts so we can display them -->
{{ $p := slice }}
{{ range (where .Site.RegularPages "Section" "==" "post") }}
{{ $post := dict "link" .RelPermalink "title" .Title "content" (substr .Plain 0 200) -}}
{{ $p = $p | append $post -}}
{{ end }}
<script>
const posts = JSON.parse(
{{ $p | jsonify }}
);
const query = new URLSearchParams(window.location.search);
const searchString = query.get('q');
document.querySelector('#search').value = searchString;
const $target = document.querySelector('#app');
// Our index uses title as a reference
const postsByTitle = posts.reduce((acc, curr) => {
acc[curr.title] = curr;
return acc;
}, {});
fetch('/gen/search-index.json').then(function (res) {
return res.json();
}).then(function (data) {
const index = lunr.Index.load(data);
const matches = index.search(searchString);
const matchPosts = [];
matches.forEach((m) => {
matchPosts.push(postsByTitle[m.ref]);
});
if (matchPosts.length > 0) {
$target.innerHTML = matchPosts.map(p => {
return `<div>
<h3><a href="${p.link}">${p.title}</a></h3>
<p>${p.content}...</p>
</div>`;
}).join('');
} else {
$target.innerHTML = `<div>No search results found</div>`;
}
});
@psharma04

This comment has been minimized.

Copy link

commented Aug 7, 2019

Lines 10-14 are displaying as text, not doing anything.

@HugoDF

This comment has been minimized.

Copy link
Owner Author

commented Aug 8, 2019

@psharma04 the HTML only works as a Hugo template

@oonid

This comment has been minimized.

Copy link

commented Aug 11, 2019

@HugoDF do you have any example how to integrate search.html to Hugo template? or may be another article at codewithhugo? thank you.

@HugoDF

This comment has been minimized.

Copy link
Owner Author

commented Aug 11, 2019

The post is at https://codewithhugo.com/hugo-lunrjs-search-index/

The build-lunrjs-index.js script needs to be built before the corresponding Hugo template is used to generate the site.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.