ホーム

Next.jsで実装するFuse.jsを利用した検索機能

作成日: 2023-4-25

更新日: 2023-9-28

Next.jsで実装するFuse.jsを利用した検索機能

この記事を読んだ後に理解できること


  • JavaScriptの全文検索ライブラリ「Fuse.js」の概要と基本的な使い方
  • Next.jsで検索機能を実装する方法


Fuse.jsとは?

概要


Fuse.jsは、軽量で高速なJavaScriptライブラリであり、クライアントサイドでのあいまい検索(ファジー検索)を実現します。

あいまい検索とは、検索クエリが完全に一致しなくても、類似した結果を返すことができる検索方法です。

Fuse.jsは、スペルミスや部分的な一致に寛容であることが特徴であり、検索の精度や柔軟性を向上させます。

このライブラリは、JSONデータの配列を対象とした検索を行い、検索クエリとデータの類似度に基づいて結果を返します。

現状(2023/4/14)のGitHubのスター数は15,700であり、その他のライブラリと比べても非常に人気があります。

基本的な使い方


まずはFuse.jsに対して、検索データを引数として渡して、インスタンス化します。

インスタンス化する際に引数は2個指定することができます。

第1引数は、検索をする対象データを指定できます。

第2引数は、検索時の詳細設定を指定できます。

例えば、keyオプションに検索時に使いたいデータのキーを指定することができます。

指定する場合は、配列内に文字列で指定します。

カンマ区切りすれば複数指定することができます。

new Fuse(searchTargetList, {
  key: ["title", "content"],
 })


他にもオプションが複数ありますが、代表的なものを3個紹介します。

  • threshold
    • 0から1の間の数字を指定します。
    • 数字が大きなれば、あいまいな検索が可能になります。
    • 例えば、0.3くらいを指定すると厳格な一致が必要になりますが、0.8くらいを指定するとあいまいな検索を実施することができます。デフォルトは0.6です。
    • 検索スコアと呼ばれるものです。
  • location
    • 検索対象のどの位置に対して、検索を行うか指定できます。
    • defaultは0です。この場合先頭付近を指定しています。
    • 例えば、300文字のブログ記事の中盤付近で検索クエリに合致する文字列等が含まれているかを検索対象としたい場合は、locationに対して150を指定する必要があります。
  • distance
    • locationで指定された位置からどれだけ離れた位置まで検索を行うかを指定します。
    • defaultは100です。
    • 300文字で構成されるブログ記事の全てを検索対象としたい場合は、distanceを300にする必要があります。
    • ただし、distanceを大きく設定すると検索スコアが高くなり、不正確な情報として取り扱われることがあります。


Next.jsでFuse.jsを使って検索機能を実装する


今回実装する検索機能について


今回実装するNext.jsでの検索機能の実装はブログ記事の検索機能を想定しています。

まずSSG等でAPIから取得したブログ記事のデータを使ってFuse.jsのインスタンス化を行います。

後はインスタンスにキーワードを渡せば、該当するデータを戻り値として返してくれます。

Fuse.jsをインストールする


$ npm install --save fuse.js

データをインデックスする


まずはデータとオプションを指定して、Fuse.jsをインスタンス化します。

今回は、SSGで取得したデータを使います。

import { GetStaticProps, NextPage } from 'next';
import Fuse from 'fuse.js';

type blogResponse = {
  contents: Array<{
    title: string,
    content: string
  }>
}

type SSGResponse = {
  response: blogResponse
}

export const getStaticProps: GetStaticProps<SSGResponse> = async (context) => {
  const response: blogResponse = await fetcher('一覧取得のAPIエンドポイントを指定')
  return {
    props: {
      response
    }
  }
}

const Home: NextPage<SSGResponse> = (props) => {
  const { response } = props;
  
  // useMemoでFuseインスタンスをキャッシュする
  const fuseInstance = useMemo(() => {
    const fuse = new Fuse(response.contents, {
      keys: ["title", "content"],
      threshold: 0.3,
    })
    return fuse
  }, [response])
  return (
    <>
    </>
  )
}



SSGはビルド時にしかデータ取得を行わないので、useMemoでキャッシュしておけば、クライアントサイドではインスタンス化の関数を実行する必要がなくなります。

注意点としては、記事の更新等によりデータが更新された場合は対応ができません。

Ondemand ISRやISRを設定することで上記に対応することができます。


検索キーワードから検索結果を取得する


input要素に入力されたキーワードを取得して検索結果を取得します。

import { GetStaticProps, NextPage } from 'next';
import { useCallback, useMemo } from 'react';
import Fuse from 'fuse.js';

type blogResponse = {
  contents: Array<{
    title: string,
    content: string
  }>
}

type SSGResponse = {
  response: blogResponse
}

export const getStaticProps: GetStaticProps<SSGResponse> = async (context) => {
  const response: blogResponse = await fetcher('一覧取得のAPIエンドポイントを指定')
  return {
    props: {
      response
    }
  }
}

const Home: NextPage<SSGResponse> = (props) => {
  const { response } = props;
  
  // useMemoでFuseインスタンスをキャッシュする
  const fuseInstance = useMemo(() => {
    const fuse = new Fuse(response.contents, {
      keys: ["title", "content"],
      threshold: 0.3,
    })
    return fuse
  }, [response])

  const inputValue = React.useRef<HTMLInputElement>(null);

  const handleSubmit = useCallback((e: React.KeyboardEvent<HTMLInputElement>) => {
    if (e.key === 'Enter') {
      // ここで検索処理を行う
      const value = inputValue.current? inputValue.current.value : '';
      const results = fuseInstance.search(value)
      console.log(results)
      if (inputValue.current) {
        inputValue.current.value = '';
      }
    }
  }, [fuseInstance])

  return (
    <>
      <input
        type='text'
        ref={inputValue}
        onKeyDown={handleSubmit}
        placeholder='検索キーワード' 
      />
    </>
  )
}


console.log上に検索結果が表示されると思います。

さいごに


Next.jsで検索機能を実現する場合、色々な方法があります。

今回はJavaScriptのライブラリであるFuse.jsで実装しました。

Next.jsはデータの取得方法がSSG以外にもSSRがあったりしますので、そちらのデータを使うこともできます。

SSRの概要については以下の記事を参考にしてください。