Route360

Gatsby.jsで作る静的サイトにAlgoliaの検索システムを導入する

目次

Algoliaは、超高速のサイト内検索を実現する、同名の会社の検索エンジン製品です。

さまざまなサイトに採用されており、エンジニアの方であればドキュメンテーション系でAlgoliaのロゴをご覧になったこともあると思います。Gatsby.jsのドキュメントでも採用されていますね。

Google Custom Searchは導入の敷居は低いですが、せっかくのJamstackサイトがGoogleのスクリプトで重くなってしまうため、今回このAlgoliaをGatsby.jsサイトに導入してみました。

動作環境:

  • Node.js v20.14.0
  • React v18.3.1
  • Gatsby.js v5.13.7
  • gatsby-plugin-algolia v1.0.3
  • algoliasearch v5.0.0
  • react-instantsearch v7.12.4

ざっくり4ステップです。

  1. Algoliaへのアカウント登録・プロジェクト側の設定
  2. 検索用データの生成・保存
  3. 検索結果をサイトに表示する機能の構築
  4. スタイルを整える

尚、今回の例では「Markdownのブログを検索させる」前提とします。ヘッドレスCMSからGraphQLクエリを引っ張ってきている場合は、クエリの取得部分をご自身の状況に合わせて書き換えてください。

Algoliaにアカウント登録

まずはAlgoliaにサインアップし、新規のインデックスデータの名前を作ります。インデックス名は、運用環境によりdev_test_prod_などの接頭語をつける旨が公式でアドバイスされています。

Algoliaの新規インデックス登録画面

©Algolia

インデックス名を登録したら、「2. Configure search relevance」を無視して、左下の⚙️Settings -> API Keysへ進みます。

Algoliaの新規インデックス登録画面

©Algolia

「API Keys」ページで、必要なAPIキーを確認。必要なのは、上から3つです。

  • Application ID
  • Search-Only API Key
  • Admin API Key

AlgoliaのAPI確認画面

©Algolia

これらは後に必要になるため、Algoliaの画面はとりあえずこのままにしておきます。

Gatsby.js用のAlgolia公式プラグイン(検索データ作成用)

Algoliaが用意しているGatsby.js用のプラグイン、gatsby-plugin-algoliaをインストール。

# npmの場合
npm install gatsby-plugin-algolia

# yarnの場合
yarn add gatsby-plugin-algolia

インストールが完了したら、gatsby-config.jsにプラグイン情報を追記します。

gatsby-config.js
require("dotenv").config({
  path: `.env.${process.env.NODE_ENV}`,
})

module.exports = {
  //...
  plugins: [
    //...
    {
      resolve: `gatsby-plugin-algolia`,
      options: {
        appId: process.env.ALGOLIA_APP_ID,
        apiKey: process.env.ALGOLIA_API_KEY,
        indexName: process.env.ALGOLIA_INDEX_NAME,
        queries: [
          {
            query: `{
              // ここにクエリを追加
            }`,
            transformer: ({ data }) =>
              data.allMarkdownRemark.edges.flatMap(({ node }) => {
                return {
                  // ここにクエリからAlgoliaに登録するオブジェクトを記載
                }
              }),
          },
        ],
        chunkSize: 10000,
      },
    },
  ],
}

先ほどのAlgoliaのページで確認したAPIキーのうち、アプリケーションIDAdmin API Keyの2つと、作成したインデックス名を環境変数として使用します。.env.productionファイルに記載。

.env.production
ALGOLIA_APP_ID=[Application ID]
ALGOLIA_API_KEY=[Admin API Key]
ALGOLIA_INDEX_NAME=[your_index_name]

検索データの構築はビルド時にのみ動くので、.env.developmentには記載不要です(ALGOLIA_INDEX_NAMEのみあとで使用)。

検索用データを生成してAlgoliaに送る

検索用データ生成ようのAlgolia製Gatsby.js用プラグイン

Algoliaを使うには、Algolia上にあらかじめ検索用のデータを保存しておく必要があります。データ構築・保存作業に使うのが、先ほどインストールしたgatsby-plugin-algoliaです。

gatsby-config.jsのプラグイン情報に、Algoliaに保存したいインデックスデータのクエリを追記すれば、もう動きます。

とは言え、「何を検索データとしてAlgoliaに保存しておきたいか」が重要です。不要なデータもコストを消費してしまうため、厳選しましょう。

当記事の例では、MarkdownのYAML Frontmatterデータから、以下を利用することにします。

  • タイトル
  • スラッグ(IDとして使用)
  • 抜粋文
  • カテゴリー

Algoliaに送るクエリはこうなりました。

gatsby-config.js
module.exports = {
  //...
  plugins: [
    //...
    {
      resolve: `gatsby-plugin-algolia`,
      options: {
        appId: process.env.ALGOLIA_APP_ID,
        apiKey: process.env.ALGOLIA_API_KEY,
        indexName: process.env.ALGOLIA_INDEX_NAME,
        queries: [
          {
            query: `{
              allMarkdownRemark {
                edges {
                  node {
                    frontmatter {
                      title
                      slug
                      description
                      categories
                    }
                  }
                }
              }
            }`,
            transformer: ({ data }) =>
              data.allMarkdownRemark.edges.flatMap(({ node }) => {
                return {
                  objectID: node.frontmatter.slug,
                  excerpt: node.frontmatter.description,
                  title: node.frontmatter.title,
                  categories: node.frontmatter.categories.map(
                    category => category.title
                  ),
                }
              }),
          },
        ],
        chunkSize: 10000,
      },
    },
  ],
}

AlgoliaではobjectIDがデータ管理に使われており、objectIDを基準に差分判定がされます。

今回はスラッグをobjectIDにしていますが、スラッグが変更になると、差分判定にムダが出てしまいます。スラッグが頻繁に変わる可能性がある場合は、他のデータを充てるようにしてください。

ビルドをして検索用データをAlgoliaに送る

ここまでで、検索用データの構築・Algoliaへ送信の準備が整いました。

ローカル上でビルドをしてみて、ビルドの終盤にAlgoliaへデータ送信が行われた旨が表示されれば完了です。

gatsby build
...
success index to Algolia - 10.728s - Done!
...

Algoliaのダッシュボードを見ると、インデックスにデータが保存されているのが確認できます。

Algoliaのインデックス済みアイテム

©Algolia

クエリにimage:url含めて送れば、画像を登録することも可能です。

検索結果を表示させる

次に、検索結果をサイト上で表示するための作業を行います。

ライブラリのインストール

検索結果を表示するために使うライブラリは、react-instantsearchです(React v16.8.0以上)。algoliaseachも必要なので、同時にインストールします。

# npmの場合
npm install algoliasearch react-instantsearch

# yarnの場合
yarn add algoliasearch react-instantsearch

Algoliaはこれまで複数の同様のライブラリをリリースしており、上記react-instantsearchが2023年22月時点の最新型となっています(重要ポイント)。

公式ドキュメントではこれまでのすべてのライブラリの情報が収められている上、別々のライブラリでも同じ名前のコンポーネントがあるので(互換性もあったりなかったり)、ドキュメント検索時には注意が必要です。

当エントリーを参考にしてAlgoliaを導入する場合は、ドキュメント検索時に画面右下のライブラリ名が「React InstantSearch v7」になっているかを確認してください。

Algoliaのドキュメント

©Algolia

検索結果を表示させる

componentsフォルダーに、algolia.jsというファイルを用意。以下のコードは、検索結果を表示させるための基本の形です。

/src/components/algolia.js
import React, { useMemo } from "react"
import { algoliasearch } from "algoliasearch"
import { InstantSearch } from "react-instantsearch"

const Algolia = () => {
  const searchClient = useMemo(
    () =>
      algoliasearch(
        process.env.GATSBY_ALGOLIA_APP_ID,
        process.env.GATSBY_ALGOLIA_SEARCH_KEY
      ),
    []
  )

  return (
    <InstantSearch
      searchClient={searchClient}
      indexName={process.env.ALGOLIA_INDEX_NAME}
    >
      {/* ここにウィジェット */}
    </InstantSearch>
  )
}

export default Algolia

searchClientuseMemo()によりメモ化して、再レンダリングしパフォーマンス向上を図ります(参考)。

環境変数を.env.development.env.productionの両方に記載。

ALGOLIA_INDEX_NAME=[your_index_name]
GATSBY_ALGOLIA_APP_ID=[Application ID]
GATSBY_ALGOLIA_SEARCH_KEY=[Search-Only API Key]

ALGOLIA_INDEX_NAMEは先ほど書いたものと同じ

検索ボックスを作る

検索ボックスは、SearchBoxというウィジェットを利用。

/src/components/algolia.js
import React, { useMemo } from "react"
import { algoliasearch } from "algoliasearch"
import { InstantSearch, SearchBox } from "react-instantsearch"

const Algolia = () => {
  const searchClient = useMemo(
    () =>
      algoliasearch(
        process.env.GATSBY_ALGOLIA_APP_ID,
        process.env.GATSBY_ALGOLIA_SEARCH_KEY
      ),
    []
  )

  return (
    <InstantSearch
      searchClient={searchClient}
      indexName={process.env.ALGOLIA_INDEX_NAME}
    >
      <SearchBox />
    </InstantSearch>
  )
}

export default Algolia

このAlgoliaコンポーネントを他のコンポーネントやテンプレート内で使えば、サイト上に検索ボックスが表示できます。

検索結果表示部分を作る

検索結果表示には、Hitsというウィジェットを利用。

/src/components/algolia.js
import React, { useMemo } from "react"
import { algoliasearch } from "algoliasearch"
import { InstantSearch, SearchBox, Hits } from "react-instantsearch"

const Algolia = () => {
  const searchClient = useMemo(
    () =>
      algoliasearch(
        process.env.GATSBY_ALGOLIA_APP_ID,
        process.env.GATSBY_ALGOLIA_SEARCH_KEY
      ),
    []
  )

  const Hit = ({ hit }) => {
    return (
      <Link to={`/blog/${hit.objectID}/`}>
        <article>
          <h1>{hit.title}</h1>
          <p>{hit.excerpt}...</p>
          <ul>
            {hit.categories.map(category => (
              <li>{category}</li>
            ))}
          </ul>
        </article>
      </Link>
    )
  }

  return (
    <InstantSearch
      searchClient={searchClient}
      indexName={process.env.ALGOLIA_INDEX_NAME}
    >
      <SearchBox />
      <Hits hitComponent={Hit} />
    </InstantSearch>
  )
}

export default Algolia

スタイルを整える

後はスタイルを整えるだけです。

  1. 独自クラス名を調整する
  2. ウィジェットにclassNamesまたはclassNameプロパティを付与

Algoliaによるコードには独自クラス名が割り振られているので、そのクラス名を利用してスタイリングが可能です。

または、classNames``classNameプロパティを使って、CSSモジュールやTailwind CSSなどでスタイリングすることもできます。

(本番前に)Gatsby.jsプラグインの調整

Algoliaへデータを送信しない設定

Algoliaへビルドの度に送信しないようにするには、dryRuntrueにしておきます。

gatsby-config.js
{
resolve: `gatsby-plugin-algolia`,
  options: {
    //...
    dryRun: true,
  }
}

この設定により、ビルドの最後にAlgoliaへのデータ送信が行われなかった旨が表示されます。

gatsby build
...
==== THIS IS A DRY RUN ====================
- No records will be pushed to your index
- No settings will be updated on your index

私の場合は、必要な時のみ「dryRun: false」とし、通常のビルドではAlgoliaへ送信しないようにしています。

状況に応じて、どのポイントのビルドでインデックスをAlgoliaに送るかご判断ください。

参考サイト