はじめに
ドキュメントサイトやブログ、社内ツールに「検索機能」を付けたいとき、皆さんはどうしていますか?Algoliaのような有償SaaSを契約する、Elasticsearchをインフラごと構築する……どちらも本格的で強力ですが、記事数百件程度の検索窓のためにそこまでの投資をするのは大げさに感じることも多いはずです。
そんなときに便利なのが、ブラウザとNode.jsの両方で動く軽量な全文検索エンジン「MiniSearch」です。サーバーもインフラも要らず、npmでインストールするだけで、ファジー検索やオートサジェストまで備えた本格的な検索機能をその場で実装できます。
MiniSearchとは
MiniSearchは、Luca Ongaro氏が開発しているJavaScript製の全文検索エンジンです。転置インデックス(inverted index)をメモリ上に構築する方式を採用しており、外部サーバーへの通信なしに高速な検索を実現します。TypeScriptで書かれているため型定義も充実しており、npmでの週間ダウンロード数も多い実績のあるライブラリです。
主な特徴
- 軽量かつ高速 - 依存パッケージなしの小さなバンドルサイズで、数千〜数万件規模のドキュメントでも快適に動作します
- ファジー検索・プレフィックス検索 - タイプミスを許容した検索や、入力途中でも候補を返すオートコンプリートに標準対応しています
- フィールド単位の重み付け - タイトルと本文で検索の重要度を変えるなど、検索結果のランキングを細かくチューニングできます
- JSONでのシリアライズ - 構築したインデックスをJSONとして保存・復元できるため、ビルド時にインデックスを事前生成しておく使い方も可能です
インストール
npm、yarn、pnpmのいずれでも導入できます。
npm install minisearch
yarn add minisearch
pnpm add minisearch
基本的な使い方
まずは、簡単なドキュメント配列からインデックスを構築し、検索してみましょう。
import MiniSearch from 'minisearch'
// 検索対象のドキュメント
const documents = [
{ id: 1, title: 'Reactの基礎', text: 'コンポーネント指向のUIライブラリについて解説します' },
{ id: 2, title: 'Vue.js入門', text: 'リアクティブなフレームワークの使い方を学びます' },
{ id: 3, title: 'TypeScript実践', text: '型安全なJavaScript開発の手法を紹介します' }
]
// インデックスを作成
const miniSearch = new MiniSearch({
fields: ['title', 'text'], // 検索対象のフィールド
storeFields: ['title'] // 検索結果に含めたいフィールド
})
// ドキュメントを追加
miniSearch.addAll(documents)
// 検索を実行
const results = miniSearch.search('React')
console.log(results)
// [{ id: 1, score: ..., title: 'Reactの基礎', ... }]
fieldsで全文検索の対象とするフィールドを指定し、storeFieldsで検索結果として取り出したいフィールドを指定するだけで、すぐに検索が使えるようになります。
ファジー検索とオートサジェスト
タイプミスに強い検索や、入力途中の文字列から候補を提示するオートサジェストも簡単に実装できます。
// タイプミスを許容するファジー検索
const fuzzyResults = miniSearch.search('Raect', {
fuzzy: 0.2 // 許容する誤差の度合い(0〜1)
})
// 入力途中の文字列から候補を提示するオートサジェスト
const suggestions = miniSearch.autoSuggest('reac', {
fuzzy: 0.2
})
console.log(suggestions)
// [{ suggestion: 'react', score: ..., terms: ['react'] }]
fuzzyオプションに数値を渡すだけで、レーベンシュタイン距離に基づいた曖昧検索が有効になります。検索フォームの体験を大きく向上させられる機能です。
実践的なユースケース
ブログ記事のクライアントサイド検索を実装する例を見てみましょう。ビルド時に生成した記事一覧をもとにインデックスを作り、入力に応じて結果を絞り込みます。
import MiniSearch from 'minisearch'
// ビルド時に用意した記事メタデータ
const posts = [
{ id: 'minisearch', title: 'MiniSearchで作る検索機能', tags: 'JavaScript 検索' },
{ id: 'zustand', title: 'Zustandで状態管理', tags: 'React 状態管理' },
{ id: 'xstate', title: 'XStateで状態遷移を設計する', tags: '状態管理 設計' }
]
const miniSearch = new MiniSearch({
fields: ['title', 'tags'],
storeFields: ['title'],
searchOptions: {
boost: { title: 2 }, // タイトル一致を優先
fuzzy: 0.2
}
})
miniSearch.addAll(posts)
// 検索ボックスの入力イベントに応じて呼び出す想定の関数
function handleSearchInput(query) {
if (query.length === 0) return []
return miniSearch.search(query)
}
console.log(handleSearchInput('検索'))
boostオプションを使うと、タイトルに一致した記事を本文一致よりも上位に表示するといった調整が可能です。この仕組みを使えば、外部の検索サービスに依存せず、静的サイトだけで完結する検索機能を構築できます。
インデックスをJSON.stringify(miniSearch)でシリアライズしてビルド時に生成しておき、クライアント側ではMiniSearch.loadJSON()で復元するようにすれば、実行時のインデックス構築コストも省略できます。
// ビルド時: インデックスをJSONとして保存
const serialized = JSON.stringify(miniSearch)
// クライアント側: 保存済みJSONからインデックスを復元
const restored = MiniSearch.loadJSON(serialized, {
fields: ['title', 'tags']
})
まとめ
MiniSearchは、外部インフラに頼らずブラウザやNode.jsだけで完結する全文検索エンジンとして、シンプルながらファジー検索やオートサジェスト、フィールド重み付けといった実用的な機能を備えています。ドキュメントサイトやブログ、社内ツールなど、検索対象の規模がそこまで大きくない場面では、まずMiniSearchを試してみる価値が十分にあります。導入の手軽さと機能のバランスを、ぜひ実際のプロジェクトで確かめてみてください。
