AI

全文検索エンジン比較:OpenSearch vs Zinc vs Manticore Search で全文検索を構築する

オープンソースラボ編集部2026年6月13日

全文検索エンジン比較:OpenSearch vs Zinc vs Manticore Search で全文検索を構築する

Elasticsearchのライセンス問題(2021年のSSPL変更)を受けて、セルフホストでスケーラブルな全文検索エンジンの選択肢が多様化しています。OpenSearch(AWS主導のElasticsearch完全互換フォーク)・Zinc(Go製・超軽量・Elasticsearch API互換)・Manticore Search(MySQL互換・C++製高速検索)の3つが2026年のOSS全文検索エンジンの主要選択肢です。

OSSで全文検索を構築する理由

  • Elasticsearch代替: Elasticsearchのライセンス変更(SSPL)を避けてOSSで全文検索を継続
  • コスト削減: Elastic Cloud($95/月〜)→VPS $20/月でフルスケール検索基盤
  • データ所有権: 検索インデックスと生データを自社サーバーに保持
  • カスタマイズ: 形態素解析・ファジー検索・ベクトル検索を自社システムに最適化

主要ツールの概要

OpenSearch

2021年公開(AWS)、Java/Go製のOSSです。GitHubスター10k+。ElasticsearchとKibana(OpenSearch Dashboards)の完全互換フォークで、Elasticsearch APIをそのまま使えます。全文検索・アグリゲーション・ダッシュボード・セキュリティプラグイン・機械学習(k-NNベクトル検索)が統合されています。

# docker-compose.yml: OpenSearch + Dashboards
version: "3.8"
services:
  opensearch-node1:
    image: opensearchproject/opensearch:latest
    restart: unless-stopped
    environment:
      - cluster.name=opensearch-cluster
      - node.name=opensearch-node1
      - discovery.seed_hosts=opensearch-node1
      - cluster.initial_cluster_manager_nodes=opensearch-node1
      - bootstrap.memory_lock=true
      - OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m
      - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_ADMIN_PASS}
      - plugins.security.disabled=false
    ulimits:
      memlock: { soft: -1, hard: -1 }
      nofile: { soft: 65536, hard: 65536 }
    volumes:
      - opensearch-data:/usr/share/opensearch/data
    ports:
      - "9200:9200"

  opensearch-dashboards:
    image: opensearchproject/opensearch-dashboards:latest
    restart: unless-stopped
    ports:
      - "5601:5601"
    environment:
      OPENSEARCH_HOSTS: '["https://opensearch-node1:9200"]'

volumes:
  opensearch-data:
# Python: OpenSearch SDK で日本語全文検索・ベクトル検索を実装
from opensearchpy import OpenSearch
from typing import Optional

client = OpenSearch(
    hosts=[{'host': 'localhost', 'port': 9200}],
    http_auth=('admin', 'your-password'),
    use_ssl=True,
    verify_certs=False,
)

def create_japanese_index(index_name: str) -> dict:
    '''日本語形態素解析(kuromoji)対応インデックスを作成'''
    body = {
        'settings': {
            'analysis': {
                'analyzer': {
                    'japanese_analyzer': {
                        'type': 'custom',
                        'tokenizer': 'kuromoji_tokenizer',
                        'filter': ['kuromoji_baseform', 'lowercase'],
                    }
                }
            }
        },
        'mappings': {
            'properties': {
                'title': {'type': 'text', 'analyzer': 'japanese_analyzer'},
                'content': {'type': 'text', 'analyzer': 'japanese_analyzer'},
                'tags': {'type': 'keyword'},
                'published_at': {'type': 'date'},
                'embedding': {
                    'type': 'knn_vector',  # ベクトル検索用(k-NNプラグイン)
                    'dimension': 1536,
                    'method': {'name': 'hnsw', 'space_type': 'cosine'},
                },
            }
        },
    }
    return client.indices.create(index=index_name, body=body)

def search_articles(query: str, index: str = 'articles',
                    filters: Optional[dict] = None) -> list:
    '''日本語全文検索(BM25)+フィルタ+ハイライト'''
    must = [{'multi_match': {
        'query': query,
        'fields': ['title^3', 'content'],
        'analyzer': 'japanese_analyzer',
    }}]
    if filters:
        for key, value in filters.items():
            must.append({'term': {key: value}})

    res = client.search(index=index, body={
        'query': {'bool': {'must': must}},
        'highlight': {
            'fields': {'content': {'number_of_fragments': 2, 'fragment_size': 150}}
        },
        'size': 20,
    })
    return [{'id': hit['_id'], 'score': hit['_score'],
             'title': hit['_source']['title'],
             'highlight': hit.get('highlight', {}).get('content', [])}
            for hit in res['hits']['hits']]

Zinc(ZincSearch)

2021年公開、Go製のOSSです。GitHubスター17k+。Go製でメモリ使用量がElasticsearchの1/10以下の超軽量全文検索エンジンで、Elasticsearch API互換なので既存の検索クライアントをそのまま流用できます。シングルバイナリで動作し、設定なしで即起動できます。

# docker-compose.yml: ZincSearch(超軽量・シングルバイナリ)
version: "3.8"
services:
  zinc:
    image: public.ecr.aws/zinclabs/zincsearch:latest
    restart: unless-stopped
    ports:
      - "4080:4080"
    environment:
      ZINC_DATA_PATH: /data
      ZINC_FIRST_ADMIN_USER: admin
      ZINC_FIRST_ADMIN_PASSWORD: ${ZINC_PASS}
    volumes:
      - zinc-data:/data

volumes:
  zinc-data:
# Python: ZincSearch REST API(Elasticsearch互換)でドキュメントを検索
import requests

ZINC_URL = 'http://localhost:4080'
AUTH = ('admin', 'your-password')

def bulk_index_articles(articles: list, index: str = 'articles') -> dict:
    '''ZincSearchに記事を一括インデックス登録(Elasticsearch Bulk API互換)'''
    body = ''
    for article in articles:
        # Elasticsearch Bulk API形式
        body += '{"index":{}}
'
        body += f'{{"title":"{article["title"]}","content":"{article["content"]}","tags":{article["tags"]}}}
'
    resp = requests.post(f'{ZINC_URL}/api/{index}/_bulk', data=body, auth=AUTH,
                         headers={'Content-Type': 'application/x-ndjson'})
    return resp.json()

def search(query: str, index: str = 'articles', size: int = 20) -> list:
    '''ZincSearchで全文検索(Elasticsearch Query DSL互換)'''
    payload = {
        'query': {'match': {'_all': query}},
        'size': size,
        'from': 0,
    }
    resp = requests.post(f'{ZINC_URL}/api/{index}/_search', json=payload, auth=AUTH)
    hits = resp.json().get('hits', {}).get('hits', [])
    return [{'id': h['_id'], 'score': h['_score'], **h['_source']} for h in hits]

Manticore Search

2017年公開(Manticore Software)、C++製のOSSです。GitHubスター9k+。MySQL互換のSQL構文で全文検索を実行できるC++製高速検索エンジンで、Sphinxエンジンベースの分散全文検索・リアルタイムインデックス・列指向ストレージをMySQL互換のSQLで操作できます。

# docker-compose.yml: Manticore Search
version: "3.8"
services:
  manticore:
    image: manticoresearch/manticore:latest
    restart: unless-stopped
    ports:
      - "9306:9306"  # MySQL互換ポート
      - "9308:9308"  # HTTP API
    environment:
      EXTRA: 1
    volumes:
      - manticore-data:/var/lib/manticore
    ulimits:
      nproc: 65535
      nofile: { soft: 65535, hard: 65535 }

volumes:
  manticore-data:
# Python: Manticore Search をMySQL互換SQLで操作
import mysql.connector
import requests

def connect_manticore():
    '''ManticoreのMySQL互換インターフェースに接続'''
    return mysql.connector.connect(
        host='localhost', port=9306, user='', password='', database=''
    )

def create_article_table(conn) -> None:
    '''Manticoreに記事検索テーブル(リアルタイムインデックス)を作成'''
    with conn.cursor() as cur:
        cur.execute('''
            CREATE TABLE IF NOT EXISTS articles (
                id          bigint,
                title       text,
                content     text,
                tags        multi,
                published   timestamp
            ) ENGINE='columnar' min_infix_len='3'
        ''')

def search_fulltext(query: str, limit: int = 20) -> list:
    '''Manticore全文検索(MySQL互換SQL)'''
    conn = connect_manticore()
    with conn.cursor(dictionary=True) as cur:
        cur.execute(
            "SELECT id, title, WEIGHT() AS relevance FROM articles "
            "WHERE MATCH(%s) ORDER BY relevance DESC LIMIT %s",
            (query, limit)
        )
        return cur.fetchall()

def search_via_http(query: str) -> dict:
    '''Manticore HTTP JSON API経由で検索(Elasticsearch互換)'''
    payload = {'query': {'match': {'title': query}}}
    resp = requests.post('http://localhost:9308/search', json=payload)
    return resp.json()

機能比較表

比較項目OpenSearchZincManticore
Elasticsearch互換✅ 完全✅ 部分
MySQL互換SQL
メモリ使用量高(JVM)✅ 最低
ベクトル検索✅ k-NN
GitHub Stars10k+17k+9k+

全文検索エンジンはDevOpsカテゴリ/categories/devopsのログ集約(Loki/Fluentd)と組み合わせてアプリケーションログのリアルタイム検索基盤(ELKスタック代替)を構築します。LLMツールカテゴリ/categories/llm-toolsのベクトルストア(pgvector/Chroma)とOpenSearchのk-NNを統合してハイブリッド検索(BM25+ベクトル類似度)によるRAGパイプラインを実現します。

FAQ

Q. OpenSearchに日本語形態素解析を追加してkuromojiで日本語全文検索するには?

A. OpenSearchのanalysis-kuromojiプラグインを有効化してjapanese_analyzerを定義します。インストール: OpenSearchコンテナ起動後にdocker exec opensearch-node1 bin/opensearch-plugin install analysis-kuromojiを実行→コンテナを再起動。インデックス作成: PUT /articlessettings.analysis.analyzer.japanese_analyzerを定義→tokenizer: kuromoji_tokenizerfilter: [kuromoji_baseform, kuromoji_part_of_speech, lowercase]を設定。マッピング: titlecontentフィールドに"analyzer": "japanese_analyzer"を指定。確認: GET /articles/_analyze?analyzer=japanese_analyzer&text=東京オリンピック東京オリンピックに分割されるか検証。kuromojiの品詞フィルタ: kuromoji_part_of_speechフィルタにstoptags: [助詞-*, 助動詞, 記号-*]を設定して助詞・助動詞を除去→検索精度向上。

Q. ElasticsearchからOpenSearchに移行するにはどうすればいいですか?

A. OpenSearchはElasticsearch 7.10互換なので、多くの場合は接続先URLを変更するだけで移行可能です。移行手順: ①既存ElasticsearchのスナップショットをS3に取得: PUT _snapshot/my_s3_repo/snapshot_1②OpenSearchを起動して同じS3リポジトリを登録: PUT _snapshot/my_s3_repo③スナップショットをリストア: POST _snapshot/my_s3_repo/snapshot_1/_restore④アプリの接続URLをElasticsearch→OpenSearchに変更(認証方式の更新が必要な場合あり)。非互換点: ①Elasticsearch 8.x固有のAPI(新しいAPIは使用不可)②セキュリティプラグインの設定(OpenSearchはデフォルトでSSL/TLS有効)③Kibanaダッシュボード→OpenSearch Dashboardsに変換(多くの場合自動)。

Q. ZincSearchはどのようなユースケースに適していますか?

A. 小〜中規模の全文検索(数百万件以下)でElasticsearch互換APIが必要だがメモリを節約したい場合に最適です。適したユースケース: ①ログ検索(アプリケーションログ10GB以下)②EC・ブログ・ドキュメントの全文検索③ElasticsearchのAPIを使う既存アプリをメモリ節約のために移行。不向きなケース: ①数十億件以上の大規模検索→OpenSearch/Elasticsearch推奨②ベクトル検索・k-NN→OpenSearch/Manticore③複雑なアグリゲーション・レポート→OpenSearch。Zincのリソース: RAM 512MB〜1GBでElasticsearchが要求する16GB〜32GBの代わりに動作→Raspberry PiやVPS最小プランでも動作。

Q. ManticoreのMySQL互換SQLとHTTP APIはどちらを使うべきですか?

A. 既存のMySQLクライアント・ORMを使う場合はMySQL互換SQLElasticsearchクライアントをそのまま流用したい場合はHTTP JSON APIを使います。MySQL互換SQL優位: ①mysql -h 127.0.0.1 -P 9306でManticoreに接続→MySQLと同じクライアントツール②Django・SQLAlchemy等のORM(mysql-connector使用)がそのまま動作③SELECT id, WEIGHT() FROM articles WHERE MATCH('検索語')の直感的なSQL構文④JOIN・集計関数・GROUP BY対応→検索結果と他データの結合が容易。HTTP JSON API優位: ①POST /searchでElasticsearch互換クエリDSL→Elasticsearch SDK流用②RESTクライアントから直接叩ける③Grafana等のElasticSourceプラグインと直接統合。

まとめ

ユースケース推奨ツール
Elasticsearch移行・k-NN・大規模OpenSearch
軽量・メモリ節約・中小規模Zinc
MySQL互換SQL・C++高速・SphinxユーザーManticore

関連外部リソース

他の記事も読む

Let's Build Together

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

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