データベース

Apache Solr

概要

Apache Solrは、Apache Luceneをベースとした高性能なオープンソース検索プラットフォームです。エンタープライズ級の全文検索、ファセット検索、分散検索機能を提供し、大規模なWebサイトや企業アプリケーションで広く利用されています。

詳細

主要特徴

  • Luceneベース: Apache Luceneの強力な検索機能を基盤とした高性能検索エンジン
  • SolrCloud: Zookeeperを利用した分散アーキテクチャによる水平スケーリング
  • 豊富な検索機能: 全文検索、ファセット検索、近接検索、ファジー検索、範囲検索
  • マルチフォーマット対応: JSON、XML、CSV、PDF、Wordなど多様なドキュメント形式をサポート
  • リアルタイムインデックス: 準リアルタイムでのドキュメント更新とインデックス作成
  • 機械学習統合: Learning to Rank(LTR)、分類、クラスタリング機能
  • ベクトル検索: 密なベクトルフィールドによるセマンティック検索

アーキテクチャ

Solrは以下の主要コンポーネントで構成されています:

  • Solrコア: 検索インデックスと設定を管理する基本単位
  • スキーマ: フィールド定義、分析器、インデックス設定を含む構造定義
  • SolrCloud: Apache Zookeeperによるクラスター管理とノード間調整
  • リクエストハンドラー: 検索、更新、管理API呼び出しの処理
  • 分析器チェーン: テキスト処理のためのトークナイザーとフィルターの組み合わせ

メリット・デメリット

メリット

  • 豊富な全文検索機能: ハイライト、ファセット、スペルチェック、オートコンプリート機能
  • 優れた拡張性: SolrCloudによる水平スケーリングと高可用性
  • エンタープライズ対応: セキュリティ、監視、管理機能が充実
  • 真のオープンソース: Apache License 2.0による完全にオープンなライセンス
  • 成熟したエコシステム: 豊富なプラグイン、ツール、コミュニティサポート
  • スキーマレス機能: 動的フィールド定義による柔軟なデータ構造対応
  • 多言語対応: 50以上の言語に対応した分析器とステマー

デメリット

  • 設定の複雑さ: 初期設定と最適化には深い知識が必要
  • メモリ消費: 大規模データセットでは高いメモリ使用量
  • 学習コスト: XMLベースの設定ファイルとLucene知識が必要
  • リアルタイム性: 完全なリアルタイム更新には制限
  • JSONクエリサポート: JSONベースのクエリAPIは後発機能
  • 開発者体験: ElasticsearchのREST APIと比較して複雑

主要リンク

書き方の例

インストール・セットアップ

# Solrの起動(techproductsサンプル)
bin/solr start -e techproducts

# 新しいコアの作成
bin/solr create -c mycore

# SolrCloudモードでの起動
bin/solr start -c -p 8983

基本操作(インデックス作成・検索)

# ドキュメントの追加(JSON)
curl -X POST "http://localhost:8983/solr/techproducts/update?commit=true" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "id": "doc1",
      "title": "Apache Solr検索エンジン",
      "content": "高性能な全文検索機能を提供"
    }
  ]'

# 基本検索
curl "http://localhost:8983/solr/techproducts/select?q=*:*"

# 特定フィールドでの検索
curl "http://localhost:8983/solr/techproducts/select?q=title:Apache"

スキーマ設計

<!-- schema.xml - フィールド定義 -->
<schema name="example" version="1.5">
  <field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false"/>
  <field name="title" type="text_general" indexed="true" stored="true"/>
  <field name="content" type="text_general" indexed="true" stored="true"/>
  <field name="category" type="string" indexed="true" stored="true" multiValued="true"/>
  
  <!-- 動的フィールド(スキーマレス) -->
  <dynamicField name="*_s" type="string" indexed="true" stored="true"/>
  <dynamicField name="*_i" type="int" indexed="true" stored="true"/>
  
  <uniqueKey>id</uniqueKey>
</schema>

ファセット検索

# カテゴリ別ファセット検索
curl "http://localhost:8983/solr/techproducts/select?q=*:*&facet=true&facet.field=category"

# 範囲ファセット
curl "http://localhost:8983/solr/techproducts/select?q=*:*&facet=true&facet.range=price&facet.range.start=0&facet.range.end=1000&facet.range.gap=100"

# 複数条件ファセット
curl "http://localhost:8983/solr/techproducts/select?q=*:*&facet=true&facet.field=category&facet.field=brand&fq=category:electronics"

実用例

// JavaScriptでのSolr検索(Solrクライアント使用)
const solr = require('solr-client');
const client = solr.createClient();

// 検索実行
client.search('category:books AND author:村上春樹', {
  facet: true,
  'facet.field': ['genre', 'publisher'],
  start: 0,
  rows: 10,
  sort: 'score desc'
}, function(err, obj) {
  if (err) {
    console.log(err);
  } else {
    console.log(obj.response.docs);
    console.log(obj.facet_counts);
  }
});

ベストプラクティス

<!-- solrconfig.xml - パフォーマンス最適化 -->
<config>
  <!-- キャッシュ設定 -->
  <query>
    <filterCache class="solr.search.CaffeineCache"
                 size="512"
                 initialSize="512"
                 autowarmCount="0"/>
    
    <queryResultCache class="solr.search.CaffeineCache"
                      size="512"
                      initialSize="512"
                      autowarmCount="0"/>
  </query>
  
  <!-- 自動コミット設定 -->
  <updateHandler class="solr.DirectUpdateHandler2">
    <autoCommit>
      <maxTime>30000</maxTime>
      <openSearcher>false</openSearcher>
    </autoCommit>
    <autoSoftCommit>
      <maxTime>1000</maxTime>
    </autoSoftCommit>
  </updateHandler>
  
  <!-- Learning to Rank設定 -->
  <transformer name="features" class="org.apache.solr.ltr.response.transform.LTRFeatureLoggerTransformerFactory">
    <str name="fvCacheName">QUERY_DOC_FV</str>
  </transformer>
</config>