AI

サイト内検索比較: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スタック)が充実しています。

機能比較表

比較項目TypesenseMeilisearchElasticsearch
セットアップ容易度
日本語対応✅(プラグイン)
ベクター検索
スケーラビリティ✅ 最大
メモリ効率
GitHub Stars21k+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.jsnew 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

関連外部リソース

他の記事も読む

Let's Build Together

OSS導入、自社だけで悩まない。

ツール選定から構築・運用・AI活用まで、オープンソースラボ運営元のClasslessが伴走します。初回のご相談は無料です。