{
    "componentChunkName": "component---src-templates-blog-post-jsx",
    "path": "/post/implement-site-search-with-algolia/",
    "result": {"data":{"site":{"siteMetadata":{"title":"WEB EGG","author":"Leko - CTO at Yuimedi"}},"markdownRemark":{"id":"50ea8ab7-46af-5556-a365-7e28f6252061","excerpt":"2019/05/06追記 この記事は古いです 当サイトはMiddlemanからGatsbyに移行したため、この記事の内容は古くなっております。 動作デモはありませんが、Middleman製サイトに検索機能を実装する場合にはこのまま続きをお読み下さい。 現サイトで運用されているGatsby…","html":"<blockquote>\n<p>2019/05/06追記</p>\n<p><strong>この記事は古いです</strong><br>\n当サイトはMiddlemanからGatsbyに移行したため、この記事の内容は古くなっております。<br>\n動作デモはありませんが、<strong>Middleman製サイトに</strong>検索機能を実装する場合にはこのまま続きをお読み下さい。<br>\n現サイトで運用されているGatsby製サイトに検索機能を実装する場合はこちらの記事を参照して下さい。</p>\n<p>— <a href=\"/post/on-site-search-in-gatsby-with-algolia/\">Gatsby製サイトにAlgoliaのサイト内検索を実装する | WEB EGG</a><br>\n— <a href=\"/post/impression-comparison-of-middleman-and-gatsby/\">ブログをMiddlemanからGatsbyに乗り換えた雑感 | WEB EGG</a></p>\n</blockquote>\n<p>こんにちは。<br>\n当ブログのサイト内検索をしたことある方はお気づきかもしれませんが、サイト内検索に<a href=\"https://www.algolia.com/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Algolia</a>を利用しています。<br>\n（後述の事情により月初に使えなくなることがたまにありますが）動作速度もかなり早く、安定して稼働してます。</p>\n<p>運用コストもゼロで、記事書いてPRをマージすれば記事公開日に勝手にコンテンツが検索対象に一手間加えてあります。<br>\nAlgoliaを１ヶ月実運用してみたので、<strong>Algoliaはいいぞ</strong>という紹介記事を書きたいと思います。</p>\n<p>また、CQRS・DDDの勉強のために、フロントはReactと<a href=\"https://github.com/almin/almin\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Almin</a>で実装してみました。<br>\nAlmin、CQRS、Fluxの小さな実装例の１つとしても参考になるかと思います。</p>\n<!--more-->\n<h2 id=\"algoliaとは\" style=\"position:relative;\"><a href=\"#algolia%E3%81%A8%E3%81%AF\" aria-label=\"algoliaとは permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Algoliaとは</h2>\n<p><a href=\"https://www.algolia.com/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://www.algolia.com/</a></p>\n<p>Algoliaは、検索機能を提供するAPIと管理用APIと公式ライブラリ、そして周辺ツール群にて構成されています。<br>\n私なりの「Algoliaのここがすごい！」は、</p>\n<ul>\n<li>検索がとにかく早い</li>\n<li>プランが柔軟</li>\n<li>カスタマイズ性が高い</li>\n<li>１ヶ月利用して１回も落ちてない</li>\n<li>検索APIの気が利いてる</li>\n<li>自動でタイポの名寄せをしてくれる（NoedをNodeとして解釈可能）</li>\n<li>バックエンド、フロントエンド共にライブラリが充実</li>\n<li>フィールドの重み付けなどのパラメータ調整をコード変えずに対応可能</li>\n</ul>\n<p>あたりです。<br>\n大きな導入事例もあり、Alt GoogleSiteSearchのデファクトなのでは!? と思うほどのクオリティと感じています。</p>\n<h2 id=\"docsearchとは\" style=\"position:relative;\"><a href=\"#docsearch%E3%81%A8%E3%81%AF\" aria-label=\"docsearchとは permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>DocSearchとは</h2>\n<p>今回はAlgoliaのすごさを全力アピールするため、周辺ツールも紹介します。<br>\nAlgoliaが提供する周辺ツールのうち、ぜひ知って欲しいのが<a href=\"https://community.algolia.com/docsearch/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">DocSearch</a>です。</p>\n<p>DocSearchはAlgoliaのコミュニティが提供する、ドキュメントに特化した検索機能用のツールです。<br>\nAlgoliaのバックエンドを利用する点は同じですが、<strong>タグを数行書けばサイト内をクローリングして検索対象を自動収集</strong>してくれます。<br>\n今は亡きGoogle Site Searchのような使い勝手で、使い心地はこちらの方が高いと感じました。</p>\n<blockquote>\n<p>グーグルは、サイト内検索サービスのGoogle Site Searchを2018年に終了することを決定しました。</p>\n<p>— <a href=\"http://web-tan.forum.impressrd.jp/e/2017/03/28/25352\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Google Site Searchが終了へ、サイト内検索は2018年までに他のサービスに乗り換えを | 編集長ブログ―安田英久 | Web担当者Forum</a></p>\n</blockquote>\n<p>導入例はかなり強力で、その界隈の開発者にとっては馴染み深いドキュメントで広く活躍してます。\nもしかしたらこれらのドキュメントを読んでる過程で、サイト内検索を利用し、Algoliaのロゴに出会ったりしていないでしょうか。</p>\n<ul>\n<li><a href=\"https://facebook.github.io/react-native/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">React Native</a></li>\n<li><a href=\"https://babeljs.io/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Babel</a></li>\n<li><a href=\"https://middlemanapp.com/basics/install/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Middleman</a></li>\n<li><a href=\"http://vuejs.org/v2/guide/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Vue.js</a></li>\n<li><a href=\"http://docs.scala-lang.org/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Scala</a></li>\n</ul>\n<p>なお、今回は自前で実装してAlmin、DDD、CQRSについて実践したかったので、DocSearchではなくAlgoliaを利用しています。</p>\n<h2 id=\"algoliaの導入例\" style=\"position:relative;\"><a href=\"#algolia%E3%81%AE%E5%B0%8E%E5%85%A5%E4%BE%8B\" aria-label=\"algoliaの導入例 permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Algoliaの導入例</h2>\n<p>周辺ツールの１つであるDocSearchに話が逸れましたが、Algolia本家の話に戻します。</p>\n<p>先述の通りAlgoliaは一部開発者にとっては馴染み深いドキュメントで利用されていますが、<br>\n他にも有名なWebサービスでも利用されているようです。</p>\n<ul>\n<li><a href=\"https://www.twitch.tv/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Twitch</a></li>\n<li><a href=\"https://www.pscp.tv/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Periscope</a></li>\n<li><a href=\"https://stripe.com/jp\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Stripe</a></li>\n</ul>\n<p>DocSearchのように、だいたいサイト内検索系のサービスの対象は静的サイトだと思いますが、<br>\nAlgoliaが恐ろしいのは<strong>動的なWebサービスでも利用されている</strong>という点です。<br>\nそれもAmazon(Twitch)やTwitter(Periscope)などすでに巨大な検索エンジンを持ってそうな企業が採用している点は脅威です。</p>\n<p>自分で使ってみても、それくらい検索性・速度の面で圧倒的に良いと思いますし、<br>\n何より、複雑になりがちな検索機能のクエリ処理を実装しなくていいというのはかなりのアドバンテージなのではないでしょうか。<br>\n試してはないですが、AlgoliaはバックエンドのAPIがかなり整っているので、動的サイトでも大きな遅延なく検索機能を代替できそうなイメージがあります。</p>\n<h2 id=\"algoliaの料金制限\" style=\"position:relative;\"><a href=\"#algolia%E3%81%AE%E6%96%99%E9%87%91%E5%88%B6%E9%99%90\" aria-label=\"algoliaの料金制限 permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Algoliaの料金・制限</h2>\n<p><a href=\"https://www.algolia.com/pricing\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Algoliaの料金ページ</a></p>\n<p>Algoliaは無料で始められます。<br>\nまた、小規模なサイトであれば無料のまま使い続けられます。</p>\n<ul>\n<li>検索対象になるマスタデータ1万件まで\n<ul>\n<li>当ブログに置き換えると1万記事までOK</li>\n</ul>\n</li>\n<li>Algolia APIを月10万回まで実行可能\n<ul>\n<li>記事の登録も1回、記事の検索も1回</li>\n</ul>\n</li>\n<li>Algoliaを用いている箇所にAlgoliaのロゴを表示すること</li>\n</ul>\n<p>で用途として足りていれば、無料のまま継続利用できます。<br>\n月10万回というのがとても絶妙な数でして、月1万PVほどの当ブログで1ヶ月試した結果、ギリギリアウトでした（残３日にて回数上限オーバー）。<br>\nなお、<strong>上限オーバーすると、検索もコンテンツの追加もできなくなります。</strong></p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 668px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/cdacbacce80c16b17268858791504032/136a2/algolia-search-limit-exceeded.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 45.50898203592815%; position: relative; bottom: 0; left: 0; background-image: url('data:image/svg+xml,%3csvg%20xmlns=\\'http://www.w3.org/2000/svg\\'%20width=\\'400\\'%20height=\\'181\\'%20viewBox=\\'0%200%20400%20181\\'%20preserveAspectRatio=\\'none\\'%3e%3cpath%20d=\\'M5%2025l3%2010c2%208%203%209%205%209%203%200%203%200%205-6l2-6%202%206c1%205%202%206%204%206s3-1%206-9c4-12%204-11%200-11-2%200-3%201-4%206l-2%206-2-6c-1-5-2-6-4-6-3%200-3%200-5%207l-2%205c-3-12-3-12-6-12l-2%201m31%200v10l1%209h7c7%200%208-1%208-3s0-2-5-2l-5-1c0-2%201-2%205-2%203%200%204%200%204-2s-1-2-4-2c-4%200-5%200-5-2s1-2%205-2%205%200%205-2-1-2-8-2l-8%201m19%209v10h7c8-1%2013-5%209-9l-1-4c2-5-1-7-9-7h-6v10m25%200l1%2010h7c7%200%208-1%208-3s0-2-5-2c-7%200-8-2-1-3s7-4%200-4c-7-1-6-3%201-4%204%200%205%200%205-2%200-1-2-2-8-2h-8v10m21-7c-7%206-3%2016%206%2017%206%200%209-2%209-7%200-4-4-6-5-1%200%206-8%203-8-2%200-4%204-6%207-5%205%202%208-2%203-4-4-2-9-1-12%202m20-1c-3%203-3%204-3%208%200%207%206%2011%2014%209%204-1%204-1%204-6%200-4%200-4-2-4s-3%201-3%203c0%205-8%203-8-3%200-3%204-5%208-4h4c4-5-9-7-14-3M17%2068c-2%205%201%2010%206%209h4l2%201-1-2-1-4c2-5-6-8-10-4m50%204c0%204%200%206%201%205l2-2v1c0%202%200%202%204%201h3v-3l1-4v-3l-6-1h-5v6m14-5v5c-2%200-2%205%200%205%209%201%2010%200%2010-1v-6c0-4%200-4-3-4-1%200-2%200-2%202l-1%202c-2%200-2%200-1-1l1-1c0-1-3-2-4-1m97%2044l1%208%201-2v-2l2%202c1%201%202%202%204%200l1-1-1-3c0-2%200-2%201-1h1l1-1v2c-2%202%202%207%203%205v-2c-2-1-3-3%200-3l1-1-1-4-2-4v3c0%202%200%203-2%203s-3-2-1-2v-1l-1-1-1-2h-4l-3-1v8m19-4l-1%204c-1-1-1%200-1%201l-1%205h1c2-2%202-2%203%200l7%201%204-1v-13l-2%202-1%202-1-2c1-1%200-2-1-2s-2%201-1%202c0%202%200%202-2%202-1%200-2%200-1-1h1l1-1-1-1-2-1-1-1-1%204M4%20178v3h9c10%200%2012-1%2012-3l2-2c2-2%202-2-10-2H4v4m39-3v2l-1%201-1%201c-1%201%200%201%207%201a170%20170%200%200115%200%20179%20179%200%200023%200h7a444%20444%200%200132-1c-5%200-5%200-5-2s1-2%208-2a708%20708%200%2000-85%200m98%202c1%204%204%206%204%202%200-3%200-3%206-3s8%201%209%204c1%201%202%201%203-1%205-5%2015-5%2014%200l2%202%201-2c0-3%200-3%203-3%202%200%203%201%204%203l3%202%204-2c1-2%202-3%204-3%203%200%203%200%203%203l1%202%201-1%203-1%203-1-3-1-2-1c0-2-4-2-32-2h-31v3m132-1c0%202-1%202-5%203-7%200-6%201%204%202h7v-2c-1-3-1-3%202-3%202%200%203%200%203%202%201%203%204%204%205%201%201-4%206-4%207%200l2%202%203-2c0-2%201-3%203-3s2%200%202%203c-1%202-1%202%202%202l3-2c1-2%202-3%204-3s3%201%204%203c0%201%201%202%203%202%203%200%203%200%202-2-1-3-1-3%202-3%202%200%203%201%203%203l4%202%203-2c0-2%201-3%203-3%203%200%203%200%202%203-1%201-1%202%201%202l1-2c0-3%200-3%208-3%206%200%207%200%207%202%200%201-2%202-6%202l20%201h24v-7h-61c-61%200-62%200-62%202\\'%20fill=\\'%23d3d3d3\\'%20fill-rule=\\'evenodd\\'/%3e%3c/svg%3e'); background-size: cover; display: block;\"\n  ></span>\n  <picture>\n          <source\n              srcset=\"/static/cdacbacce80c16b17268858791504032/5251b/algolia-search-limit-exceeded.webp 167w,\n/static/cdacbacce80c16b17268858791504032/7390e/algolia-search-limit-exceeded.webp 334w,\n/static/cdacbacce80c16b17268858791504032/7c056/algolia-search-limit-exceeded.webp 668w,\n/static/cdacbacce80c16b17268858791504032/8af20/algolia-search-limit-exceeded.webp 884w\"\n              sizes=\"(max-width: 668px) 100vw, 668px\"\n              type=\"image/webp\"\n            />\n          <source\n            srcset=\"/static/cdacbacce80c16b17268858791504032/21521/algolia-search-limit-exceeded.png 167w,\n/static/cdacbacce80c16b17268858791504032/86d36/algolia-search-limit-exceeded.png 334w,\n/static/cdacbacce80c16b17268858791504032/74866/algolia-search-limit-exceeded.png 668w,\n/static/cdacbacce80c16b17268858791504032/136a2/algolia-search-limit-exceeded.png 884w\"\n            sizes=\"(max-width: 668px) 100vw, 668px\"\n            type=\"image/png\"\n          />\n          <img\n            class=\"gatsby-resp-image-image\"\n            src=\"/static/cdacbacce80c16b17268858791504032/74866/algolia-search-limit-exceeded.png\"\n            alt=\"上限超えた時の図\"\n            title=\"上限超えた時の図\"\n            loading=\"lazy\"\n            decoding=\"async\"\n            style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n          />\n        </picture>\n  </a>\n    </span></p>\n<p>このブログにおいて検索はさほどクリティカルな機能ではないし、無料で提供しているので、まぁ数日くらい検索できなくてもいいやくらいの温度感でやってます。<br>\nもしプラン上げたくなったら、管理画面から手続きすればプランの分だけ上限は復活するので、最悪そうします。</p>\n<p>概要、メリット、費用面について整理できたので早速Algoliaで検索機能の実装を始めます</p>\n<h2 id=\"記事をjson化してalgoliaに登録する\" style=\"position:relative;\"><a href=\"#%E8%A8%98%E4%BA%8B%E3%82%92json%E5%8C%96%E3%81%97%E3%81%A6algolia%E3%81%AB%E7%99%BB%E9%8C%B2%E3%81%99%E3%82%8B\" aria-label=\"記事をjson化してalgoliaに登録する permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>記事をJSON化してAlgoliaに登録する</h2>\n<p>当ブログはMiddleman(Ruby)を使用しているので、<a href=\"https://github.com/algolia/algoliasearch-client-ruby\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">algolia/algoliasearch-client-ruby</a>を使用します。<br>\nおそらく大体の言語の公式クライアントライブラリがあるので、言語に合わせて読み替えてください。</p>\n<p>使用するメソッドは<code>Algolia::Index#save_objects!</code>です。ソースは<a href=\"https://github.com/algolia/algoliasearch-client-ruby/blob/5062d7b6fff7d58694731c7c294d82677620a07b/lib/algolia/index.rb#L295\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">こちら</a><br>\n記事を検索できるように、記事データを登録するために、記事データをJSON化する必要があります。</p>\n<p>Middlemanの場合、幸い.jsonのついたファイルを作っておくだけで勝手にJSONを吐いてくれるので、とても楽でした。<br>\n<strong>なんて美味い話はなくて</strong>、多少は楽だったんですが、多少の工夫が必要でした。<br>\n記事データをJSON化しているのは<a href=\"https://github.com/Leko/WEB-EGG/blob/master/source/posts.json.erb\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">このファイル</a>で、以下の通りです。</p>\n<div class=\"gatsby-highlight\" data-language=\"erb\"><pre class=\"language-erb\"><code class=\"language-erb\"><span class=\"token erb language-erb\"><span class=\"token delimiter punctuation\">&lt;%=</span> all_articles<span class=\"token punctuation\">.</span>select<span class=\"token punctuation\">{</span><span class=\"token operator\">|</span>a<span class=\"token operator\">|</span> a<span class=\"token punctuation\">[</span><span class=\"token symbol\">:published</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">.</span>to_json <span class=\"token delimiter punctuation\">%></span></span></code></pre></div>\n<p>ここはとてもシンプル。公開されている記事だけフィルタしてJSON化してます。<br>\nここで出てくる<code>all_articles</code>は自作のヘルパです。<a href=\"https://github.com/Leko/WEB-EGG/blob/master/config.rb\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">このファイル</a>に記述しており、以下の通りです。</p>\n<div class=\"gatsby-highlight\" data-language=\"ruby\"><pre class=\"language-ruby\"><code class=\"language-ruby\">helpers <span class=\"token keyword\">do</span>\n  <span class=\"token comment\"># ...</span>\n  <span class=\"token keyword\">def</span> <span class=\"token method-definition\"><span class=\"token function\">all_articles</span></span>\n    blog<span class=\"token punctuation\">.</span>articles<span class=\"token punctuation\">.</span>map<span class=\"token punctuation\">{</span><span class=\"token operator\">|</span>post<span class=\"token operator\">|</span>\n      <span class=\"token punctuation\">{</span>\n        objectID<span class=\"token punctuation\">:</span> <span class=\"token constant\">Digest</span><span class=\"token punctuation\">:</span><span class=\"token punctuation\">:</span><span class=\"token constant\">MD5</span><span class=\"token punctuation\">.</span>hexdigest<span class=\"token punctuation\">(</span>post<span class=\"token punctuation\">.</span>slug<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        title<span class=\"token punctuation\">:</span> post<span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">,</span>\n        date<span class=\"token punctuation\">:</span> post<span class=\"token punctuation\">.</span>date<span class=\"token punctuation\">,</span>\n        body<span class=\"token punctuation\">:</span> strip_tags<span class=\"token punctuation\">(</span>post<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        summary<span class=\"token punctuation\">:</span> strip_tags<span class=\"token punctuation\">(</span>post<span class=\"token punctuation\">.</span>summary<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        tags<span class=\"token punctuation\">:</span> post<span class=\"token punctuation\">.</span>tags<span class=\"token punctuation\">,</span>\n        published<span class=\"token punctuation\">:</span> post<span class=\"token punctuation\">.</span>published<span class=\"token operator\">?</span><span class=\"token punctuation\">,</span>\n        locale<span class=\"token punctuation\">:</span> post<span class=\"token punctuation\">.</span>locale<span class=\"token punctuation\">,</span>\n        slug<span class=\"token punctuation\">:</span> post<span class=\"token punctuation\">.</span>slug<span class=\"token punctuation\">,</span>\n        path<span class=\"token punctuation\">:</span> post<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span>path<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">end</span>\n  <span class=\"token comment\"># ...</span>\n<span class=\"token keyword\">end</span></code></pre></div>\n<ul>\n<li>objectIDという一位なキーがAlgoliaに必要</li>\n<li>記事の本文や冒頭文はHTMLになってるので、HTMLを剥がす必要がある</li>\n</ul>\n<p>という手を加えています。</p>\n<blockquote>\n<p>— <a href=\"http://webfood.info/middleman-how-to-strip-tag-to-plain-text/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Middlemanでstrip_tagsを使ってサマリーをplain textにする方法</a></p>\n</blockquote>\n<p>という感じでMiddlemanなら簡単に記事データをJSON化できたので、あとはそれをクライアントライブラリに渡すだけです。\n記事データの登録に成功すると、記事データがAlgoliaに登録されて、検索可能な状態になります。</p>\n<h2 id=\"検索機能を実装する\" style=\"position:relative;\"><a href=\"#%E6%A4%9C%E7%B4%A2%E6%A9%9F%E8%83%BD%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B\" aria-label=\"検索機能を実装する permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>検索機能を実装する</h2>\n<p>検索対象を登録したので、早速記事データを検索してみます。<br>\n実装の全体像は<a href=\"https://github.com/Leko/WEB-EGG/tree/master/source/javascripts/SearchApp\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">こちら</a>を見るといいかと思います。</p>\n<p>Reactと、<a href=\"https://github.com/almin/almin\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Almin</a>というユースケース駆動、CQRS、DDDと相性の良いFlux実装を使って実装してます。<br>\nAlgoliaを使って検索する、という点はオリジナルですが、実装の雛形はAlmin公式のTODO MVCのチュートリアルがほぼ全てです。<br>\nデータの取得方法と、取って来たデータの見せ方を変えただけ、という感じです。</p>\n<blockquote>\n<p>In this guide, we’ll walk through the process of creating a simple Todo app.</p>\n<p>— <a href=\"https://almin.js.org/docs/tutorial/todomvc/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Todo App · Almin.js</a></p>\n</blockquote>\n<p>上記チュートリアルで<code>MemoryDB</code>というオンメモリの値をDBかのように振る舞うインフラレイヤのアダプタを参考に、<a href=\"https://github.com/Leko/WEB-EGG/blob/master/source/javascripts/infra/adapter/Algolia.js\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Algolia用のインフラレイヤのアダプタ</a>を実装してあります。<br>\nAlmin自体の詳細はもっとノウハウを貯めて別の記事にて書ければと思います。</p>\n<h2 id=\"記事を書いたら自動でalgoliaにコンテンツが登録されるようにする\" style=\"position:relative;\"><a href=\"#%E8%A8%98%E4%BA%8B%E3%82%92%E6%9B%B8%E3%81%84%E3%81%9F%E3%82%89%E8%87%AA%E5%8B%95%E3%81%A7algolia%E3%81%AB%E3%82%B3%E3%83%B3%E3%83%86%E3%83%B3%E3%83%84%E3%81%8C%E7%99%BB%E9%8C%B2%E3%81%95%E3%82%8C%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%99%E3%82%8B\" aria-label=\"記事を書いたら自動でalgoliaにコンテンツが登録されるようにする permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>記事を書いたら自動でAlgoliaにコンテンツが登録されるようにする</h2>\n<p>当ブログはTravis CIで事前にビルドしたものをデプロイしているので、<br>\nサイトのビルド・デプロイ時に自動的に記事データをAlgoliaに登録する処理を入れてみました。</p>\n<div class=\"gatsby-highlight\" data-language=\"ruby\"><pre class=\"language-ruby\"><code class=\"language-ruby\">configure <span class=\"token symbol\">:build</span> <span class=\"token keyword\">do</span>\n  after_build <span class=\"token keyword\">do</span>\n    <span class=\"token keyword\">def</span> <span class=\"token method-definition\"><span class=\"token function\">update_search_index</span></span><span class=\"token punctuation\">(</span>path<span class=\"token punctuation\">)</span>\n      <span class=\"token constant\">Algolia</span><span class=\"token punctuation\">.</span>init application_id<span class=\"token punctuation\">:</span> <span class=\"token constant\">ENV</span><span class=\"token punctuation\">[</span><span class=\"token string\">'ALGOLIA_APP_ID'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> api_key<span class=\"token punctuation\">:</span> <span class=\"token constant\">ENV</span><span class=\"token punctuation\">[</span><span class=\"token string\">'ALGOLIA_API_KEY'</span><span class=\"token punctuation\">]</span>\n      index <span class=\"token operator\">=</span> <span class=\"token constant\">Algolia</span><span class=\"token punctuation\">:</span><span class=\"token punctuation\">:</span><span class=\"token constant\">Index</span><span class=\"token punctuation\">.</span><span class=\"token keyword\">new</span><span class=\"token punctuation\">(</span><span class=\"token constant\">ENV</span><span class=\"token punctuation\">[</span><span class=\"token string\">'ALGOLIA_INDEX'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n      batch <span class=\"token operator\">=</span> <span class=\"token constant\">JSON</span><span class=\"token punctuation\">.</span>parse<span class=\"token punctuation\">(</span><span class=\"token builtin\">File</span><span class=\"token punctuation\">.</span>read<span class=\"token punctuation\">(</span>path<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n      index<span class=\"token punctuation\">.</span>save_objects<span class=\"token operator\">!</span><span class=\"token punctuation\">(</span>batch<span class=\"token punctuation\">)</span>\n      <span class=\"token builtin\">File</span><span class=\"token punctuation\">.</span>delete<span class=\"token punctuation\">(</span>path<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">end</span>\n    update_search_index<span class=\"token punctuation\">(</span><span class=\"token string\">'./build/posts.json'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">end</span>\n<span class=\"token keyword\">end</span></code></pre></div>\n<p><code>build/posts.json</code>は、先ほどのJSON化するためのファイルが出力される場所です。<br>\nAlgoliaに登録したらもうJSONデータは使わないので消してあります。</p>\n<p>これで<code>middleman build</code>すれば自動的にAlgoliaに記事データが登録されます。<br>\n差分更新にするには記事の追加だけでなく変更や削除まで対応する必要があり、面倒だったので全件更新にしてます。<br>\n何か問題が起きたら差分更新にすると思います。</p>\n<h2 id=\"検索対象のデータとレスポンスに含めるフィールドを調整する\" style=\"position:relative;\"><a href=\"#%E6%A4%9C%E7%B4%A2%E5%AF%BE%E8%B1%A1%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E3%81%A8%E3%83%AC%E3%82%B9%E3%83%9D%E3%83%B3%E3%82%B9%E3%81%AB%E5%90%AB%E3%82%81%E3%82%8B%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E3%82%92%E8%AA%BF%E6%95%B4%E3%81%99%E3%82%8B\" aria-label=\"検索対象のデータとレスポンスに含めるフィールドを調整する permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>検索対象のデータと、レスポンスに含めるフィールドを調整する</h2>\n<p>記事の本文も検索対象に含めたかったのですが、記事本文が丸ごとレスポンスに入っているとデータ量が多くてパフォーマンスが出なかったので、調整をしました。</p>\n<p>検索する側のパラメータは</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">  <span class=\"token keyword\">async</span> <span class=\"token function\">find</span> <span class=\"token punctuation\">(</span>keyword<span class=\"token operator\">:</span> string<span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> Promise<span class=\"token operator\">&lt;</span>PostList<span class=\"token operator\">></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> algoliaOptions <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n      query<span class=\"token operator\">:</span> keyword<span class=\"token punctuation\">,</span>\n      hitsPerPage<span class=\"token operator\">:</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">,</span>\n      attributesToRetrieve<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n        <span class=\"token string\">'title'</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string\">'summary'</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string\">'tags'</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string\">'path'</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string\">'objectID'</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string\">'date'</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string\">'published'</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string\">'locale'</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string\">'slug'</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">const</span> response<span class=\"token operator\">:</span> AlgoliaResponse <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>index<span class=\"token punctuation\">.</span><span class=\"token function\">search</span><span class=\"token punctuation\">(</span>algoliaOptions<span class=\"token punctuation\">)</span>\n    <span class=\"token comment\">// ...</span>\n  <span class=\"token punctuation\">}</span></code></pre></div>\n<p><code>attributesToRetrieve</code>を指定すると、レスポンスに含めるフィールドを指定できます。<br>\n記事本文をレスポンスから除外したら、かなりパフォーマンスが上がりました。</p>\n<h2 id=\"検索にマッチした箇所をハイライト表示\" style=\"position:relative;\"><a href=\"#%E6%A4%9C%E7%B4%A2%E3%81%AB%E3%83%9E%E3%83%83%E3%83%81%E3%81%97%E3%81%9F%E7%AE%87%E6%89%80%E3%82%92%E3%83%8F%E3%82%A4%E3%83%A9%E3%82%A4%E3%83%88%E8%A1%A8%E7%A4%BA\" aria-label=\"検索にマッチした箇所をハイライト表示 permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>検索にマッチした箇所をハイライト表示</h2>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 668px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/761838713efce497bc886ac83f437bb9/76823/algolia-search-highlight.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 86.82634730538922%; position: relative; bottom: 0; left: 0; background-image: url('data:image/svg+xml,%3csvg%20xmlns=\\'http://www.w3.org/2000/svg\\'%20width=\\'400\\'%20height=\\'347\\'%20viewBox=\\'0%200%20400%20347\\'%20preserveAspectRatio=\\'none\\'%3e%3cpath%20d=\\'M244%2026l1%2014V12h147v14l1%2015V11H244v15M58%2056c0%204%200%205-2%205-3%200-3%200-3-3%200-1%200-2%201-1l2%201c2%200%201-2-2-3l-4%201h-1l-3-1c-4%200-4%200-3%203l-1%203c-1%201-1%200-1-1%200-4-1-5-4-5-2%200-2%201-2%202%200%203-5%205-6%203-1-1-1-1%201-1%203%200%203%200%203-2l-1-2c0-1-5%200-6%202-2%203-2%202-2-3%200-2%200-2-2-2-7%200-8%204-3%207%202%201%203%201%202%202h-3c-2-2-2-2-2%200s7%203%208%200h2c1%201%205%202%2011%202h10l-1-3c0-4%202-3%204%200%201%202%202%203%206%203%205%200%205%200%205-3l1-3%201%203c-1%203-1%203%201%203%203%200%202-7-1-8l-2-2-2-2c-1%200-2%201-1%205m106-3c1%202%201%202-1%202l-3%202-2-1c-4-3-10%201-7%205%201%202%205%203%207%201l2-1c1%201%202%202%205%202h3v-6c0-4-1-6-2-6-2%200-2%200-2%202m-93%206c0%204%201%204%203%204s2%200%201-1l-1-3c0-3%203-1%205%201%200%202%201%203%207%203%207%200%209-2%205-5l-1-2%202%201h1c0-2-1-2-3-2s-2%201-2%203-5%204-6%202c-1-1-1-1%201-1%203%200%204-2%201-3h-6l-3-1c-3%200-4%200-4%204M0%20131v56h8V75H0v56m336-45c-1%200-2%202-2%205%200%204%200%204%203%203h2l-2%201-1%202%203-1%203-1%202%202c2%200%202%200%202-3l-1-6c0-3%200-3-4-3l-5%201M39%2087c0%202-5%204-6%203-2-2-5-1-6%201-2%202-2%202-2%200l1-3c1-1%201-1-1-1-1%200-2%200-1%201l-1%202-1-1c-1-3-5-3-5%201l-1%203c-2%202-1%203%201%203s2%200%201%201c-2%201%204%201%2014%201h16v-7c1-3-4-2-6%201v-2c0-4%200-4-2-4l-1%201m38%204c0%204%200%205%202%205%201%200%202%200%201-2l3-3c2-1%202-1%202%202-1%203%200%203%202%203%201%200%202%200%201-2l1-2%202%202c1%203%205%203%207%200s0-5-7-5l-7-1-4-1-3-1v5m41-4h-4l-1%201%202%202%202%202c0%202%202%204%204%204s2%200%200-2c-2-1-2-2-2-4h2l3%202%201%201c0%202%201%203%209%203s8%200%207-2c0-2%200-3%202-3%201%200%202%201%202%203l2%202%202-1-1-1-1-1c0-3%203-2%204%201%201%202%201%202-1%202v2l6-8-4-1-5-1h-2l-4%201c-3%200-3%200-3%203%200%205-1%203-1-2%200-3-1-4-2-4l-1%206-1%203-1-5c0-3%200-4-2-4s-2%200-1%201%200%202-1%203c-2%201-3%200-4-2s-2-2-6-1m48%203c0%204%200%204-2%204l-2-1c0-1%201-2%202-1l1-1c0-3-4-2-5%200-3%204-1%205%2018%205h18l-2-3v-2l1-1h-3c-1%201-1%200-1-1%200-2-1-3-2-3l-1%204c0%204%200%204-2%204l-2-1c0-1%201-2%202-1l1-1c0-2-3-3-5-1h-2c-1-2-6%200-6%203l-1%203v-3l-2-4-2-2-2-1-1%204m67%200l-1%204-1-2c1-3-2-6-3-4l-4%201c-2%200-3%201-4%203l-1%203v-3c0-3%200-3-5-3h-6v7h6l6-1h2l4%201c1-1%201-1-1-2s-2-1%200-1l2-1c1-1%201%200%201%201%200%203%200%203%206%203%205%200%206%200%206-2h1c1%202%205%203%207%201h1l4%201h4l-1-5c0-4-1-5-2-5l-1%201c0%202-4%204-7%203h-4l-2%201-3-2-2-1-1-2c-2%200-2%201-1%204m40-2v4l-1%203-1-3c0-3%200-3-5-3h-6v7h20l23%201c1%202%202%202%204%201%202%200%203-4%201-4v-1c2-3%201-4-2-4s-4%201-4%203c0%203%200%204-1%201%200-3-1-4-4-4s-3%200-3%203c0%204-1%203-1-1-1-4-3-7-3-4%200%202-1%202-3%202l-4%202-1%203v-3c0-3-4-2-5%201l-1%203v-7c-1-2-3-2-3%200M40%20134v4h5l6-1h1c2%202%206%201%206%200l-2-2v1l-1%201c-2-1-2-2-1-4h4c-1-2-4-2-6%200h-1c0-2-3-3-5-1h-1l-3-1c-2%200-2%201-2%203m149-1l-1%204v2l11%201h10v-3c1-4%200-5-3-3-1%201-1%201-1-1%201-1%200-2-1-2v1l-6%201-3-1-1-1-1%202v3l-1-3c-2-3-3-3-3%200m49%202l1%203%201-1%202-2c1-1%201%200%201%201l1%202%201-1%201-2%201%202c0%202%204%201%204-1h2l2%202%201-1v-2l1%202c2%202%202%202%202%200%201-2%203-3%203-1%201%202%204-1%204-3h1v5l2-4c0-3-4-4-6-2l-2%201-2%201h-1l-3-1-3%201h-1l-4-1-4-1c-3-2-5-1-5%203m-221%208c-2%201-2%203-2%206l1%205h11v-5c0-6-2-9-5-7h-2c0-1-1-1-3%201m226%201l-1%201-3%202-2%201c0-4-6-3-6%202-1%202-1%202-1-1l1-5c1-1%201-1-1-1-1%200-2%201-1%203%200%202%200%202-1%201l-2-3-1-1c-1%200-2%201-2%203l-1%204v3c0%203%2026%203%2026%200h1l2%202c1%200%202-2%202-5%200-4-1-4-3-5l-5%202c-1%202-1%202-1-1v-4c-1%200-2%201-1%202m49%201l2%203%202%203-2-1h-2c-1%201-1%201-1-1%200-4-7-5-8-1h-1l-1-2c-2%200-2%201-1%203%202%204%202%204%204%200%200-1%201-2%202-1v3h13l3%201-1-1c-2-2-1-3%202-4%202%200%202%201%202%203-1%201%200%202%201%202s2-1%201-2c0-2%200-3%202-3l1%203c-1%201%200%202%201%202l1-3c0-3%200-3-3-3h-5c-2-1-4%200-4%201h-4c-2-1-3-3-1-3l1%201%201%201%201-1c0-3-5-2-6%200m-188%2032l-2%202-3%202h-1c0-4-6-3-7%200-1%202-1%202-1%200s-1-2-4-2c-4%200-4%200-3%208l15%201h15l-1-3c0-3%201-4%202-3v2c-1%202-1%203%201%204l6-2%202-1c1%200%202-1%201-3v-3h-8c-7%200-8%200-9%202v-2l-1-4-2%202m-61%201l-2%201h-4c-1%202-2%202-3%200-2-2-5%200-5%203%200%202%201%203%207%203l6-1h-1c-2%200-2%200-2-2%200-1%200-2%201-1h2c1-1%201-1%201%201%200%203%200%203%204%203s5%200%204-2l1-2%202%202c1%203%206%202%207-1%200-2-3-5-4-3-1%201-10%200-11-1h-3m30%2014v3c0%202%200%202-1%201l-1-1-1-1c-2-2-3-1-2%201%200%202%200%202-1%201l-1-2h-1c-2-1-2-1-2%201v3l-1-3c-1-3-2-3-2%202v3h19v-5l1%203%201%201%201-1%201%201v1l1-1-1-2v-1c1-2%200-3-1-3v2h-1l-3-2v2h-2l-1-1c0-2-1-2-2-2m-55%2022v1c-1-2-3%202-3%206v4h12v-5c0-6-2-8-5-6h-2l-1-1-1%201m225%202l-1%201-3%202-2%202v-2c-1-4-5-2-6%203-1%201-1%201-1-2l1-4v-1c-1%200-2%201-2%203l-1%203-1-3-3-3v3c2%201%202%203%201%203-1%201-1%201-1-1s0-2-2%200c-1%204-1%204%201%204s2%200%201%201c-2%201%203%201%2013%201%209%200%2015%200%2014-1l-1-4c0-2-1-3-2-2h-1c0-1-1-1-2%201-1%201-1%201-1-2%201-3%201-4-1-4v2m-66%2031v3l-1%202v4c-2%202%201%203%204%201h5c2%200%203-2%201-2v-2c2-4-1-7-5-7l-4%201m-138%204c0%204-2%206-3%204-2-1-1-3%201-2l1-1c0-2-3-2-4-1h-8c-1%201-2%201-2-1l-4-2c-3%200-3%200-3%205%200%204%200%204%202%204s2%200%201-1c-1-2-1-2%201-3s3%200%203%201c1%203%204%204%206%202h3l8%201h6l-2-2v-3l1-1h-3c-1%201-1%200-1-1%200-5-3-4-3%201m91-2l-1%201-3%202h-2c-1-3-5-2-6%200-2%203-3%202-2-1%202-3%202-3%200-3-1%200-2%201-1%202l-1%202-1-2c-1-3-5-3-5%201l-1%204c-1%202-1%202%201%202s2%200%201%201l15%201h18v-12l-1%202-3%201c-3%200-4%201-5%203s-1%202-1-2c1-4%201-4-1-4-1%200-2%200-1%202m-58%204c-2%204-2%204%200%204l1-1%201-2%202%202%204%201c4%200%205%200%204-2l1-2%204-2c1-2%201-2%201%202-1%203%200%204%201%204%202%200%203-1%203-4v-5H80l1%204c0%204%200%204-1%203l-3-5c-1-3-2-2-5%203m-56%2033l-1%206%201%205h11v-5c0-4-1-7-3-7v2h-1c-1-2-6-2-7-1m207%201l2%202v4l-1-1c-1-3-1-3-1-1l-1%203v2c0%202%202%202%2015%202h16v-5l-1-4v4l-1%203-1-5c0-3-3-2-5%200-1%202-1%202-1-1v-4l-1%202-2%201-3%202-1%201c0-2%200-2-2%200l-2%203v-3c0-2%200-2-2-1-1%202-1%202-1%200l1-4v-1c-1%200-2%201-2%203l-1%204-1-4c-2-3-4-4-4-2\\'%20fill=\\'%23d3d3d3\\'%20fill-rule=\\'evenodd\\'/%3e%3c/svg%3e'); background-size: cover; display: block;\"\n  ></span>\n  <picture>\n          <source\n              srcset=\"/static/761838713efce497bc886ac83f437bb9/5251b/algolia-search-highlight.webp 167w,\n/static/761838713efce497bc886ac83f437bb9/7390e/algolia-search-highlight.webp 334w,\n/static/761838713efce497bc886ac83f437bb9/7c056/algolia-search-highlight.webp 668w,\n/static/761838713efce497bc886ac83f437bb9/0a92e/algolia-search-highlight.webp 1002w,\n/static/761838713efce497bc886ac83f437bb9/6a022/algolia-search-highlight.webp 1038w\"\n              sizes=\"(max-width: 668px) 100vw, 668px\"\n              type=\"image/webp\"\n            />\n          <source\n            srcset=\"/static/761838713efce497bc886ac83f437bb9/21521/algolia-search-highlight.png 167w,\n/static/761838713efce497bc886ac83f437bb9/86d36/algolia-search-highlight.png 334w,\n/static/761838713efce497bc886ac83f437bb9/74866/algolia-search-highlight.png 668w,\n/static/761838713efce497bc886ac83f437bb9/d69c4/algolia-search-highlight.png 1002w,\n/static/761838713efce497bc886ac83f437bb9/76823/algolia-search-highlight.png 1038w\"\n            sizes=\"(max-width: 668px) 100vw, 668px\"\n            type=\"image/png\"\n          />\n          <img\n            class=\"gatsby-resp-image-image\"\n            src=\"/static/761838713efce497bc886ac83f437bb9/74866/algolia-search-highlight.png\"\n            alt=\"ハイライト\"\n            title=\"ハイライト\"\n            loading=\"lazy\"\n            decoding=\"async\"\n            style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n          />\n        </picture>\n  </a>\n    </span></p>\n<p>当サイトの検索機能はキーワードにマッチしたところがハイライトされるようにしてるのですが、<br>\nこれは実装したわけではなく、Algolia側が勝手にやってくれてます。まじすごい。</p>\n<p>レスポンスの中に<code>_highlightResult</code>というキーが入っており、ここにハイライト済みのHTMLが格納されています。<br>\nHTML文字列をそのまま表示させるので普通にReactは使えなくなってしまうのですが、<a href=\"https://facebook.github.io/react/docs/dom-elements.html#dangerouslysetinnerhtml\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">dangerouslySetInnerHTML</a>というpropを使うとお茶を濁せます。</p>\n<h2 id=\"まとめ\" style=\"position:relative;\"><a href=\"#%E3%81%BE%E3%81%A8%E3%82%81\" aria-label=\"まとめ permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>まとめ</h2>\n<ul>\n<li>Algoliaはすごい</li>\n<li>Algoliaで検索するときはレスポンスのデータ量を最小限にすると早い</li>\n<li>Alminとても可能性を感じる、手に馴染む</li>\n</ul>\n<p>他にも検索可能なフィールドを絞ったり、検索結果の優先度設定などパラメータをいじっているのですが、<br>\nそれだけで一本記事書けるレベルに量が多くなると思うので、この記事では割愛します。</p>\n<p>ぜひAlgoliaとAlmin試してみてください。</p>","timeToRead":13,"frontmatter":{"title":"Algoliaを利用してMiddleman製サイトに検索機能を実装する","tags":["Algolia","JavaScript","Ruby","Middleman","React","Almin","DDD"],"date":"August 17, 2017","featuredImage":{"childImageSharp":{"fluid":{"tracedSVG":"data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='400'%20height='130'%20viewBox='0%200%20400%20130'%20preserveAspectRatio='none'%3e%3cpath%20d='M0%2060v35h13l23-1c12%200%2014-1%2011-5-1-2%200-2%2050-2a1654%201654%200%200096-4c0-4-1-4-56-5l-57-1c-16-4-17-26-3-33%204-2%208-2%2063-2a603%20603%200%200023-2c-27%200-31%200-30-1l1-4c-1-2-2-2-29-2H77l1-3-1-4-39-1H0v35m258-6c-2%204%204%2010%207%208s5-6%204-8c-1-3-10-3-11%200m76%205l-2%208-2%204h9c8%200%208%200%209-3l-1-3c-1-2%200-2%209-2h11v-6l-17-1h-16v3'%20fill='%23d3d3d3'%20fill-rule='evenodd'/%3e%3c/svg%3e","aspectRatio":3.0925925925925926,"src":"/static/805aa3b7b4b5304126127d01dcd71951/8eab8/featured-image.png","srcSet":"/static/805aa3b7b4b5304126127d01dcd71951/1ec58/featured-image.png 334w,\n/static/805aa3b7b4b5304126127d01dcd71951/ccb4a/featured-image.png 668w,\n/static/805aa3b7b4b5304126127d01dcd71951/8eab8/featured-image.png 1336w,\n/static/805aa3b7b4b5304126127d01dcd71951/85e22/featured-image.png 2004w,\n/static/805aa3b7b4b5304126127d01dcd71951/a9ec1/featured-image.png 2672w,\n/static/805aa3b7b4b5304126127d01dcd71951/b3888/featured-image.png 2878w","srcWebp":"/static/805aa3b7b4b5304126127d01dcd71951/f7e47/featured-image.webp","srcSetWebp":"/static/805aa3b7b4b5304126127d01dcd71951/cd98f/featured-image.webp 334w,\n/static/805aa3b7b4b5304126127d01dcd71951/7535d/featured-image.webp 668w,\n/static/805aa3b7b4b5304126127d01dcd71951/f7e47/featured-image.webp 1336w,\n/static/805aa3b7b4b5304126127d01dcd71951/f6b67/featured-image.webp 2004w,\n/static/805aa3b7b4b5304126127d01dcd71951/f71bf/featured-image.webp 2672w,\n/static/805aa3b7b4b5304126127d01dcd71951/650fc/featured-image.webp 2878w","sizes":"(max-width: 1336px) 100vw, 1336px"}}}}}},"pageContext":{"slug":"/implement-site-search-with-algolia/","previous":{"fields":{"slug":"/how-to-use-webpack-with-middleman/"},"frontmatter":{"title":"MiddlemanのビルドにWebpackを組み込む方法","tags":["Middleman","webpack","JavaScript"]}},"next":{"fields":{"slug":"/how-to-build-and-deploy-to-deploygate-in-cli/"},"frontmatter":{"title":"CLIだけでReact NativeアプリをビルドしてDeployGateにデプロイする方法","tags":["iOS","Android","React Native","DeployGate"]}}}},
    "staticQueryHashes": ["2585454260","2954598359"]}