Apache Solr

Apache Luceneベースのエンタープライズ検索プラットフォーム。豊富な検索機能、ファセット検索、分散検索を提供。XMLとJSON APIをサポート。

検索エンジンApache Luceneフルテキスト検索分散企業向けオープンソースJavaRESTful

Apache Solr

Apache Solrは、Apache Luceneをベースとした企業向けの検索プラットフォームです。高度な検索機能、豊富なテキスト分析、分散検索、Web管理インターフェースを提供し、大規模なドキュメント管理と高性能な検索システムの構築を可能にします。

主な特徴

高度な検索機能

  • フルテキスト検索と複雑なクエリ
  • ファセット検索とフィルタリング
  • 自動補完とスペルチェック
  • MoreLikeThis(類似ドキュメント検索)

スケーラブルアーキテクチャ

  • SolrCloudによる分散処理
  • 自動シャーディングとレプリケーション
  • ゼロダウンタイムでのスケーリング
  • ZooKeeperによるクラスタ管理

エンタープライズ機能

  • リッチなWeb管理インターフェース
  • RESTful APIとJSON/XML対応
  • セキュリティ認証・認可
  • 詳細な監視とログ機能

インストール

事前準備(Java)

# Java 11以上のインストール確認
java -version

# Java 11のインストール(Ubuntu/Debian)
sudo apt update
sudo apt install openjdk-11-jdk

# Java 11のインストール(CentOS/RHEL)
sudo yum install java-11-openjdk-devel

バイナリインストール

# Solrのダウンロード
wget https://downloads.apache.org/lucene/solr/9.4.1/solr-9.4.1.tgz
tar -xzf solr-9.4.1.tgz

# インストールスクリプトの実行
sudo tar xzf solr-9.4.1.tgz solr-9.4.1/bin/install_solr_service.sh --strip-components=2
sudo bash ./install_solr_service.sh solr-9.4.1.tgz

# サービス開始
sudo systemctl start solr
sudo systemctl enable solr

Docker

# Solr単体起動
docker run --name my_solr -d -p 8983:8983 -t solr:9.4

# データボリューム付きで起動
docker run --name my_solr -d -p 8983:8983 \
  -v solr_data:/var/solr \
  -t solr:9.4

# カスタム設定で起動
docker run --name my_solr -d -p 8983:8983 \
  -v $PWD/myconfig:/opt/solr/myconfig \
  -t solr:9.4 \
  solr-create -c mycollection -d /opt/solr/myconfig

Docker Compose (SolrCloud)

version: '3.8'
services:
  zookeeper:
    image: zookeeper:3.8
    hostname: zookeeper
    ports:
      - "2181:2181"
    environment:
      ZOO_MY_ID: 1
      ZOO_SERVERS: server.1=zookeeper:2888:3888;2181
    volumes:
      - zoo_data:/data
      - zoo_logs:/datalog

  solr1:
    image: solr:9.4
    hostname: solr1
    ports:
      - "8981:8983"
    environment:
      - ZK_HOST=zookeeper:2181
    depends_on:
      - zookeeper
    volumes:
      - solr1_data:/var/solr

  solr2:
    image: solr:9.4
    hostname: solr2
    ports:
      - "8982:8983"
    environment:
      - ZK_HOST=zookeeper:2181
    depends_on:
      - zookeeper
    volumes:
      - solr2_data:/var/solr

  solr3:
    image: solr:9.4
    hostname: solr3
    ports:
      - "8983:8983"
    environment:
      - ZK_HOST=zookeeper:2181
    depends_on:
      - zookeeper
    volumes:
      - solr3_data:/var/solr

volumes:
  zoo_data:
  zoo_logs:
  solr1_data:
  solr2_data:
  solr3_data:

基本設定

solr.xml設定

<?xml version="1.0" encoding="UTF-8" ?>
<solr>
  <solrcloud>
    <str name="host">${host:}</str>
    <int name="hostPort">${jetty.port:8983}</int>
    <str name="hostContext">${hostContext:solr}</str>
    <bool name="genericCoreNodeNames">${genericCoreNodeNames:true}</bool>
    <int name="zkClientTimeout">${zkClientTimeout:30000}</int>
    <int name="distribUpdateSoTimeout">${distribUpdateSoTimeout:600000}</int>
    <int name="distribUpdateConnTimeout">${distribUpdateConnTimeout:60000}</int>
    <str name="zkCredentialsProvider">${zkCredentialsProvider:org.apache.solr.common.cloud.DefaultZkCredentialsProvider}</str>
    <str name="zkACLProvider">${zkACLProvider:org.apache.solr.common.cloud.DefaultZkACLProvider}</str>
  </solrcloud>

  <shardHandlerFactory name="shardHandlerFactory"
                       class="HttpShardHandlerFactory">
    <int name="socketTimeout">${socketTimeout:600000}</int>
    <int name="connTimeout">${connTimeout:60000}</int>
  </shardHandlerFactory>
</solr>

solrconfig.xml(基本設定)

<?xml version="1.0" encoding="UTF-8" ?>
<config>
  <luceneMatchVersion>9.4.1</luceneMatchVersion>

  <lib dir="${solr.install.dir:../../../..}/contrib/extraction/lib" regex=".*\.jar" />
  <lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-cell-\d.*\.jar" />

  <dataDir>${solr.data.dir:}</dataDir>

  <directoryFactory name="DirectoryFactory"
                    class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}"/>

  <codecFactory class="solr.SchemaCodecFactory"/>

  <indexConfig>
    <lockType>${solr.lock.type:native}</lockType>
    <infoStream>true</infoStream>
  </indexConfig>

  <jmx />

  <updateHandler class="solr.DirectUpdateHandler2">
    <updateLog>
      <str name="dir">${solr.ulog.dir:}</str>
      <int name="numVersionBuckets">${solr.ulog.numVersionBuckets:65536}</int>
    </updateLog>

    <autoCommit>
      <maxTime>${solr.autoCommit.maxTime:15000}</maxTime>
      <openSearcher>false</openSearcher>
    </autoCommit>

    <autoSoftCommit>
      <maxTime>${solr.autoSoftCommit.maxTime:1000}</maxTime>
    </autoSoftCommit>
  </updateHandler>

  <query>
    <maxBooleanClauses>${solr.max.booleanClauses:1024}</maxBooleanClauses>
    <filterCache class="solr.CaffeineCache"
                 size="512"
                 initialSize="512"
                 autowarmCount="0"/>
    <queryResultCache class="solr.CaffeineCache"
                      size="512"
                      initialSize="512"
                      autowarmCount="0"/>
    <documentCache class="solr.CaffeineCache"
                   size="512"
                   initialSize="512"
                   autowarmCount="0"/>
  </query>

  <requestDispatcher>
    <requestParsers enableRemoteStreaming="true"
                    multipartUploadLimitInKB="-1"
                    formdataUploadLimitInKB="-1"
                    addHttpRequestToContext="false"/>
    <httpCaching never304="true" />
  </requestDispatcher>

  <requestHandler name="/select" class="solr.SearchHandler">
    <lst name="defaults">
      <str name="echoParams">explicit</str>
      <int name="rows">10</int>
      <str name="df">text</str>
    </lst>
  </requestHandler>
</config>

基本操作

Solrサーバー管理

# Solr開始
bin/solr start

# SolrCloud開始
bin/solr start -c

# 特定ポートで開始
bin/solr start -p 8984

# メモリ設定付きで開始
bin/solr start -m 2g

# Solr停止
bin/solr stop

# Solr状態確認
bin/solr status

コレクション管理

# コレクション作成(スタンドアローン)
bin/solr create -c mycore

# コレクション作成(SolrCloud)
bin/solr create -c mycollection -d sample_techproducts_configs -shards 2 -replicationFactor 2

# 設定セットをアップロード
bin/solr zk upconfig -n myconfig -d server/solr/configsets/_default/conf -z localhost:9983

# 既存の設定セットを使用してコレクション作成
bin/solr create -c logs2 -n basic

# コレクション削除
bin/solr delete -c mycollection

ZooKeeper操作

# 設定をZooKeeperにアップロード
bin/solr zk upconfig -z 127.0.0.1:9983 -n my_new_config -d server/solr/configsets/_default/conf

# ZooKeeper設定確認
bin/solr zk ls /configs -z localhost:9983

# 設定ダウンロード
bin/solr zk downconfig -z localhost:9983 -n myconfig -d ./myconfig

データ操作

ドキュメント追加

# JSON形式でドキュメント追加
curl -X POST "http://localhost:8983/solr/mycollection/update?commit=true" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "id": "1",
      "title": "Apache Solr入門",
      "content": "Solrは強力な検索エンジンです",
      "category": "technology",
      "published_date": "2024-01-15T10:00:00Z"
    },
    {
      "id": "2", 
      "title": "検索システム設計",
      "content": "効率的な検索システムの構築方法",
      "category": "engineering",
      "published_date": "2024-01-16T14:30:00Z"
    }
  ]'

# XMLでドキュメント追加
curl -X POST "http://localhost:8983/solr/mycollection/update?commit=true" \
  -H "Content-Type: application/xml" \
  -d '<add>
    <doc>
      <field name="id">3</field>
      <field name="title">Lucene活用法</field>
      <field name="content">Luceneの高度な機能</field>
      <field name="category">tutorial</field>
    </doc>
  </add>'

# CSVファイルからインポート
curl -X POST "http://localhost:8983/solr/mycollection/update?commit=true" \
  -H "Content-Type: application/csv" \
  --data-binary @data.csv

アトミック更新

# 部分更新
curl -X POST "http://localhost:8983/solr/mycollection/update?commit=true" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "id": "1",
      "content": {"set": "更新されたコンテンツです"},
      "views": {"inc": 1}
    }
  ]'

ドキュメント削除

# IDで削除
curl -X POST "http://localhost:8983/solr/mycollection/update?commit=true" \
  -H "Content-Type: application/json" \
  -d '{"delete":{"id":"1"}}'

# クエリで削除
curl -X POST "http://localhost:8983/solr/mycollection/update?commit=true" \
  -H "Content-Type: application/json" \
  -d '{"delete":{"query":"category:old"}}'

検索クエリ

基本検索

# 全検索
curl "http://localhost:8983/solr/mycollection/select?q=*:*"

# テキスト検索
curl "http://localhost:8983/solr/mycollection/select?q=Solr"

# フィールド指定検索
curl "http://localhost:8983/solr/mycollection/select?q=title:Apache"

# フレーズ検索
curl "http://localhost:8983/solr/mycollection/select?q=content:\"検索エンジン\""

# AND/OR検索
curl "http://localhost:8983/solr/mycollection/select?q=title:Apache AND category:technology"

範囲検索・フィルタ

# 範囲検索
curl "http://localhost:8983/solr/mycollection/select?q=*:*&fq=published_date:[2024-01-01T00:00:00Z TO 2024-12-31T23:59:59Z]"

# 数値範囲
curl "http://localhost:8983/solr/mycollection/select?q=*:*&fq=price:[100 TO 500]"

# 複数フィルタ
curl "http://localhost:8983/solr/mycollection/select?q=*:*&fq=category:technology&fq=published_date:[NOW-30DAYS TO NOW]"

ファセット検索

# 基本ファセット
curl "http://localhost:8983/solr/mycollection/select?q=*:*&facet=true&facet.field=category"

# 複数フィールドファセット
curl "http://localhost:8983/solr/mycollection/select?q=*:*&facet=true&facet.field=category&facet.field=author"

# 日付ファセット
curl "http://localhost:8983/solr/mycollection/select?q=*:*&facet=true&facet.date=published_date&facet.date.start=2024-01-01T00:00:00Z&facet.date.end=2024-12-31T23:59:59Z&facet.date.gap=%2B1MONTH"

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

高度な検索機能

# ハイライト
curl "http://localhost:8983/solr/mycollection/select?q=Solr&hl=true&hl.fl=content"

# More Like This
curl "http://localhost:8983/solr/mycollection/mlt?q=id:1&mlt.fl=title,content&mlt.mintf=1&mlt.mindf=1"

# スペルチェック
curl "http://localhost:8983/solr/mycollection/spell?q=slor&spellcheck=true&spellcheck.build=true"

# オートコンプリート
curl "http://localhost:8983/solr/mycollection/suggest?suggest=true&suggest.build=true&suggest.dictionary=mySuggester&suggest.q=ap"

ソート・ページング

# ソート
curl "http://localhost:8983/solr/mycollection/select?q=*:*&sort=published_date desc"

# ページング
curl "http://localhost:8983/solr/mycollection/select?q=*:*&start=20&rows=10"

# フィールド指定
curl "http://localhost:8983/solr/mycollection/select?q=*:*&fl=id,title,score"

スキーマ設定

フィールド定義

<!-- managed-schema -->
<schema name="myschema" version="1.6">
  <fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
    <analyzer type="index">
      <tokenizer class="solr.StandardTokenizerFactory"/>
      <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
      <filter class="solr.LowerCaseFilterFactory"/>
    </analyzer>
    <analyzer type="query">
      <tokenizer class="solr.StandardTokenizerFactory"/>
      <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
      <filter class="solr.SynonymGraphFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
      <filter class="solr.LowerCaseFilterFactory"/>
    </analyzer>
  </fieldType>

  <fieldType name="text_cjk" class="solr.TextField" positionIncrementGap="100">
    <analyzer>
      <tokenizer class="solr.CJKTokenizerFactory"/>
      <filter class="solr.CJKBigramFilterFactory"/>
      <filter class="solr.LowerCaseFilterFactory"/>
    </analyzer>
  </fieldType>

  <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_cjk" indexed="true" stored="true"/>
  <field name="category" type="string" indexed="true" stored="true"/>
  <field name="published_date" type="pdate" indexed="true" stored="true"/>
  <field name="tags" type="string" indexed="true" stored="true" multiValued="true"/>

  <dynamicField name="*_i" type="pint" indexed="true" stored="true"/>
  <dynamicField name="*_s" type="string" indexed="true" stored="true"/>
  <dynamicField name="*_txt" type="text_general" indexed="true" stored="true"/>

  <uniqueKey>id</uniqueKey>
</schema>

コピーフィールド

<!-- 検索用統合フィールド -->
<field name="text" type="text_general" indexed="true" stored="false" multiValued="true"/>
<copyField source="title" dest="text"/>
<copyField source="content" dest="text"/>
<copyField source="category" dest="text"/>

SolrCloud設定

分散設定

# 3ノードSolrCloudクラスタ起動
bin/solr start -c -p 8983 -s example/cloud/node1/solr
bin/solr start -c -p 7574 -s example/cloud/node2/solr -z localhost:9983
bin/solr start -c -p 8984 -s example/cloud/node3/solr -z localhost:9983

# シャード付きコレクション作成
bin/solr create -c products -shards 3 -replicationFactor 2

ゾーン・ラック対応

# ノードにゾーン属性設定
curl "http://localhost:8983/solr/admin/collections?action=ADDREPLICAPROP&collection=mycollection&shard=shard1&replica=core_node1&property=preferredleader&property.value=true"

# レプリカを特定ノードに配置
curl "http://localhost:8983/solr/admin/collections?action=ADDREPLICA&collection=mycollection&shard=shard1&node=solr_node2:8984_solr"

セキュリティ設定

基本認証

# 基本認証有効化
bin/solr auth enable

# security.json設定
cat > security.json << 'EOF'
{
  "authentication":{
    "blockUnknown": true,
    "class":"solr.BasicAuthPlugin",
    "credentials":{"admin":"IV0EHq1OnNrj6gvRCwvFwTrZ1+z1oBbnQdiVC3otuq0= Ndd7LKvVBAaZIF0QAVi1ekCfAJXr1GGfLtRUXhgrF8c="},
    "realm":"Solr",
    "forwardCredentials": false
  },
  "authorization":{
    "class":"solr.RuleBasedAuthorizationPlugin",
    "permissions":[
      {"name":"security-edit", "role":"admin"},
      {"name":"collection-admin-edit", "role":"admin"},
      {"name":"core-admin-edit", "role":"admin"}
    ],
    "user-role":{"admin":"admin"}
  }
}
EOF

SSL/TLS設定

# キーストア作成
keytool -genkeypair -alias solr-ssl -keyalg RSA -keysize 2048 -keypass secret -storepass secret -validity 9999 -keystore solr-ssl.keystore.jks -ext SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Organizational Unit, O=Organization, L=Location, ST=State, C=Country"

# Solr SSL有効化
bin/solr start -p 8984 \
  -Dsolr.ssl.keyStore=solr-ssl.keystore.jks \
  -Dsolr.ssl.keyStorePassword=secret \
  -Dsolr.ssl.trustStore=solr-ssl.keystore.jks \
  -Dsolr.ssl.trustStorePassword=secret \
  -Dsolr.ssl.needClientAuth=false \
  -Dsolr.ssl.wantClientAuth=false

監視とメンテナンス

メトリクス監視

# メトリクスAPI
curl "http://localhost:8983/solr/admin/metrics?group=all"

# JMX メトリクス
curl "http://localhost:8983/solr/admin/metrics?group=jvm"

# コレクション統計
curl "http://localhost:8983/solr/admin/metrics?group=core&prefix=CORE.mycollection"

ログ設定

<!-- log4j2.xml -->
<Configuration>
  <Appenders>
    <RollingFile name="RollingFile" fileName="logs/solr.log"
                 filePattern="logs/solr.log.%i.gz">
      <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level (%thread) [%X{collection} %X{shard} %X{replica} %X{core}] %logger{36} %msg%n"/>
      <Policies>
        <SizeBasedTriggeringPolicy size="32 MB"/>
      </Policies>
      <DefaultRolloverStrategy max="10"/>
    </RollingFile>
  </Appenders>
  
  <Loggers>
    <Logger name="org.apache.solr" level="INFO"/>
    <Logger name="org.apache.solr.update.LoggingInfoStream" level="OFF"/>
    <Root level="INFO">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

バックアップ・リストア

# バックアップ作成
curl "http://localhost:8983/solr/admin/collections?action=BACKUP&name=backup1&collection=mycollection&location=/var/backups"

# リストア実行
curl "http://localhost:8983/solr/admin/collections?action=RESTORE&name=backup1&collection=restored_collection&location=/var/backups"

# バックアップステータス確認
curl "http://localhost:8983/solr/admin/collections?action=REQUESTSTATUS&requestid=1000"

パフォーマンス最適化

# インデックス最適化
curl "http://localhost:8983/solr/mycollection/update?optimize=true"

# コミット実行
curl "http://localhost:8983/solr/mycollection/update?commit=true"

# キャッシュウォームアップ
curl "http://localhost:8983/solr/mycollection/admin/system?action=warming"

Apache Solrは、その豊富な機能と企業グレードの安定性により、電子商取引サイトの商品検索、企業内検索、ログ分析、コンテンツ管理システムなど、様々な検索アプリケーションで広く採用されています。適切な設計と設定により、高性能で拡張性のある検索システムを構築できます。