WordPress to Jekyll part 3 - Site search

Site search is a feature that WordPress got right. Analytics also tells me it is popular. A static site is at a disadvantage, but we have some options to address that.

Considering options

My first consideration was to use Google Site Search alas it was deprecated last year. There are alternative options, but few are free. I agree that people should be paid for their services, something has to keep the lights on, but a small personal blog with no income stream can’t justify this cost.

My next thought was to generate reverse-index JSON files during building. Client-side JavaScript would utilize them as the user types in the search box. It’s an idea I might come back to, but the migration had already taken longer than I anticipated, and I like to ship fast and often :)

Algolia

I soon came across Algolia which, provides a simple API and free tier. Crucially they also supply a Jekyll plug-in to generate the necessary search indexes! Awesome.

Set-up was a breeze! Algolia have a specific, useful guide to indexing with Jekyll. Once you sign-up, you’ll need to configure indexing and integrate it with your site.

Index integration

Firstly, install the jekyll-algolia gem making sure to specify it in your gemfile.

Then configure your Jekyll _config.yml so it knows what to index and where as well as what document attributes are significant:

algolia:
  application_id: {your-algolia-app-id}
  index_name: {your-algolia-index-name}
  settings:
    searchableAttributes:
      - title
      - excerpt_text
      - headings
      - content
      - categories
      - tags
    attributesForFaceting:
      - type
      - searchable(categories)
      - searchable(tags)
      - searchable(title)

Finally, you’ll need to run the indexing. You need to ensure the environment variable ALGOLIA_API_KEY is set to your private Admin API Key from your Algolia API Keys page, then run the following command after your static content is generated:

bundle exec jekyll algolia

Site integration

Wiring up the search box can be a little overwhelming as they have so many clients, options and APIs available. I went with a design that presents the results as you type like this:

This design uses two of their libraries - the search lite and the search helper plus some code to wire it up to my search box and render the results in a drop-down list. I’ll probably further tweak the result format and maybe consider wiring up to the API directly as two libraries for such a simple use case seems a bit overkill.

<script src="https://cdn.jsdelivr.net/npm/algoliasearch@3/dist/algoliasearchLite.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/algoliasearch-helper@2.26.0/dist/algoliasearch.helper.min.js"></script>
<script>
  let searchForm = document.getElementById('search-form')
  let hits = document.getElementById('hits')
  let algolia = algoliasearch('{your-algolia-app-id}', '{your-algolia-search-token}')
  let helper = algoliasearchHelper(algolia, '{your-algolia-index-name}',
    { hitsPerPage: 10, maxValuesPerFacet: 1, getRankingInfo: false })
  helper.on('result', searchCallback)

  function runSearch() {
    let term = document.getElementById('s').value
    if (term.length > 0)
      helper.setQuery(term).search()
    else
      searchForm.classList.remove('open')
  }

  function searchCallback(results) {
    if (results.hits.length === 0) {
      hits.innerHTML = '<li><a>No results!</a></li>'
    } else {
      renderHits(results)
      searchForm.classList.add('open')
    }
    let credits = document.createElement('li');
    credits.innerHTML = "<img src=\"https://www.algolia.com/static_assets/images/press/downloads/search-by-algolia.svg\" onclick=\"window.open('https://www.algolia.com', '_blank')\" />"
    hits.appendChild(credits)
  }

  function renderHits(results) {
    hits.innerHTML = ''
    for (let i = 0; i < results.hits.length; i++) {
      let li = document.createElement('li')
      let title = document.createElement('a')
      title.innerHTML = results.hits[i]._highlightResult.title.value
      title.href = results.hits[i].url
      li.appendChild(title)
      hits.appendChild(li)
    }
  }
</script>

Analytics

I’m a big proponent of analytics when used purely for engineering improvement, and Algolia provides a useful dashboard to let you know how performance is doing, what topics are being searched for and what searches might not be returning useful content.

I’ll dig through that when I have a little more time.

[)amien Note: I do not receive any compensation from Algolia either directly or via any referral program. I’m just a happy user.

1 responses

  1. Avatar for Karthik Chintala

    I’ve been searching to have a search bar on top of my blog. Will try to integrate in my blog.

    Thank you Damien.

    Karthik Chintala September 18th, 2018