lunr.js and Jekyll
I recently spent some time hooking up a Jekyll site with lunr.js. Lunr.js is a full text client-side search engine and it works rather well. It took me a few tries to understand how lunr works and then translate that into a Jekyll site (I had some help from this post) so here’s a walk through of I how got it all connected.
I also built a live demo with my site: https://katydecorah.com/search
Tell lunr all about your fields
I started off by creating a file called, lunr-feed.js and adding front matter since I’ll be using logic to loop through my posts.
Next, I declared fields to describe my data (and by data I mean my posts). I can customize the field names, but it’s important to keep the id
field (this acts like a unique identifier). I can also add a boost to each field. A boost tells lunr that I want it to favor this field that much more in the context of searching. In my case, I wanted lunr to focus on the content of my posts, so I applied a boost to that field.
var index = lunr(function () {
this.field("title");
this.field("content", { boost: 10 });
this.field("category");
this.field("tags");
this.ref("id");
});
Now give lunr your data
Next, I gave lunr my data. This is the data that I want lunr to search and it corresponds with the fields that I defined above. And to keep my data straight, I increment a count
to build the id
as my unique identifier for each post.
{% assign count = 0 %}
{% for post in site.posts %}
index.add({
title: {{post.title | jsonify}},
content: {{post.content | strip_html | jsonify}},
category: {{post.category | jsonify}},
tags: {{post.tags | jsonify}},
id: {{count}}
});
{% assign count = count | plus: 1 %}
{% endfor %}
By adding my fields and data, lunr passes it through its pipeline, processes the data, and builds an object that I’ll query later.
Build a data reference for lunr
To complement the data that I gave to lunr, I created an object that has basic information about each post. This is so I can reference it against lunr’s search results because lunr returns the reference id
as a search result and not all the data I gave it.
var store = [{% for post in site.posts %}{
'title': {{post.title | jsonify}},
'link': {{ post.url | jsonify }},
'image': {{ post.image | jsonify }},
'date': {{ post.date | date: '%B %-d, %Y' | jsonify }},
'category': {{ post.category | jsonify }},
'excerpt': {{ post.content | strip_html | truncatewords: 20 | jsonify }}
}{% unless forloop.last %},{% endunless %}{% endfor %}
];
Query lunr and the match results
Finally, it’s time to query lunr aka query that index
object lunr created from my fields and data. Lunr returns id
numbers as search results (in order of most relevant) and then I use that id
as an index number for my store
object so I can output details about each search result.
$(document).ready(function () {
$("input#search").on("keyup", function () {
var resultdiv = $("#results");
// Get query
var query = $(this).val();
// Search for it
var result = index.search(query);
// Show results
resultdiv.empty();
for (var item in result) {
var ref = result[item].ref;
var searchitem =
'<div class="result"><a href="' +
store[ref].link +
'" class="post-title">' +
store[ref].title +
'</a> <div class="post-date small">' +
store[ref].category +
" × " +
store[ref].date +
"<div><p>" +
store[ref].excerpt +
"</p></div>";
resultdiv.append(searchitem);
}
});
});
Now search
And that’s how I brought lunr.js to Jekyll. You can add some fun stuff, like the number of search results or even play around with lunr’s options to really customize the search.
- Documentation: lunr.js
- Demo: https://katydecorah.com/search
- Code: lunr-feed.js
- Live file: lunr-feed.js