Liferay 7.3 + Elasticsearch 7.9.3 + Kuromoji NEologd plugin

# はじめに
Liferay7.3では、検索エンジンにElasticsearchを利用している。だがデフォルトはシンプルなKuromojiのみを利用しており、アナライザ・トークナイザの精度は高くはない。今回は新語や固有表現に強いIpadic-NEologd辞書を利用したプラグインを利用した、形態素解析の精度が高い日本語検索にElasticsearchをチューニングしてみます。

この記事の内容を反映したDocker composeは[https://github.com/yasuflatland-lf/elasticsearch-docker-composer-for-liferay-7/tree/7.9.3_AdoptOpenJDK11](https://github.com/yasuflatland-lf/elasticsearch-docker-composer-for-liferay-7/tree/7.9.3_AdoptOpenJDK11)に置いてあります。ご自分が利用されるElasticsearchのバージョンが異なる場合は、以下の手順でElasticsearchプラグインを自作する必要がありますがこのDocker Composeをそのまま利用される場合は、[[How to set upの1-4]](https://github.com/yasuflatland-lf/elasticsearch-docker-composer-for-liferay-7/tree/7.9.3_AdoptOpenJDK11#how-to-set-up)を実行して、[Liferay側のElasticsearch設定](#liferay側のelasticsearch設定)から読んでください。

## 前提条件
- Liferay DXP 7.3 SP1
- Elasticsearch 7.9.3
- Docker 3.3.3+
- テストした環境はMac OS 11.4 (Big Sur) メモリ32GB

## Elasticsearchの指定
Liferay 7.3 SP1現在、対応するElasticsearchは7.9+ということで、7.9.3を利用することとする。

## Elasticsearch + Kuromoji NEologdのDocker Composeレポジトリを取得
[https://github.com/yasuflatland-lf/elasticsearch-docker-composer-for-liferay-7](https://github.com/yasuflatland-lf/elasticsearch-docker-composer-for-liferay-7)を`git clone`し、ブランチを`7.9.3_AdoptOpenJDK11`に切り替える。

## 該当のElasticsearchで利用しているLuceneのバージョンを調べる
Elasticsearchで利用しているLuceneに対してプラグインを作成するので、以下の方法で利用しているLuceneのバージョンを調べることができる。

1. この[Docker Composeファイル](https://github.com/yasuflatland-lf/elasticsearch-docker-composer-for-liferay-7)を`docker-compose up --build`で起動する。
1. `http://localhost:9200/`にアクセス。以下のような表示がされる

```json
{
  "name" : "LiferayElasticsearchCluster",
  "cluster_name" : "LiferayElasticsearchCluster",
  "cluster_uuid" : "AR7mNfG8Q5K_Iy_F7pMqzg",
  "version" : {
    "number" : "7.9.3",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "c4138e51121ef06a6404866cddc601906fe5c868",
    "build_date" : "2020-10-16T10:36:16.141335Z",
    "build_snapshot" : false,
    "lucene_version" : "8.6.2",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}
```
ここからLuceneのバージョンは`"lucene_version" : "8.6.2",`ということがわかる。

## Kuromoji を mecab-neologdとしてビルド
[https://github.com/kazuhira-r/kuromoji-with-mecab-neologd-buildscript](https://github.com/kazuhira-r/kuromoji-with-mecab-neologd-buildscript)に有志の方が作成してくれているビルドスクリプトがあるので、これを利用させてもらって、Kuromojiをビルドする。

1. [https://github.com/kazuhira-r/kuromoji-with-mecab-neologd-buildscript](https://github.com/kazuhira-r/kuromoji-with-mecab-neologd-buildscript)を`git clone`する。
1. `chmod +x build-lucene-kuromoji-with-mecab-ipadic-neologd.sh`で実行権限をつける。
1. `build-lucene-kuromoji-with-mecab-ipadic-neologd.sh`を開き、以下のコードの`lucene-solr`のバージョンを先ほど調べた`Lucene`バージョンに変更する。

        ## Lucene Target Tag
        DEFAULT_LUCENE_VERSION_TAG=releases/lucene-solr/8.6.2
        LUCENE_VERSION_TAG=${DEFAULT_LUCENE_VERSION_TAG}

4. `./build-lucene-kuromoji-with-mecab-ipadic-neologd.sh`でビルドを実行する。
1. `lucene-analyzers-kuromoji-ipadic-neologd-8.6.2-20200910.jar`が生成される。

## Elasticsearch用プラグインビルド
これもまた有志の方がプラグインを作ってくれているのだが、7.9.3バージョンはない。自前ビルドバージョンを読み込むように変更したコードをここにおいてあるので、それを利用してビルドする。

1. [https://github.com/codelibs/elasticsearch-analysis-kuromoji-ipadic-neologd](https://github.com/codelibs/elasticsearch-analysis-kuromoji-ipadic-neologd) リポジトリを`git clone`する。
1. `pom.xml`を以下のように編集する。


```diff
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>org.codelibs</groupId>
 	<artifactId>elasticsearch-analysis-kuromoji-ipadic-neologd</artifactId>
-	<version>7.2.1-SNAPSHOT</version>
+	<version>7.9.3-SNAPSHOT</version>
 	<packaging>jar</packaging>
 	<description>This plugin provides an analysis library for Kuromoji with UniDic and Neologd.</description>
 	<inceptionYear>2011</inceptionYear>
```

```diff
 	<properties>
-		<elasticsearch.version>7.2.1</elasticsearch.version>
+		<elasticsearch.version>7.9.3</elasticsearch.version>
 		<elasticsearch.plugin.classname>org.codelibs.elasticsearch.kuromoji.ipadic.neologd.KuromojiNeologdPlugin</elasticsearch.plugin.classname>
 		<maven.compiler.source>1.8</maven.compiler.source>
 		<maven.compiler.target>1.8</maven.compiler.target>
-		<lucene.version>8.0.0</lucene.version>
-		<kuromoji-ipadic-neologd.version>20190808</kuromoji-ipadic-neologd.version>
+		<lucene.version>8.6.2</lucene.version>
+		<kuromoji-ipadic-neologd.version>20200910</kuromoji-ipadic-neologd.version>

 	</properties>

```

 3 . Mavenの最新バージョンを使うように編集する。
※ バージョン一覧：[[maven-source-plugin]](https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-source-plugin)  [[maven-assembly-plugin]](https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-assembly-plugin)   [[org.codelibs.analyzers]](https://mvnrepository.com/artifact/org.codelibs/analyzers)

```diff
<plugin>
    <artifactId>maven-source-plugin</artifactId>
-		<version>3.1.0</version>
+		<version>3.2.1</version>

```

```diff
<plugin>
    <artifactId>maven-assembly-plugin</artifactId>
-		<version>3.1.1</version>
+		<version>3.3.0</version>

```

```diff
<dependency>
    <groupId>org.codelibs</groupId>
    <artifactId>analyzers</artifactId>
-		<version>8.0.0.0</version>
+		<version>8.7.0.0</version>

```

4 . ロードするAnalizerが変わったことから、全コードのパッケージを以下のように変更。IDEを使っている場合は、再度インポート元を指定しなおしてやる。

```diff
- org.codelibs.neologd.ipadic.lucene.analysis
+ org.apache.lucene.analysis
```

5 . `build-lucene-kuromoji-with-mecab-ipadic-neologd.sh`で生成されたjarをそのディレクトリにコピーする。
6 . `mvn package`でビルドする

※1. Jarファイルが識別できない場合、下記のコマンドで、JARファイルをMavenにインストールしてください。）
参考：[Guide to installing 3rd party JARs](https://maven.apache.org/guides/mini/guide-3rd-party-jars-local.html)

```
mvn install:install-file -Dfile=JARファイルのパス/lucene-analyzers-kuromoji-ipadic-neologd-8.6.2-20200910.jar -DgroupId=org.codelibs -DartifactId=lucene-analyzers-kuromoji-ipadic-neologd -Dversion=8.6.2-20200910 -Dpackaging=jar
```

7 . `/target/releases`以下に`elasticsearch-analysis-kuromoji-ipadic-neologd-7.9.3-SNAPSHOT.zip`というファイルが生成される。

## Elasticsearchに組み込み
1. `elasticsearch-docker-composer-for-liferay-7`リポジトリに移動
1. `es`ディレクトリに`elasticsearch-analysis-kuromoji-ipadic-neologd-7.9.3-SNAPSHOT.zip`をコピー
1. `Dockerfile`を開いて、以下のように変更。

```dockerfile
FROM docker.elastic.co/elasticsearch/elasticsearch:7.9.3

COPY ./elasticsearch-analysis-kuromoji-ipadic-neologd-7.9.3-SNAPSHOT.zip ./

RUN elasticsearch-plugin install file:./elasticsearch-analysis-kuromoji-ipadic-neologd-7.9.3-SNAPSHOT.zip
RUN elasticsearch-plugin install analysis-icu
RUN elasticsearch-plugin install analysis-smartcn
RUN elasticsearch-plugin install analysis-stempel
```

## Liferay側のElasticsearch設定
1. Control Panel -> System Settings -> Search -> Elasticsearch 7へ移動
1. `Production Mode Enabled`にチェックを入れる
1. `Additional Index Configurations`に`elasticsearch-docker-composer-for-liferay-7`リポジトリのルートディレクトリにある`index-settings.json`の中身をコピー
1. `Override Type Mappings`に`liferay-type-mappings.json`の中身をコピー
1. `Network Host Addresses`にElasticsearchのサーバーURLを入力。（今回はローカルDockerイメージの為、デフォルト`http://localhost:9200`のままで設定不要）
1. 設定を保存、サーバーを再起動
1. Control Panel -> Search -> Connections -> Active ConnectionsにElasticsearchサーバが識別されて、バージョンが`7.9.3`であることを確認
1. Control Panel -> Search -> Index Actions -> Reindex all search indexes.を実行


## Liferayでの動作テスト
これでLiferay、Elasticsearchのインテグレーションができたので、次は実際にコンテンツを作ってみる。今回は、`東京都`というワードと`京都`が入った記事を作成し、正しく別の言葉として検索できるかをテストする。

Liferayのデフォルト言語を英語としてセットアップしてあるという前提で説明する。Liferayではデフォルト言語はデフォルト設定ではStandard Analyzerが利用されてしまうため、日本語検索結果が不正になる。そのため、デフォルト言語は英語でセットアップすることをおすすめする。

### Web Contentの作成
1. Web Contentを開き、Basic Web Contentを作成する。英語ロケールでタイトルは`Test1`、コンテンツも`Test1`とする
1. 次にロケールを日本語に変更し、タイトルはそのまま、コンテンツを`京都の観光施設・名所巡りランキングTOP10`とする
1. もう一つBasic Web Contentを作成し、英語ロケールでタイトルとコンテンツを`Test2`、日本語ロケールでタイトルは`Test2`のままにし、コンテンツは`東京都の水道に関する手続きや料金、東京都水道局の事業や取組、事業者の方向けの情報などをご紹介しています。`とする。
1. 右上のアバターアイコンをクリックし、Acount SettingからPersonal InformationのLanguageを日本語に変更し、保存。
1. 一回ログアウトして、再ログイン。

### 検索テスト
1. ユーザー側のトップ画面に戻り、画面右上の検索バーから`東京都`と検索する
1. Test2のみヒットする
1. 次に`京都`で検索する。
1. Test1のみヒットする

# まとめ
このように、LiferayはElasticsearchのアナライザとトークナイザーをNEologd辞書を利用したプラグインに差し替えるだけで、日本語での検索性能をデフォルトでも向上させることが可能となる。例えば特定の製品コードのパターンにマッチさせたいなどの調整はまたコード側のカスタマイズが必要にはなるが、単純な検索でよい要件の場合は、実用にたえる検索のベースとして利用できるのではないだろうか。