neo4jでも全文検索がしたい！

どうもー。個人開発でグラフDBを使ったSNSを作っております。neo4jの全文検索機能を実装したので、その時のメモを残しておくぜ！

# この記事でわかる事
- neo4jで転置インデックスを作って全文検索する方法
- 日本語検索に対応する方法
- GrapheneDB（herokuのneo4j用add-on)で全文検索を設定する時の注意点

# 前提知識
- neo4j browserを起動して触った事がある
- neo4jでnodeやrelationを作成したり、取得したり、ある程度のクエリを書いた事がある
- 転置インデックスとかluceneの概要を何となく知っている


-------------

...ここから本題...

## neo4jに全文検索を実装したい
neo4jは、バッチリ全文検索もサポートしてます。
[全文検索のドキュメント](https://neo4j.com/docs/cypher-manual/3.5/schema/index/#schema-index-fulltext-search-query)


早速neo4jブラウザを開いて転置インデックスを作ってみましょう

### 転置インデックス作成
SNSの掲示板を想定して、「Post」（投稿）というnodeがあるとしましょう。propertyには「text」が指定されていて、ここに投稿本文が入ります。

#### インデックスを作成する

```
CALL db.index.fulltext.createNodeIndex("postsIndex",["Post"],["text"])
```

第１引数：インデックス名
第２引数：node名
第３引数：property名


#### インデックスを使って検索する

```
CALL db.index.fulltext.queryNodes("postsIndex", "リンクっぽいやつ、出てこいやぁ！") YIELD node, score
RETURN node.text, score
```

第１引数：インデックス名
第２引数：検索文

#### 結果

| node.text | score |
|:--|--:|
| 何らかのリンク https://google.com | 0.0356929749250412 |

検索の結果、 **何らかのリンク https://google.com** というtextが存在するPostが抽出されました。
一致度を示すスコアも計算されてますね。


### え、めっちゃ簡単じゃね？
neo4jの全文検索、これで終わりです。記事書くほどの事でもないよね・・・ごめんね・・・少しだけ補足していくね・・・

#### インデックス更新は自動的に行われる
ちなみに一度作成した転置indexは今後新しいPostが追加されるたび、勝手にtextを解析してindexを追加してくれます。
こちらで投稿内容を保持して、バッチを動かしてインデックス作成して・・・みたいな作業は不要。

#### バックグラウンドスレッドでの実施も簡単
「でも同一トランザクションでインデックスまで作ってたら、遅くならない？」
と思われる方のためには、インデックス生成を別スレッドで実施するオプションも用意されています(eventually_consistent)
設定方法はあとで紹介しますね。



### でも日本語のアナライザを使いたい
デフォルトで使われるアナライザでは日本語の検索に不十分かもしれません。Apache Luceneにあまり詳しくないので、このアナライザがどこまで日本語に対応しているのかは正直全然分からないのですが、アナライザを変えたい場合はオプションを指定すると良いようです。

#### インデックス作成時にオプションを追加する
```
CALL db.index.fulltext.createRelationshipIndex("postIndex",["Post"],["text"], { analyzer: "cjk"})
```

第４引数：インデックス単位の設定

これでpostIndexはcjkアナライザを使ってインデックスを作るようになります。

ただし**enterprise edition**でneo4jを使っている場合は注意が必要です。
[クラスタ間でインデックス単位の設定が引き継がれない可能性がある](https://neo4j.com/docs/cypher-manual/3.5/schema/index/#schema-index-fulltext-search-create-and-configure)そうです。

communityだとnode数も少ないから同一クラスタで賄ってるんですかね？（lucene本当に分からないのでデタラメ言ってるかもしれません）

#### neo4j自体のアナライザ設定を変える
なのでenterprise editionを使うときはneo4j.confを修正して、インデックス単位ではなく、neo4j自体のデフォルトアナライザを指定しちゃうのが良いでしょう。[このプロパティを変えると良いみたい](https://neo4j.com/docs/operations-manual/3.5/performance/index-configuration/fulltext-search/)ですね。

`neo4j.conf`

```
dbms.index.fulltext.default_analyzer
```

#### ちなみに前述の「eventually_consistent」もこんな感じで設定できます

インデックス単位で設定する場合

```
CALL db.index.fulltext.createRelationshipIndex("postIndex",["Post"],["text"], { eventually_consistent: "true"})
```

DB単位で設定する場合

`neo4j.conf`

```
dbms.index.fulltext.eventually_consistent
```


### GrapheneDBを使うときの注意点
GrapheneDBってneo4j.confどこで変えるんでしょうね。調べたけど分からなかったので、現在問い合わせ中です。わかり次第、こちらの記事を更新します。

ただ幸いにもデフォルトアナライザは[dynamic setting](https://neo4j.com/docs/operations-manual/3.5/configuration/dynamic-settings/#dynamic-settings-reference)（neo4j起動中も動的に設定変更できるパラメタ）なので、neo4jブラウザを開いて、こんなクエリを打ち込めば設定を変更できます

```
CALL dbms.setConfigValue('dbms.index.fulltext.default_analyzer', 'cjk')
```

ただし、これは**enterprise edition**でしか使えない機能みたいですね。なので僕はまだ実行した事がありません。
あとdynamic settingはrestartしたら消えるので、本番稼働してるサービスで好ましい設定方法ではありません。

だからneo4j.confで設定したいんですけどね・・・どうやって設定すんねん！（現在問い合わせ中です）