algoliaを使ってJekyll製ブログに全文検索をつけた話
algoliaとは
Products to accelerate search and discovery experiences across any device and platform.
algoliaとは全文検索エンジンサービスです。「algoliaサーバにブログやドキュメントなどのコンテンツデータをアップすると、そのデータを全文検索するAPIを利用できる」といった感じのものです。
例を挙げると、Vue, React, Webpack, babel などの公式ドキュメントにある検索機能が、algoliaのDocSearchという機能によって実現されています。
今回は、このalgoliaを使ってブログに全文検索を実装した方法をまとめてみます。
手順
- algoliaのフリーアカウントを取得する
- jekyll-algoliaを使い、ブログコンテンツをalgoliaサーバにアップする
- autocomplete.jsを使い検索窓を実装する
1. algoliaのフリーアカウントを取得する
サービスを利用するためにフリーアカウントを作成します。
ダッシュボード画面のサンプル
アカウントを作成するとコンテンツをアップロードでき、そのデータに対してAPI経由で全文検索できるようになります。algoliaが提供している機能は基本的にここまでで、検索窓や検索結果の表示などのUIは別途用意する必要があります。
ただ「ドキュメント」に全文検索を導入する場合は、検索窓などのWidgetがセットになったDocSearchという機能を使うことができます。
ちなみにこれを利用するには審査が必要です。私は当初「algolia = DocSearch」だと思い込んでいたのでDocSearchの登録申請を出しましたが、審査で「これはドキュメントじゃなくてブログよね?」とツッコミいただき、通常のalgoliaアカウントを作成するよう助言いただきました…。
2. jekyll-algoliaを使い、ブログコンテンツをalgoliaにアップする
algoliaにコンテンツをアップする方法はいろいろあります。その中のひとつがjekyll-algoliaです。
おさらいすると、Jekyllは「markdown等で書かれたコンテンツデータから静的サイトを生成する」ものです。対してjekyll-algoliaは「markdown等で書かれたコンテンツデータから静的サイトを生成し、生成したコンテンツをalgoliaにアップする」ものです。要はアップロードを手助けしてくれるプラグインですね。
このjekyll-algoliaを導入していきます。
Environment
- Jekyll >= 3.6.0
- Ruby >= 2.3.0
Installation
Gemfile
にプラグインを追加し、インストールします。
source 'https://rubygems.org'
gem 'jekyll', '~> 3.6'
group :jekyll_plugins do
gem 'jekyll-algolia'
end
$ bundle install
Configuration
algoliaアカウントを作成後、ダッシュボードの「API Keys」にてApplication IDを確認し、_config.yml
に設定を既述します。
algolia:
application_id: your_application_id
index_name: mf_code # indexingする対象の名前をここで決めます。
私はブログを生成するための設定とAlgoliaにアップするための設定を一部変えたかったので、_config.algolia.yml
と別ファイルにしました。
Usage
ダッシュボードの「API Keys」からAdmin API Keysを確認し、実行時に渡します。
$ ALGOLIA_API_KEY='your_admin_api_key' bundle exec jekyll algolia
読み込む設定ファイルを変更するには--config
オプションを使います。
$ ALGOLIA_API_KEY='your_admin_api_key' bundle exec jekyll algolia --config ./_config.algolia.yml
Advanced
毎回手動で実行するのは面倒なので、CIで実行するようにします。
ただalgoliaのフリーアカウントでは1ヶ月のアップロード回数に限度があるので、むやみにアップロードするのは避けたいところ。記事コンテンツが修正された時だけアップロードするようにしてみます。具体的には「前回のCI実行時のリビジョンと比較し_posts
以下に差分がある場合」にアップロードします。
そのために必要なことは以下のとおりです。
- CIの環境変数にAdmin API Keyを登録する
- jekyll-algoliaを実行するスクリプトを実装する
- CIの過去の実行結果を取得し、そのトリガーとなったCommit Idを取得する
- 現在のCI実行のトリガーとなったCommit Idを取得する
- それらのCommit Idのdiffを取得し、
_posts
以下の差分の有無を確認する _posts
以下に差分がある場合はjekyll-algoliaを実行する
スクリプトはindexing.sh - aloerina01.github.ioに実装していますので、よければ参考にどうぞ(shell力の低い実装ですが…)。
3. autocomplete.jsを使い検索窓を実装する
algoliaを使ってインクリメンタルサーチを実現できるライブラリがautocomplete.jsです。これはJekyllとは関係ないもので様々な環境に適用することができます。公式ドキュメントも手厚く、バックエンドはphp, ruby, JavaScript, python, C# に対応しており、フロントエンドはvanillaなJavaScript, Angular, jQuery 用のサンプルコードが用意されています。
このブログではvanillaJSでの利用方法にのっとりJSとCSSを読み込み、任意の箇所にDOMを追加しています。
HTML
<div class="aa-input-container" id="aa-input-container">
<input type="search" id="aa-search-input" class="aa-input-search" placeholder="Search for players or teams..." name="search" autocomplete="off" />
<svg class="aa-input-icon" viewBox="654 -372 1664 1664">
<path d="M1806,332c0-123.3-43.8-228.8-131.5-316.5C1586.8-72.2,1481.3-116,1358-116s-228.8,43.8-316.5,131.5 C953.8,103.2,910,208.7,910,332s43.8,228.8,131.5,316.5C1129.2,736.2,1234.7,780,1358,780s228.8-43.8,316.5-131.5 C1762.2,560.8,1806,455.3,1806,332z M2318,1164c0,34.7-12.7,64.7-38,90s-55.3,38-90,38c-36,0-66-12.7-90-38l-343-342 c-119.3,82.7-252.3,124-399,124c-95.3,0-186.5-18.5-273.5-55.5s-162-87-225-150s-113-138-150-225S654,427.3,654,332 s18.5-186.5,55.5-273.5s87-162,150-225s138-113,225-150S1262.7-372,1358-372s186.5,18.5,273.5,55.5s162,87,225,150s113,138,150,225 S2062,236.7,2062,332c0,146.7-41.3,279.7-124,399l343,343C2305.7,1098.7,2318,1128.7,2318,1164z" />
</svg>
</div>
<!-- 中略 -->
<script src="https://cdn.jsdelivr.net/algoliasearch/3/algoliasearch.min.js"></script>
<script src="https://cdn.jsdelivr.net/autocomplete.js/0/autocomplete.min.js"></script>
JavaScript
const client = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');
const index = client.initIndex('YourIndex');
//initialize autocomplete on search input (ID selector must match)
autocomplete(
'#aa-search-input',
{ hint: false },
{
source: autocomplete.sources.hits(index, {hitsPerPage: 5}),
//value to be displayed in input control after user's suggestion selection
displayKey: 'name',
//hash of templates used when rendering dataset
templates: {
//'suggestion' templating function used to render a single suggestion
suggestion: (suggestion) => return `<span>${suggestion._highlightResult.title.value}</span>`;
}
}
);
suggestion._highlightResult
に検索結果が入ってきます。プロパティのtitle
は自分がアップロードしたコンテンツが持つプロパティで、algoliaダッシュボードで確認できます。何をヒットさせるか、どんな優先度でヒットさせるか等をダッシュボードから変更することができます。
CSS
.aa-input-container {
display: inline-block;
position: relative;
}
.aa-input-search {
width: 300px;
border: 1px solid rgba(228, 228, 228, 0.6);
padding: 12px 28px 12px 12px;
box-sizing: border-box;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
.aa-input-search::-webkit-search-decoration, .aa-input-search::-webkit-search-cancel-button, .aa-input-search::-webkit-search-results-button, .aa-input-search::-webkit-search-results-decoration {
display: none;
}
.aa-input-icon {
height: 16px;
width: 16px;
position: absolute;
top: 50%;
right: 16px;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
fill: #e4e4e4;
}
.aa-dropdown-menu {
background-color: #fff;
border: 1px solid rgba(228, 228, 228, 0.6);
min-width: 300px;
margin-top: 10px;
box-sizing: border-box;
}
.aa-suggestion {
padding: 12px;
cursor: pointer;
}
.aa-suggestion + .aa-suggestion {
border-top: 1px solid rgba(228, 228, 228, 0.6);
}
.aa-suggestion:hover, .aa-suggestion.aa-cursor {
background-color: rgba(241, 241, 241, 0.35);
}
インクリメンタルサーチWidgetのシンプルなCSSです。実際にはこのCSSをカスタマイズして利用しています。
感想
algoliaはドキュメントがけっこうしっかり書かれている印象でした。機能が豊富なこととドキュメントがしっかりしてることで、かえって自分に最適な情報を探すのに苦労しましたが、見つけてしまえばアップロードまではすぐできました。
また、DocSearchの印象が強く「インクリメンタルな検索Widgetまでセットになったもの」を求めていたので、autocomplete.jsを見つけるまではいろいろな情報に振り回されました。jekyll-algoliaの公式ドキュメントではUIの実装にInstantSearch.jsを使うよう案内していて、情報迷路にハマりました…(ちなみにInstantSearch.jsは、簡単な検索窓だけでなく、検索結果によって画面全体が更新されるなどのリッチなコンテンツを実装するためのライブラリのようでした)。
とは言え、algoliaもautocomplete.jsも理解してしまえば不自由なく使える便利なものでした。日本語の情報が少なかったので、この記事が少しでも役に立てれば幸いです。