
多言語のGatsbyJSサイトにAlgoliaで全文検索を導入しましょう
ブログとドキュメントサイトといえば、GatsbyJSが有力な候補者となるでしょう。レンダリングのスピードがかなり早く、プラグインの数が多くSEO対策も簡単にできます。ドキュメントがかなりわかりやすく、学習コストが低いと言えるでしょう。ReactJSのオフィシャルサイト https://reactjs.org/ がGatsbyJS で作られていますね。ブログ、ドキュメントサイトなら検索体験が非常に重要なポイントとなります。最近世によく使われる全文検索マネージドサービスはAlgoliaとなります。GatsbyJSのドキュメントサイトにすでにAlgoliaの導入チュートリアルが載ってあります: https://www.gatsbyjs.com/docs/adding-search-with-algolia/。しかし、多言語サイトはどうすればいいでしょうか?
今日は一歩踏まえて、多言語のGatsbyJSサイトにAlgoliaの導入方法を紹介したいと思います。
前提条件
- JavaScriptとGatsbyJSの基本知識
gatsby-plugin-i18nで作った多言語のGatsbyJSサイト(langKeyが必要) なければ、このプロジェクト をチェックアウトしてください
Algoliaの導入
GatsbyJSのオフィシャルサイトのドキュメントにほぼ書いてあります。下記の手順と唯一の違いは、Pagesパラメータをハードコーディングではなく、.envのGATSBY_ALGOLIA_INDEX_NAMEで定義することです。
Algoliaに全ページのインデックス作成
- プラグインのインストール
yarn add dotenv escape-string-regexp gatsby-plugin-algollia
.envをプロジェクトのルートフォルダに追加し、下記のものを定義
GATSBY_ALGOLIA_APP_ID=<App ID>
GATSBY_ALGOLIA_SEARCH_KEY=<Search-Only API Key>
ALGOLIA_ADMIN_KEY=<Admin API Key>
GATSBY_ALGOLIA_INDEX_NAME=Pages
- Algoliaのインデックスデータ生成用のスクリプトを作成。
./src/utilsフォルダにalgoliaQueries.jsを作成。
const escapeStringRegexp = require('escape-string-regexp');
const pagePath = `content`;
const indexName = process.env.GATSBY_ALGOLIA_INDEX_NAME;
const pageQuery = `{
pages: allMarkdownRemark(
filter: {
fileAbsolutePath: { regex: "/${escapeStringRegexp(pagePath)}/" },
}
) {
edges {
node {
id
frontmatter {
title
}
fields {
slug
}
excerpt(pruneLength: 5000)
}
}
}
}`;
function pageToAlgoliaRecord({ node: { id, frontmatter, fields, ...rest } }) {
return {
objectID: id,
...frontmatter,
...fields,
...rest,
};
}
const queries = [
{
query: pageQuery,
transformer: ({ data }) => data.pages.edges.map(pageToAlgoliaRecord),
indexName,
settings: { attributesToSnippet: [`excerpt:20`] },
},
];
module.exports = queries;
gatsby-plugin-algoliaプラグインの設定
require('dotenv').config();
module.exports = {
{
resolve: `gatsby-plugin-algolia`,
options: {
appId: process.env.GATSBY_ALGOLIA_APP_ID,
apiKey: process.env.ALGOLIA_ADMIN_KEY,
queries: require('./src/utils/algoliaQueries'),
},
},
}
-
Algoliaサイトから
Application ID,Search-Only API Key,Admin API Keyを取得し、.envファイルに設定GatsbyJSのオフィシャルサイトのドキュメントを参考にしてください
-
インデックス生成
# gatsby build
yarn build
-
完了
全てのページがAlgoliaにインデックスズミとなります。次はビュー側のコンポーネント作成しましょう。
SearchBoxコンポーネントの作成
単純なReactコンポーネントの作成となりますが、Gatsbyのオフィシャルサイトドキュメントを参考にしてください。あるいはこちらのコミットをコピーしても構いません。
それでも手間かなと感じる方は、こちらのプロジェクトをチェックアウトし、ご利用ください。
多言語検索の実装
ここまで、SearchBoxでAlgoliaから検索できるようになりました。ただ、言語ごとの検索が有効にされていないため、言語とかかわらずに全てのページが出てきます。

これを直しましょう。
AlgoliaのインデックスにlangKeyの追加
言語ごと検索ができるように、Algoliaのインデックスに言語区別するためのキー(langKey)を追加し、フィルターをかけなければいけません。
まずAlgoliaのインデックス生成用のalgoliaQueries.jsを下記の通りに直しましょう。
- Algoliaに
langKeyのインデックスを生成できるように、langKeyをpageQueryに追加
fields {
slug
langKey
}
- Algoliaに多言語の設定を有効に
settings: {
attributesToSnippet: [`excerpt:20`],
searchableAttributes: ['title', 'excerpt'],
ranking: ['typo', 'geo', 'words', 'filters', 'proximity', 'attribute', 'exact', 'custom'],
attributesForFaceting: ['filterOnly(langKey)'],
indexLanguages: ['en', 'zh'],
queryLanguages: ['en', 'zh'],
}
編集済のalgoliaQueries.jsがこのようになります:
const escapeStringRegexp = require('escape-string-regexp');
const pagePath = `content`;
const indexName = process.env.GATSBY_ALGOLIA_INDEX_NAME;
const pageQuery = `{
pages: allMarkdownRemark(
filter: {
fileAbsolutePath: { regex: "/${escapeStringRegexp(pagePath)}/" },
}
) {
edges {
node {
id
frontmatter {
title
}
fields {
slug
langKey
}
excerpt(pruneLength: 5000)
}
}
}
}`;
function pageToAlgoliaRecord({ node: { id, frontmatter, fields, ...rest } }) {
return {
objectID: id,
...frontmatter,
...fields,
...rest,
};
}
const queries = [
{
query: pageQuery,
transformer: ({ data }) => data.pages.edges.map(pageToAlgoliaRecord),
indexName,
settings: {
attributesToSnippet: [`excerpt:20`],
attributesForFaceting: ['filterOnly(langKey)'],
indexLanguages: ['en', 'zh'],
queryLanguages: ['en', 'zh'],
searchableAttributes: ['title', 'excerpt'],
ranking: ['typo', 'geo', 'words', 'filters', 'proximity', 'attribute', 'exact', 'custom'],
},
},
];
module.exports = queries;
SearchBoxにフィルター機能を有効
Algoliaのフィルター関連のドキュメントがわかりやすく思いません。苦労したが、フィルター関連の設定方法のドキュメントに辿り着けました。
ドキュメントの通り、SearchBox(./src/components/Search/index.js)コンポーネントを下記のように直しましょう。
Configureコンポーネントの導入
import { Configure } from 'react-instantsearch-dom';
langKeyフィルターの追加
<Configure filters={`langKey:${lang}`} />
まとめると:
/* eslint-disable react/prop-types */
import React, { createRef, useState } from 'react';
import algoliasearch from 'algoliasearch/lite';
import { InstantSearch, Configure } from 'react-instantsearch-dom';
import { useLang } from 'context/LanguageContext';
import { ThemeProvider } from 'styled-components';
import StyledSearchBox from './StyledSearchBox';
import StyledSearchResult from './StyledSearchResult';
import StyledSearchRoot from './StyledSearchRoot';
import useClickOutside from './useClickOutside';
const theme = {
foreground: '#050505',
background: 'white',
faded: '#888',
};
export default function Search({ indices }) {
const { lang } = useLang();
const rootRef = createRef();
const [query, setQuery] = useState();
const [hasFocus, setFocus] = useState(false);
const searchClient = algoliasearch(
process.env.GATSBY_ALGOLIA_APP_ID,
process.env.GATSBY_ALGOLIA_SEARCH_KEY,
);
useClickOutside(rootRef, () => setFocus(false));
return (
<ThemeProvider theme={theme}>
<StyledSearchRoot ref={rootRef}>
<InstantSearch
searchClient={searchClient}
indexName={indices[0].name}
// eslint-disable-next-line no-shadow
onSearchStateChange={({ query }) => setQuery(query)}
>
<Configure filters={`langKey:${lang}`} />
<StyledSearchBox onFocus={() => setFocus(true)} hasFocus={hasFocus} />
<StyledSearchResult show={query && query.length > 0 && hasFocus} indices={indices} />
</InstantSearch>
</StyledSearchRoot>
</ThemeProvider>
);
}
インデックスの再生成
これで多言語の設定が終わりまして、langKey付きのインデックスを生成して、SearchBoxで検索をもう一度やってみましょう。
yarn build

完璧!
完了
GatsbyJSサイトの多言語検索を実装するのに大変とはいえませんが、ドキュメントがまだ充実ともいえないでしょう。ご参考まで。では。
実装済のプロジェクトはこちらへ。

Blog part of ThunderMiracle.com
コメントは表示領域に入ると読み込みます