Solrのドキュメントルーティングのやり方




# ドキュメントルーティングとは
クライアントからインデックスさせるシャードを指定する機能です。
シャーディングしているインデックスに対しては[group.ngroups、group.facetが正常に機能しない](https://cwiki.apache.org/confluence/display/solr/Result+Grouping#ResultGrouping-DistributedResultGroupingCaveats)(間違った値が取れる)という欠点がありますが、ドキュメントルーティングすることで、これらの機能を正常に動かすことができます。

# 参考資料
下記を参考にしました。
https://lucidworks.com/blog/2013/06/13/solr-cloud-document-routing/

# やり方
ドキュメントルーティングをするには、下記の形式でunique keyを書き換えます。

```
routing key + ! + unique key 
```

例

```
hogehoge!00001
```

routing keyの文字列を元にしてハッシュ値(murmurhash3の32bit)が生成されます。
各シャードは自分が担当するハッシュの値域が予め設定されており、このハッシュ値が該当するシャードにドキュメントが振り分けられます。
各シャードの担当値域はstate.jsonの中で確認できます。

```
{"routing":{
    "replicationFactor":"3",
    "router":{"name":"compositeId"},
    "maxShardsPerNode":"1",
    "autoAddReplicas":"false",
    "shards":{
      "shard1":{
        "range":"80000000-d554ffff", //これ
```

# 調査用のインデックス
https://github.com/tkondo1223/solr-advent-calender2016/tree/master/routing

# 各種比較
## ドキュメントの偏り
### ドキュメントルーティング無し

全シャードにドキュメントが振り分けられています。
シャード1
![image](https://qiita-image-store.s3.amazonaws.com/0/147530/59232441-ef35-6c63-177c-81ffa7b2027b.png)


シャード2
![image](https://qiita-image-store.s3.amazonaws.com/0/147530/abc20f82-7567-ada0-d9d5-f3fb2974e85d.png)



シャード3
![image](https://qiita-image-store.s3.amazonaws.com/0/147530/57c1e2dc-76d5-5ae0-ba5f-21129ba10203.png)




### ドキュメントルーティング有り
シャード1
![image](https://qiita-image-store.s3.amazonaws.com/0/147530/a8c6992a-4d38-d4c4-175f-5eeaffb107c1.png)

シャード2
![image](https://qiita-image-store.s3.amazonaws.com/0/147530/a351a039-3f36-2786-b160-66976e66ae96.png)

シャード3
![image](https://qiita-image-store.s3.amazonaws.com/0/147530/81fc2d6e-f2d5-d692-3f18-95be9ca92968.png)

キーと各シャードの担当範囲は下記の通りなので、正しくルーティングされています。


|ルーティングキー|  mmh3| 振り分け先| ドキュメント数|
|:-----------|------------|------------|------------:|
|key1| 9b970125 | shard1 | 6|
|key2| 737139b4 | shard3| 4|
|key3| a648eb58 | shard1| 9|


|            |  担当範囲|
|:-----------|------------:|
|    shard1 |         80000000-d554ffff|
|    shard2 |         d5550000-2aa9ffff|
|    shard3 |         2aaa0000-7fffffff|



## groupingへの影響

### group.ngroups
```
q=*:*&wt=json&start=0&rows=10&group=true&group.field=mmh3&group.ngroups=true
```

#### ドキュメントルーティング無し
ngroupsが正しくない

```
{
  "grouped": {
    "mmh3": {
      "matches": 19,
      "ngroups": 7, //間違い
      "groups": [
        {
          "groupValue": "a648eb58",
          "doclist": {
            "numFound": 9,
            "start": 0,
            "maxScore": 1,
            "docs": [
              {
                "id": "5",
                "mmh3": "a648eb58",
                "_version_": 1553056878448083000
              }
            ]
          }
        },
        {
          "groupValue": "9b970125",
          "doclist": {
            "numFound": 6,
            "start": 0,
            "maxScore": 1,
            "docs": [
              {
                "id": "18",
                "mmh3": "9b970125",
                "_version_": 1553056878470103000
              }
            ]
          }
        },
        {
          "groupValue": "737139b4",
          "doclist": {
            "numFound": 4,
            "start": 0,
            "docs": [
              {
                "id": "1",
                "mmh3": "737139b4",
                "_version_": 1553056878368391200
              }
            ]
          }
        }
      ]
    }
  }
}
```


#### ドキュメントルーティング有り
正しい結果が返ってきている

```
{
  "grouped": {
    "mmh3": {
      "matches": 19,
      "ngroups": 3, //正しい
      "groups": [
        {
          "groupValue": "737139b4",
          "doclist": {
            "numFound": 4,
            "start": 0,
            "docs": [
              {
                "id": "key2!1",
                "mmh3": "737139b4",
                "_version_": 1553055543823696000
              }
            ]
          }
        },
        {
          "groupValue": "9b970125",
          "doclist": {
            "numFound": 6,
            "start": 0,
            "docs": [
              {
                "id": "key1!2",
                "mmh3": "9b970125",
                "_version_": 1553055543823696000
              }
            ]
          }
        },
        {
          "groupValue": "a648eb58",
          "doclist": {
            "numFound": 9,
            "start": 0,
            "docs": [
              {
                "id": "key3!3",
                "mmh3": "a648eb58",
                "_version_": 1553055543826841600
              }
            ]
          }
        }
      ]
    }
  }
}
```


## group.facet
```
q=*:*&wt=json&start=0&rows=10&group=true&group.field=mmh3&group.ngroups=true&group.facet=true&facet.field=mmh3&facet=true
```

mmh3単位での集計のため、全て1が正しい

ドキュメントルーティング無し


```
    "facet_fields": {
      "mmh3": [
        "9b970125",
        3,
        "a648eb58",
        3,
        "737139b4",
        1
      ]
    },
```

ドキュメントルーティング有り

```
    "facet_fields": {
      "mmh3": [
        "737139b4",
        1,
        "9b970125",
        1,
        "a648eb58",
        1
      ]
    }
```

# デメリット
ドキュメントルーティングを使い振り分けをすればngourpsなどの機能が正常に処理できますが、デメリットも存在します。
1つ目はハッシュ値(ユーザーから確認できない)で振り分けがされるのですが、キーがどのハッシュ値になるかSolrからは確認できないので、事前にソースコードを書いてどのキーでどのシャードに振り分けられるか確認しておかないといけません。
2つ目はキー名の自由度が無いことです。
キー名をgroup[1-3]みたいなものにして、それぞれ対応するナンバリングのshardに振り分けたいと考えていても、group1とgroup3のハッシュがshard1の担当範囲に入るとキー名を変える必要があります。
3つ目はこの仕様をシステム担当者がずっと引き継いでいかないといけないことです。
時が経って、仕様も忘れ去られた時に新しいグループの追加が発生すると大変なことになるかもしれません。

# まとめ
ドキュメントルーティング自体はクセがあって少し使いにくい機能だなと感じましたが、ドキュメント数が多くなるとインデキシングのパフォーマンス向上やインデックスをメモリに乗せきるために、シャーディングは必須なため、上手くこの仕様と付き合っていきたいところです。