はじめに
gatsby-plugin-algoliagatsby-plugin-algolia と react-instantsearchreact-instantsearch を利用して当ブログに記事を全文検索できる機能を追加しました
この記事含めまだ3件しか書いていないので全文検索を活用するべく2025年はブログの1年にしていきたい所存
ページのIndex
レコードが10KBalgolia-record-sizeに収まるよう本文を3000文字に絞っています
異体字etcに関してはフォントをサブセット化している関係上、当ブログではそもそも使用できないため考慮していません
絵文字も性格上あまり使わない気がするので多分大丈夫
import type { GatsbyConfig } from "gatsby";
import { parse } from "@formkit/tempo";
require("dotenv").config({
path: `.env.${process.env.NODE_ENV}`,
});
const config: GatsbyConfig = {
siteMetadata: {
title: "blog.miyamo.today",
siteUrl: "https://blog.miyamo.today",
lang: "ja",
},
graphqlTypegen: true,
plugins: [
{
resolve: `gatsby-plugin-algolia`,
options: {
appId: process.env.GATSBY_ALGOLIA_APP_ID,
apiKey: process.env.ALGOLIA_ADMIN_KEY,
indexName: process.env.GATSBY_ALGOLIA_INDEX_NAME,
queries: [
{
query: `{
allMarkdownRemark(filter: { frontmatter: { id: { ne: "Noop" } } }) {
nodes {
excerpt(pruneLength: 3000, truncate: true)
frontmatter {
id
title
tags {
name
}
thumbnail
createdAt
}
}
}
site {
siteMetadata {
title
siteUrl
lang
}
}
}`,
transformer: ({
data: { site, allMarkdownRemark },
}: {
data: { site: SiteMetadataForAlgolia; allMarkdownRemark: GetAllArticlesForAlgoria };
}) =>
allMarkdownRemark.nodes.flatMap((node) => {
return {
id: node.frontmatter?.id ?? "",
content: node.excerpt ?? "",
title: node.frontmatter?.title ?? "",
publishedAt: parse(
node.frontmatter?.createdAt ?? "1970-01-01T00:00:00Z",
"YYYY-MM-DDTHH:mm:ssZ",
"en"
),
tags: (() => {
return node.frontmatter?.tags
?.filter((tag) => tag && typeof tag.name === "string" && tag.name.length > 0)
.flatMap((tag) => tag?.name ?? "")
.map((tagName) => tagName);
})(),
hierarchy: {
lvl0: site?.siteMetadata?.title ?? "",
lvl1: node.frontmatter?.title ?? "",
},
thumbnail: node.frontmatter?.thumbnail ?? "",
type: "lvl1",
url: `${site?.siteMetadata?.siteUrl ?? ""}/articles/${node.frontmatter?.id ?? ""}`,
};
}),
},
],
settings: {
searchableAttributes: ["title", "content", "tags"],
indexLanguages: ["ja"],
queryLanguages: ["ja"],
attributesToSnippet: [`content:10`],
},
mergeSettings: true,
chunkSize: 10000,
dryRun: process.env.ALGOLIA_DRY_RUN,
},
},
],
};
こだわり
yamada-uiのユーザーガイドyamada-ui-guideのようなダミーの検索窓をクリックすると検索モーダルが展開するUIにしてみました
またyamada-uiはユーザーガイドもOSSとして公開しているため実装面でも参考にすることができました
ハマったこと(IME対応)
algolia や instantsearch とは直接関係のないことですが
Gatsby.jsadding-search-with-algoliaのサンプルコード通りに
onChangeイベントでalgoliaへのリクエストを行うと全角入力の変換とバッティングしてしまうため
onCompositionStartCompositionEvent発火後は
onChangeイベントでは入力文字列のStateのみ更新し
onCompositionEnd内でalgoliaへのリクエストを行う形で対応しています
import React, { useState } from "react";
import { useSearchBox } from "react-instantsearch";
import { faSearch, faCancel} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@yamada-ui/fontawesome";
import { Input, InputGroup, InputLeftElement, InputRightElement } from "@yamada-ui/input";
import { IconButton } from "@yamada-ui/button";
export interface SearchBoxProps {}
const SearchBox = (props: SearchBoxProps) => {
const { query, refine, clear } = useSearchBox();
const [ enteredValue, setEnteredValue ] = useState(query);
const [ compositionOngoing, setCompositionOngoing ] = useState(false);
return (
<InputGroup>
<InputLeftElement>
<FontAwesomeIcon icon={faSearch} />
</InputLeftElement>
<Input
value={enteredValue}
onChange={(e) => {
setEnteredValue(e.target.value)
if (compositionOngoing) {
return;
}
refine(e.target.value);
}}
onCompositionStart={() => { setCompositionOngoing(true) }}
onCompositionEnd={() => {
setCompositionOngoing(false);
refine(enteredValue);
}}
/>
<InputRightElement clickable>
<IconButton
icon={<FontAwesomeIcon icon={faCancel} />}
onClick={() => {
setEnteredValue("");
clear();
}}
/>
</InputRightElement>
</InputGroup>
);
};
export default SearchBox;
仕事ではAWS CDK、Newman拡張、バリデーション処理ぐらいでしかJS/TSを触ったことがないのですがフロントエンド開発のあるあるネタなのでしょうか...
algoliaへのリクエスト数を絞るのもやぶさかではないのでインクリメンタルサーチ自体今後やめるかも
- https://github.com/algolia/gatsby-plugin-algolia↩
- https://github.com/algolia/instantsearch/tree/master/packages/react-instantsearch↩
- https://qiita.com/atomyah/items/b772a63fc70bf8e7dbdd#algolia%E3%81%AB%E3%82%A4%E3%83%B3%E3%83%87%E3%83%83%E3%82%AF%E3%82%B9↩
- https://yamada-ui.com/ja↩
- https://www.gatsbyjs.com/docs/adding-search-with-algolia/#search-box↩
- https://developer.mozilla.org/ja/docs/Web/API/CompositionEvent↩