After years of having our company site powered by Wordpress, we got tired of it and migrated to the oh-my-god-it-has-markdown Middleman, hoping it will make us want to blog more often than three times a year.
It was a great move - no more spambots, no more Wordpress vulnerabilities exploited, no more server maintaining. The migration process was really fun, and we could migrate 10+ years of blogposts without major headaches.
But, of all the issues we created during the migration, the first one was still open - enable site-wide search. The great thing about Middleman is it generates static sites, and a search engine doesn't seem that static.
We considered using one of those Google Search plugins that never look good, so we decided we could do better. We found a couple of solutions that didn't seem to work that much or weren't that easy to integrate, so with Palla we did what any obsessive programmer fiddling with a new tool would: we built an extension to do that ourselves.
The extension
The extension uses lunrjs
, a full-text search engine that runs on the browser. It adds a new Resource
to Middleman's Sitemap
with the LunrJS
index, so it's available to the client's browsers to perform the search.
When it's time to render
the resource, the extension sets up a V8 context, loads LunrJS in there, and sends it each sitemap's resource that has to be indexed. The index will also have an extra field for mapping index resources with useful non-indexed data like the resource's URL, so the results give more information.
The user of the extension should specify which fields of each resource have to be indexed, and we take care of avoid rendering the resource's layouts: we don't want our company name to return each and every page of the site as a result.
The LunrJS source code is included as an asset, so the client just needs to //= require lunr.min
in the site's all.js
or similar and it'll get through Middleman's standard assets pipeline - so it'll be ready to be used on the browser.
Shut up and take my money!
If you want to use the extension - yay! - you should first add it to your project: add gem 'middleman-search'
to your project's Gemfile
and bundle install
it.
Then you should activate
it on your config.rb
- with it's configuration.
activate :search do
search.resources = ['blog/', 'index.html', 'contactus/index.html']
search.index_path = 'search/lunr-index.json'
search.fields = {
title: {boost: 100, store: true, required: true},
content: {boost: 50},
url: {index: false, store: true}
}
end
You should tell the extension which path's to index, where to generate the index, and how to treat each of the resource's fields.
After that, tell Sprockets to include lunr.min.js
by adding //= require lunr.min
to your all.js
or equivalent, and then code some search.js
that loads and queries the index. For example, you can load the generated index to a lunrData
global, create the Lunr index storing lunr.Index.load(lunrData.index)
in a lunrIndex
variable, and query the results using lunrIndex.search(search_term)
. You can then access lunrData.docs[your_result]
to get all the associated data you stored.
Here's our current code, using jquery-ui
to get an autocomplete on the search bar:
If you want to test it - you already probably are in the right place. Look for a search box on the top of any page in our site and give it a try:
Contributing
The extension WorksForUs™ and seems to be customisable-enough to get published - it's already listed in the Middleman Extensions Directory - but it still may not suit your needs 100%. So we hope we can improve it toghether - did I say it's open source?
Feel free to report any issue you have or send a pull request with any new feature you implement.
Happy searching!