前回の記事ではHugoのブログに全文検索機能をつけるために、検索インデックスとしてのJSONの出力方法や、Netlifyでのビルド時に自動的にインデックスが更新されるようなWebTaskの利用について書きました。
https://blog.piyo.tech/posts/2018-04-07-hugo-search-index/今回はインデックスを利用してページ上に検索ボックスや検索結果を表示するページを用意する手順について書いていきます。
全体の流れ
Algoliaの公式ドキュメントに沿ってinstantSearch.jsを使う形で進めていきます。概ね簡単なのですが、オプションが多くて迷う点がありました。僕はこうしたよ的な観点で書いていきます。
https://community.algolia.com/instantsearch.js/v2/getting-started.html流れとしては、
- アセットの読み込み
 - 検索ページを作成
 - JSで検索の設定を記述
 - 検索結果の調整
 
という感じです。
アセットの読み込み
npmで導入する方法とCDNを指定する方法の2種類提供されています。今回は静的サイトのためCDNを利用しました。
現在利用している、HugoのRobustというテンプレートではページ全体をrobust/layouts/_default/baseof.htmlに記述しています。これをオーバーライドするために、プロジェクトルートのlayouts/_default以下にbaseof.htmlを作成し元ファイルの中身をコピーした上で、CDNの記述を追加します。
<html>
  <head>
    ...
    <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/instantsearch.js@2.6.3/dist/instantsearch.min.css">
    <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/instantsearch.js@2.6.3/dist/instantsearch-theme-algolia.min.css">
    ...
  </head>
  <body>
    <script src="https://cdn.jsdelivr.net/npm/instantsearch.js@2.6.3"></script>
    ...
  </body>
</html>
検索ページを作成
検索をどこで実施できるようにするかというのはちゃんと考えるべきことなんですが、今回は簡単に実施するために検索用のページを用意することにしました。
通常の投稿はposts以下に配置しています。検索ページ通常のブログ投稿とは異なりますのでpages以下にsearchとして作りました。
ページのMarkdownに直接HTMLやJavascriptを書く形で検索ページを用意しました。
---
title: xxx
date: xxx
tags: xxx
---
<div id="search-box">
    <!-- 検索ボックス用の空DOM -->
</div>
<ul id="hits">
    <!-- 検索結果用のDOM、各結果をliで置くためulにしている -->
</ul>
<div id="pagination">
    <!-- ページネーションがここに -->
</div>
<script>
  // ここに検索用のJSを記述
</script>
実際に完成した画面との対応がこんな感じです。
JSで検索の設定を記述
先ほどのページ内、<script>タグの中にinstantSearch.jsの設定を書いていきます。
- 検索ボックス
 - 検索結果
 - ページネーション
 
の3要素のみのシンプルな形にしました。
前述の公式ドキュメントに色々なパターンが載っていたり、リファレンスを見るとオプションがかなり豊富だったりとたくさんの設定がありそうです。
ここで指定するapiKeyはAlgoliaダッシュボードのsearch-only API Keyを使います。
<script>
// instantSearchを初期化
var search = instantsearch({
  appId: '3BFGDTL653',
  apiKey: '3f41e36d23c8180ba83e5157b43eb3cd',
  indexName: 'BlogPosts',
  urlSync: true
});
// 検索ボックスをDOMに設定
search.addWidget(
  instantsearch.widgets.searchBox({
	  container: '#search-box',
	  placeholder: '記事を検索',
	  poweredBy: true
  })
);
// 検索結果をDOMに設定
// 結果には<li>を使うように
search.addWidget(
  instantsearch.widgets.hits({
	  container: '#hits',
	  templates: {
      empty: '見つかりませんでした。',
	    item: '<li><code>{{ dateString }}</code> <a href="{{permalink}}">{{ title }}</a></li>'
	  }
  })
);
// 検索結果をページネーションするための設定
search.addWidget(
  instantsearch.widgets.pagination({
	  container: '#pagination',
	  maxPages: 20,
	  scrollTo: false
  })
);
search.start();
</script>
ここまででサイト上で検索を実施できるようになりました。すごく簡単でした。
検索結果の調整
最後は検索結果の調整です。
先ほどまでの状態だと、結果の順序がバラバラだったり関係なさそうなものまでヒットしたり、なんか欲しい結果と違うなあという感じになってしまっていました。
このあたりのロジックをAlgoliaのダッシュボードから変更できるので、ある程度納得できる状態になるまで調整を試みました。
対象のアプリのダッシュボードへ入り、左メニューのIndices、RANKINGタブ、と進みます。
基本設定
基本設定として、どの項目を検索対象とするのか、優先度はどの順番なのかという設定をします。インデックスをアップロード・登録した時点では特に何も入っていないので、この項目は100%設定すべきです。
本ブログの場合は、
- title
 - tags
 - content
 
を対象とし、この順に優先度が高い、としました。
ランキング
続いて検索順位を設定します。特定のフィールドでの並び替えもここで設定します。僕の場合はひとまず新しい順に並べたいと思い、投稿日が新しいものが順位が高くなるようにしました。
並び替えの場合、「ADD A SORT-BY ATTRIBUTE」から項目を追加します。投稿日で並び替えたいので、dateをdescで並び替えさせるようにしました。
詳細設定
最後にタイポの許容値などを少し厳し目に設定しました。検索結果がいまいち、というのはここの微調整で少し良くなったと感じます。
僕が設定したのはTypo Torelanceの項目で、それ以外は既定のままにしました(項目が多くてよくわからん)。
要は、タイポをどれだけ許容するかという話で、基本はfalseにして無効としました。最初は、1タイポを許す文字数や2タイポを許す文字数を多めに設定してなどとやってましたが、まあタイポない前提でいいだろうということでfalseとしたわけです。
おわり
というわけで完成!