Solrの設計・構築時に先に知っておきたかったトピックス集 

# 0.はじめに
2018年も残すところあとわずかになりました。個人的な話ですが2018年を振り返ってみると Solr案件で大小様々な壁にぶち当たり、そのたびにGoogle先生にお世話になりました。

ただ、Solrの場合、Solrの日本語処理については豊富なドキュメントが見かるものの、それ以外の言語の処理やZooKeeper、ManifoldCFについての実際のところについては情報が少なく、情報収集に苦労しました。

そこで、本記事では、情報収集自体が難しかったSolr関連トピックについて記載させていただきます。トピックの一つひとつは小粒な話になってしまいがちですが、試行錯誤のエッセンス(?)と思っていただければ幸いです。

# 1. Solrについて

## 1.1. Apache Solr のよくある構成
Solr(Apache Solr) はApacheソフトウェア財団で開発されているオープンソースの全文検索システムです。
Solrは実際には「Solr+ZooKeeper+ManifoldCF」という構成で構築することが多いです。
それぞれの役割は下表のとおりです。

|モジュール|説明|
|:---|:---|
|Solr|全文検索エンジン|
|ZooKeeper|分散環境の設定情報集中管理など|
|ManifoldCF|文書ファイルやWebページなどコンテンツを収集し、<br> それらを検索エンジンに送るクローラー。|

# 2. Solr トピックス

## 2.1. Solr編

### 2.1.1. StandardTokenizer と WhitespaceTokenizer の違い
Solrにおけるトークナイザーは検索対象文字列を字句単位またはトークンに分割する役割を担っています。日本語用には「KuromojiTokenizer」というトークナイザーが用意されていますが、日本語以外の言語をインデックス化する場合、もしくは単語のリストを取り込んでスペルチェックやサジェスチョンに用いる場合のトークナイザ―として以下のようなものがあります。

|トークナイザー|動作|
|:---|:---|
|KeywordTokenizer|一切ドキュメントの分割を行わない。|
|WhitespaceTokenizer|空白文字でのみ分割を行う。|
|StandardTokenizer|空白文字と記号で分割を行う。|

StarndardTokenizer では記号での分割を行いますが、"_"(アンダーバー)、":"(コロン)、"'"(シングルクオート)は区切り文字とはなりません。
正書法として分かち書き(英語の単語間を半角スペースで区切るような書き方)が徹底されている言語でしたら StandardTokenizer で最低限の仕事はしてくれます。


WhitespaceTokenizer は WordDelimiterGraphFilter と組み合わせることで、英字と数字の間、小文字と次の大文字の間といった箇所で分割を行うことが可能です。
こちらも分かち書きされる言語のトークナイザ―として使用されていますが、DBから製品番号と製品名をとってくるといった場合に1回のクエリでそれぞれインデックス化できるというような使い方もあります。


KeywordTokenizerはサジェスチョンの元ネタとして過去の検索履歴をxmlファイルなどに書き出して使用するというような場合に使用することになるかと思います。
検索履歴の場合スペース区切りで複数単語を検索した結果まで元ネタにすることができます。

###2.1.2. 中国語の形態素解析
Solrのデフォルトのスキーマ設定に中国語が含まれていない点からも中国語は苦手な言語なようです。

一応 HMMChineseTokenizer がされているものの、かなり精度が悪く、単語として分割可能な語彙が極端に少なくなっています。

そもそもフリーで使えて精度の高い中国語のトークナイザ―というものは存在が怪しいため、インデックスのサイズが大きくなってしまいますがN-gramを利用して2～5文字で分割して精度を高めるしかないのではないでしょうか。

###2.1.3. スペルチェック
スペルチェックの元ネタに使用するデータの文字数は注意しなければ大変なことになることがあります。
スペルチェックは実行前に辞書のビルドが必要ですが対象となる単語の文字数が15文字程度となるとビルドがいつまでたっても終わらない事態が発生してしまいます。
最悪の場合ディスクを使いつぶしてExceptionを発生させた挙句に何もできずに終わることもありました。

日本語等で製品名のリストを元ネタとしてスペルチェックを実施しようとする場合は、形態素解析を行い、適度な長さの単語に分割しなければならないことがあるということです。
(とにかく長い薬品名でひどい目にあいました。)

## 2.2. ManifoldCF編
ManifoldCFのクロール処理はクロール対象のサイズ次第ではそれなりの時間が必要です。

一つ二つのWebサイトやテーブルが対象であればたいして問題とはなりませんが、クロール対象が何十にもなるとすべて完了するまで数時間必要な場合があります。

ボトルネックになるのはあくまでManifoldCF側であるので、Solr側でデータを見に行く方式の方がよい場合があります。


### 2.2.1. クロール速度の上げ方
ManifoldCFの公式にパフォーマンスチューニングを開設した記事があります。

https://manifoldcf.apache.org/release/release-2.10/en_US/performance-tuning.html

ここで解説されている通りManifoldCFのクロール速度を上げるのに最も重要なものはワーカー数になります。

正直言ってここ以外のプロパティを調整しても速度にほぼ変化がありませんでした。
なお、DBの自動vacuumをオフにしておくことも推奨されていますが、私が対応した環境ではいずれもパフォーマンスにあまり影響はありませんでした。

同時処理数以外でクロール速度の上昇に影響があったのは管理DBとして使用したpostgreSQLのバージョンを上げることでした。
postgreSQLはバージョンが上がるごとに処理速度が上がりますので最新のものをインストールしたほうがいいでしょう。

```
【実例】
RHEL6系標準のpostgreSQL8系をそのまま使用した場合と、
postgreSQLを10系にバージョンアップした場合で
クロール速度が20%程度改善してしまった事例もあったりします。
```

### 2.2.3. Oracleクロールにはドライバ入手が必要
ManifoldCF では Oracle のデータベースをクロールの対象とすることができますが、実際にクロールするにはOracleのJDBCドライバが必要となります。クロール対象OracleDBのバージョンにあったJDBドライバをOracleのサイトからダウンロードしてきましょう。

ダウンロードしてきたJDBCドライバ(ojdbcX.jar)は{manifoldCFインストールディレクトリ}/lib等に配置し起動オプションファイルに追記してください。


## 2.3. ZooKeeper編
ZooKeeperは設定さえ正しく行えていれば特に問題なく動作してくれる優秀な奴です。

### 2.3.1. jute.maxbufferの設定
ZooKeeperは扱うデータ量を大分少ないものであると想定しているらしく、10個程度のconfを使用していたり100を超えるノードが存在する場合に扱うデータ量が想定を超えるためExceptionが発生してしまうことがあります。

これは、ZooKeeperの起動オプションでjute.maxbufferの値を指定することで回避可能です。
jute.maxbufferの値はデフォルトでは1MBとなっているため、Exceptionに含まれるデータ量に対応できる値を指定しましょう。

起動オプションの指定はZooKeeper側のzkServer.shとSolr側のzkcli.shの2か所で設定する必要があります。


## 3.最後に
本記事ではSolrの設計・構築で情報収集が難しかったトピックについて書かせていただきました。各トピックで粒度もバラバラで恐縮ではありますが、同じようなところで悩まれている方の何かのお役に立てれば幸いです。