サイト内検索を自由にカスタマイズ!Jekyllに全文検索を追加する「jekyll-lunr-js-search」

【告知】 10/14(土)新潟県上越市でIT勉強会を開催します!!

スポンサーリンク

今までサイト内検索にGoogleカスタム検索を使っていたのだが、
いかんせんSNSのウィジェットとかJavaScript SDKとかいろいろ読み込みが遅いのが気になっていたので、
UX向上とパフォーマンスチューニングの一環として全文検索をJekyllプラグインを使って実装することにした。

調査したところによると、静的サイトジェネレータであるJekyllでの全文検索は当然JavaScriptベースだ。
検索用のJSONファイルをLiquidで生成して、Ajaxで取り込んで処理するのが多い。外部APIを使わないならば必然とそうなる。

今回使うのはこのプラグイン。

プラグイン自体オールインワン的な感じで、導入はかなり楽だ。
クライアントはjQueryベースなのだが、検索結果のHTMLテンプレートではMustacheを採用しているため、テンプレートのカスタマイズも自由にできるのでこれに決めた。
URLにクエリ付きでのアクセスにも対応している。

機能

Jekyllの機能というよりは、JavaScriptによる全文検索だ。
内包されているJavaScriptライブラリは以下。

lunr.js

中でも中核を担うのが、テキスト検索エンジンのlunr.jsだ。
EventEmitterもあるし、PipelineとかTokenizerとか使えそう。
docs もしっかりしていて安心だ。アプリケーション構築にはかなり使える。
個人的に使ってみたい。

導入方法

jekyll-lunr-js-search の導入方法は2パターン。

  1. Gemパッケージをインストールする。
  2. 静的に個別ファイル(プラグイン本体と必要なJavaScriptファイル)をダウンロードしてサイトに配置する。

今回はBundlerでインストールした。
他のインストール方法についての詳細はGithubのリポジトリを見てほしい。

Gemをインストール。

gem install jekyll-lunr-js-search

# もしくはbundlerの場合、Gemfileに追記
gem 'jekyll-lunr-js-search'

_config.yml に使用するGemパッケージを追記。

gems: ['jekyll-lunr-js-search']

HTMLファイルで search.min.js を読み込む。(現時点では search.min.js はまだない。)

<script src="/js/search.min.js" type="text/javascript" charset="utf-8"></script>

検索フォームをHTMLに追加。

<div id="search">
  <form action="/search" method="get">
    <input type="text" id="search-query" name="q" placeholder="Search" autocomplete="off">
  </form>
</div>

検索結果を表示する要素をHTMLに追加。

<section id="search-results" style="display: none;">
  <p>Search results</p>
  <div class="entries">
  </div>
</section>

Mustacheで使うHTMLテンプレートをHTMLに追加。

{% raw %}
<script id="search-results-template" type="text/mustache">
  {{#entries}}
    <article>
      <h3>
        {{#date}}<small><time datetime="{{pubdate}}" pubdate>{{displaydate}}</time></small>{{/date}}
        <a href="{{url}}">{{title}}</a>
      </h3>
    </article>
  {{/entries}}
</script>
{% endraw %}

{% raw %} ~ {% endraw %} でラップされているのは、MustacheのタグがJekyll側で勝手に削除されるのを防ぐため。

jQueryプラグインの初期化。

<script type="text/javascript">
  $(function() {
    $('#search-query').lunrSearch({
      indexUrl: '/js/index.json',   // Url for the .json file containing search index data
      results : '#search-results',  // selector for containing search results element
      entries : '.entries',         // selector for search entries containing element (contained within results above)
      template: '#search-results-template'  // selector for Mustache.js template
    });
  });
</script>

_config.yml で検索対象から特定のファイルを除外設定可能。

lunr_search:
  excludes: [rss.xml, atom.xml]

他に検索オプションやJavaScriptディレクトリの指定ができる。

実行

$ jekyll build

ビルドするとJSONが生成される。
search.min.js などもこのタイミングで生成される。
search.min.js はjQueryとかその他のライブラリがconcatされたもの。

デモ

このページのヘッダーの検索窓から検索して欲しい。
とりあえず検索結果の表示は簡素なモーダル風にしてみた。

実は問題があった

Jekyll v3.0.1でエラー

Gemをインストールして、ビルドを実行するとコンソールに次の表示。

$ jekyll serve
Configuration file: /path/to/project/_config.yml
            Source: /path/to/project
       Destination: /path/to/project/_site
 Incremental build: disabled. Enable with --incremental
      Generating...
              Lunr: Creating search index...
jekyll 3.0.1 | Error:  nesting of 151 is too deep

Jekyll v3.0.1 で今配信されている v3.0.0 を使用しようとしたがエラーになってしまう。

解決策は有志がForkして修正してくれているファイルを置いてるリポジトリを指定する。

gem 'jekyll-lunr-js-search', :git => "https://github.com/InsidiousMind/jekyll-lunr-js-search.git"

このプラグイン自体が精力的に開発されてない感じなので、これで様子を見たい。

日本語にヒットしない

どうやらlunr.jsは英語しか対応していない。しかし、日本語分かち書きを行えば対応できる。
日本語にも対応した多言語化対応ライブラリもすでに存在した。

日本語対応に関しては改めて対応したいと思う。

プラグインは他にもある

他にもSimple-Jekyll-Searchなどがある。
名前の通りシンプルだから、必要最低限の機能があれば良いのであればこちらの方が使いやすいかもしれない。
今回のプラグインとは違い、JSONの項目を自分で定義することができる。もしLiquidがわかればJSONのカスタマイズが自由にできるから便利だ。


イソップへのお悩み相談募集中

イソップに相談しませんか?

当ブログで紹介しているような、Web制作やフリーランスへの悩みをイソップに相談してみませんか?
回答できることがあれば記事の中でご紹介します。