サイト内検索比較:Typesense vs Meilisearch vs Elasticsearch で高速検索を実装する
オープンソースラボ編集部 ・ 2026年6月14日
サイト内検索比較:Typesense vs Meilisearch vs Elasticsearch で高速検索を実装する
Webサイト・ECサイト・ドキュメントポータルに高速・高精度なサイト内検索を実装するOSS検索エンジンが充実しています。Typesense(C++製・超高速・簡単セットアップ)・Meilisearch(Rust製・タイポ許容・DX重視)・Elasticsearch(Java製・フルスケール)の3つが2026年のOSS検索エンジンデファクトスタンダードです。
サイト内検索ツールを選ぶ理由
- ユーザー体験: SQLのLIKE検索(遅い・タイポNG)から全文検索エンジン(高速・ファジーマッチ)に移行
- Algolia代替: Algolia($29/月〜)の高コストをセルフホストで$0にして同等の検索UXを実現
- リアルタイム更新: データベースの更新を即座に検索インデックスに反映して最新情報を検索
- 日本語対応: 形態素解析・N-gram分割によって日本語のスペースなし連続文字を正確に検索
主要ツールの概要
Typesense
2019年公開、C++製のOSSです。GitHubスター21k+。Algoliaの代替として設計されたC++製の超高速検索エンジンで、ミリ秒レイテンシ・タイポ許容・ベクター検索・ジオサーチを1つのエンジンで提供します。TypeScript SDK・Instantsearch.js互換でフロントエンド統合が容易です。
# docker-compose.yml: Typesense
version: "3.8"
services:
typesense:
image: typesense/typesense:27.0
restart: unless-stopped
ports:
- "8108:8108"
environment:
TYPESENSE_API_KEY: ${TYPESENSE_API_KEY}
TYPESENSE_DATA_DIR: /data
volumes:
- typesense_data:/data
volumes:
typesense_data:
// Typesense: コレクション作成・インデックス化・検索
import Typesense from 'typesense'
const client = new Typesense.Client({
nodes: [{ host: 'localhost', port: 8108, protocol: 'http' }],
apiKey: process.env.TYPESENSE_API_KEY!,
connectionTimeoutSeconds: 2,
})
// コレクション(インデックス)のスキーマ定義
const schema = {
name: 'articles',
fields: [
{ name: 'id', type: 'string' as const },
{ name: 'title', type: 'string' as const },
{ name: 'content', type: 'string' as const },
{ name: 'category', type: 'string' as const, facet: true },
{ name: 'tags', type: 'string[]' as const, facet: true },
{ name: 'published_at', type: 'int64' as const },
{ name: 'view_count', type: 'int32' as const },
// ベクター検索フィールド(セマンティック検索)
{ name: 'embedding', type: 'float[]' as const, embed: {
from: ['title', 'content'],
model_config: { model_name: 'ts/all-MiniLM-L12-v2' },
}},
],
default_sorting_field: 'view_count',
}
await client.collections().create(schema)
// ドキュメントをバッチでインポート
const documents = articles.map(a => ({
id: a.id,
title: a.title,
content: a.content.substring(0, 2000),
category: a.category,
tags: a.tags,
published_at: Math.floor(new Date(a.publishedAt).getTime() / 1000),
view_count: a.viewCount,
}))
await client.collections('articles').documents().import(documents, { action: 'upsert' })
// 検索クエリ(ファセット・タイポ許容・ハイライト)
const searchResult = await client.collections('articles').documents().search({
q: 'ドッカー コンテナ', // タイポ・ひらがな混在OK
query_by: 'title,content,tags',
query_by_weights: '3,1,2', // タイトルを3倍重視
facet_by: 'category,tags',
filter_by: 'published_at:>1700000000',
sort_by: '_text_match:desc,view_count:desc',
highlight_full_fields: 'title',
per_page: 20,
page: 1,
num_typos: '2', // 2文字のタイポを許容
})
console.log(searchResult.found) // 総件数
console.log(searchResult.hits) // 検索結果
console.log(searchResult.facet_counts) // ファセット(カテゴリ別件数)
// Next.js: Typesenseと同期するSupabase→Typesenseパイプライン
// supabase/functions/sync-typesense/index.ts (Edge Function)
import Typesense from 'https://esm.sh/typesense@1.7.2'
Deno.serve(async (req) => {
const payload = await req.json()
const { type, record, old_record } = payload
const client = new Typesense.Client({
nodes: [{ host: Deno.env.get('TYPESENSE_HOST')!, port: 443, protocol: 'https' }],
apiKey: Deno.env.get('TYPESENSE_API_KEY')!,
})
if (type === 'INSERT' || type === 'UPDATE') {
await client.collections('articles').documents().upsert({
id: record.id,
title: record.title,
content: record.content_md?.substring(0, 2000) || '',
category: record.category,
tags: record.tags || [],
published_at: Math.floor(new Date(record.created_at).getTime() / 1000),
view_count: record.view_count || 0,
})
} else if (type === 'DELETE') {
await client.collections('articles').documents(old_record.id).delete()
}
return new Response('ok')
})
Meilisearch
2019年公開、Rust製のOSSです。GitHubスター48k+。Rust製で5分でセットアップできる開発者フレンドリーな検索エンジンです。ゼロコンフィグでタイポ許容・ハイライト・日本語対応(Chapaize分割器)が動作し、InstantSearch.js(Algolia互換)ライブラリで検索UIを素早く実装できます。
# Meilisearch Docker セットアップ
docker run -d --name meilisearch -p 7700:7700 -e MEILI_MASTER_KEY=your-master-key -v $(pwd)/meili_data:/meili_data getmeili/meilisearch:v1.11
// Meilisearch TypeScript SDKでのインデックス設定
import { MeiliSearch } from 'meilisearch'
const client = new MeiliSearch({ host: 'http://localhost:7700', apiKey: 'your-master-key' })
// インデックス設定(日本語対応)
await client.index('articles').updateSettings({
searchableAttributes: ['title', 'content', 'tags'],
filterableAttributes: ['category', 'tags', 'published_at'],
sortableAttributes: ['view_count', 'published_at'],
rankingRules: ['words', 'typo', 'proximity', 'attribute', 'sort', 'exactness'],
typoTolerance: { enabled: true, minWordSizeForTypos: { oneTypo: 4, twoTypos: 8 } },
pagination: { maxTotalHits: 10000 },
})
// ドキュメント追加
await client.index('articles').addDocuments(documents)
// 検索
const results = await client.index('articles').search('機械学習', {
filter: 'category = "llm-tools"',
facets: ['category', 'tags'],
limit: 20,
attributesToHighlight: ['title', 'content'],
})
Elasticsearch
2010年公開、Java製のOSSです。GitHubスター70k+。エンタープライズ規模の全文検索・ログ分析・APMのデファクトスタンダードです。KibanaでのUI・Logstash/Fluentdでのデータパイプライン(ELKスタック)が充実しています。
機能比較表
| 比較項目 | Typesense | Meilisearch | Elasticsearch |
|---|---|---|---|
| セットアップ容易度 | ✅ | ✅ | △ |
| 日本語対応 | ✅ | ✅ | ✅(プラグイン) |
| ベクター検索 | ✅ | ✅ | ✅ |
| スケーラビリティ | 大 | 中 | ✅ 最大 |
| メモリ効率 | ✅ | ✅ | △ |
| GitHub Stars | 21k+ | 48k+ | 70k+ |
サイト内検索はDevOpsカテゴリ/categories/devopsのSupabaseやPostgreSQLと同期パイプラインを構築してデータ更新を即座にインデックスに反映します。LLM Toolsカテゴリ/categories/llm-toolsのベクターデータベース(Qdrant・pgvector)と組み合わせてキーワード検索とセマンティック検索を融合したハイブリッド検索UIを実装します。
FAQ
Q. TypesenseをNext.jsのApp Routerで使うには?
A. Server ComponentでTypesenseクライアントを直接呼び出すか、Route HandlerでAPIを作成します。Server Component実装: const searchResult = await client.collections('articles').documents().search({q: searchParams.q || '*', query_by: 'title,content'})→検索結果をJSXに渡してSSRレンダリング。検索UIはReact Server ComponentとClient Componentを分離: ①<SearchBox />('use client'・入力・URLパラメーター更新)②<SearchResults searchParams={params} />(Server Component・Typesenseを呼び出す)。Supabase Edge Function経由でデータ同期: Supabaseのウェブフックを設定してarticlesテーブルの変更をTypesenseに同期するEdge Functionを実行します。
Q. 日本語テキストの検索精度を上げるには?
A. N-gram分割またはMeCab形態素解析が必要です。Typesense: デフォルトのトークナイザーがN-gramをサポートし日本語が動作します(tokenizer: 'ja'設定は不要)。Meilisearch: v1.3+でCharapaize(Lindera)MeCabベースの日本語トークナイザーが統合済みでlocales: ['jpn']設定で有効化。Elasticsearch: analysis-kuromojiプラグイン(sudo ./bin/elasticsearch-plugin install analysis-kuromoji)でMeCabベースの日本語形態素解析を有効化。設定例: "analyzer": {"japanese": {"type": "kuromoji"}}. 検索精度向上: 読み仮名フィールド(読み: 'きかいがくしゅう')を追加して平仮名・カタカナの相互変換検索を可能にします。
Q. 検索UIをAlgoliaのInstantSearch.jsで構築するには?
A. TypesenseはInstantSearch.jsの公式アダプターを提供しており、Algoliaからの移行が容易です。①npm install typesense-instantsearch-adapter instantsearch.js②new TypesenseInstantSearchAdapter({server: {apiKey, nodes}, additionalSearchParameters: {query_by: 'title,content'}})③Algolia InstantSearchと同じ<SearchBox>・<Hits>・<RefinementList>コンポーネントをそのまま使用。React版: react-instantsearchパッケージでReact向けコンポーネントを使用。カスタマイズ: Typesense固有のファセット・ソート設定をadditionalSearchParametersで追加して既存のInstantSearch UIを拡張できます。
Q. Meilisearchで検索インデックスをバッチ更新するには?
A. addDocuments()はデフォルトで非同期タスクとして処理されます。バッチ更新: client.index('articles').addDocuments(docs, {primaryKey: 'id'})で最大10万件をバッチ送信→返却されたtaskUidでclient.tasks.getTask(taskUid)をポーリングしてインデックス完了を確認。大規模: 1万件ずつ分割して順番にaddDocuments()を呼び出す(Meilisearchは内部でキューイング)。全削除→再インデックス: client.index('articles').deleteAllDocuments()→バッチ追加(インデックスの再構築が必要な場合)。設定変更(updateSettings)もタスクキューに追加されるため、設定変更完了を待ってからデータ投入する場合はtaskUidでポーリングします。
まとめ
| ユースケース | 推奨ツール |
|---|---|
| 高速・Algolia代替・ベクター検索統合 | Typesense |
| DX重視・5分セットアップ・Rust軽量 | Meilisearch |
| ログ分析・ELKスタック・エンタープライズ | Elasticsearch |