feed

2013年12月08日, 編集履歴

Jekyll製ウェブサイトに簡易検索機能を実装する

はじめに

 Jekyllは静的ウェブサイト生成システムであり、検索機能は付いていない。
 プラグインで検索機能を付けられるのかもしれないが、GitHub Pages上でビルド・ホストをする場合は使えないので、ちゃんと探していない。かんたんにサイト内検索を実現するならGoogleのカスタム検索を使用する方法もあるが、Googleには頼りたくないので今回は自分で実装した。

 こんなの。

 キィワードを入力してエンターで検索実行。キィワードを[](角括弧)でくくるとそれをブログ記事に付けられたタグとして認識する。たとえば[jekyll] 検索とすると、「jekyll」タグを付けられたもの、かつ本文に「検索」を含むブログ記事がヒットする。

コンセプト

 基本コンセプトは「simple jekyll searching - alex pearce」を参考にした。
 かいつまんで言えば、ブログ記事の情報(タイトル、URL、タグ、日付、本文)をJSONファイルに配列として詰め込み、JavaScriptで条件に合致する記事を取得するという方法をとった。

JSONファイルの作成

 ブログ記事の情報を溜め込むsearch.jsonを作成する。ウェブサイトのビルド時に全ブログ記事の情報を自動で埋め込むために、Jekyllの処理対象として作成する。

---
---
[
  {% for post in site.posts %}
  {
    "title": "{{ post.title | escape }}",
    "tags": [{% for tag in post.tags%}"{{ tag }}"{% unless forloop.last %}, {% endunless %}{% endfor %}],
    "url": "{{ post.url }}",
    "date": {"year": "{{ post.date | date: "%Y" }}", "month": "{{ post.date | date: "%m" }}", "day": "{{ post.date | date: "%d" }}"},
    "content": "{{ post.content | strip_html | strip_newlines | escape }}"
  }{% unless forloop.last %},{% endunless %}
  {% endfor %}
]

 全体を配列として、その中にブログ記事の情報を詰め込む。Jekyllの処理対象とするためにファイル先頭にYAML Front-matterが必要。

 ポイントはcontentの値に適用するフィルタ。
 post.contentにはブログ記事の本文がHTMLタグ付きで入っているので、strip_htmlフィルタを適用してHTMLタグを取り除く。strip_newlinesで改行を除去して単一行にし、最後にescapeでクォーテイション文字等をエスケープする。

 JSONファイルが作成できたら、一度ローカルでビルド、生成されたJSONファイルを検査し(JSONLint - The JSON Validator)、Validなファイルができているかを確認した方が良い。

検索フォーム、検索結果ページの作成

 検索ページへGETリクエストを送信するフォームを作成し、すべてのページで読み込まれるテンプレート内に配置する。

<form action="/search.html" method="get">
  <input type="search" name="q" placeholder="Search"/>
</form>

 検索結果ページsearch.htmlを作成し、空のdiv要素をひとつ置いておく。

<div id="matchedList">
</div>

JavaScriptによる検索

 search.jsを作成し、前節のsearch.htmlに読み込ませる。

 search.jsの要点を書くと、

  1. search.htmlGETリクエストが送信されているので、URL文字列から検索キィワードを取り出し、整形する
  2. search.jsonを読み込む
  3. search.jsonから読み込んだブログ記事情報の配列から検索条件に合致するものを抜き出し、search.htmlに配置しているdivに情報を整形して埋め込む

 本文にキィワードが含まれているかどうかはString.match()の正規表現を用いた。たとえばキィワードがjekyll 検索であれば(?=.*jekyll)(?=.*検索)という正規表現を本文文字列に対して用いた。この場合は、本文文字列内に「jekyll」「検索」の両方(順不同)が含まれている場合にマッチする。

まとめ

 実際のファイル構成はGitHubリポジトリィを参照。

 この方式の問題点はブログ記事数が増えるごとに生成されるJSONファイルが肥大化し、検索速度が低下することだが、それはそのときになって考えれば良いとして、今回はこれまで。