ElasticsearchのMore Like This Queryを試してみた

弊社が運用する[cozreマガジン](https://feature.cozre.jp/)では、記事の検索に[Elasticsearch](https://www.elastic.co/products/elasticsearch)を使っています。理由としては、まあ、色々やりたいことがある（秘密）からなのですがｗ

先日ようやくユーザ登録機能をリリースしまして、「これでユーザの行動履歴が持てる！ひゃっほーい」ということで、ユーザ登録機能と一緒にリリースした、記事のお気に入り機能のデータを使っての類似記事検索をやってみます！

## 準備

Elasticsearchを使っていれば、[More Like This Query](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-mlt-query.html)で簡単に類似検索が出来ます。ということで本日のレシピです。

* 必要なもの：Elasticsearch1.4以上
* 準備するもの：お気に入り保存した、という想定で記事を３本（適当に選定）
 * [子どもと一緒に社会科見学！東京都内の新聞社の見学ツアーにレッツゴー](https://feature.cozre.jp/50116)（ID=50116）
 * [混雑する通勤電車で職場に通う妊婦が気を付ける事とは？｜専門家の見解](https://feature.cozre.jp/65741)（ID=65741）
 * [【コメタパン育児絵日記（20）】幼稚園の親子遠足！お弁当づくりという試練](https://feature.cozre.jp/66401)（ID=66401）
* 記事のマッピング定義（簡略版）

```json
"mappings" : {
  "post" : {
    "properties" : {
      "id" : { "type" : "long" },
      "title" : { "type" : "string", "index" : "analyzed", "analyzer" : "kuromoji_custom" },
      "content" : { "type" : "string", "index" : "analyzed", "analyzer" : "kuromoji_custom" },
      "tags" : { "type" : "string", "index" : "not_analyzed" }
    }
  }
}
```

* 実行するクエリ

```json
curl -XGET localhost:9200/magazine/post/_search?pretty=true -d '
{
  "query": {
    "more_like_this" : {
        "fields" : ["title"],
        "ids" : [50116,65741,66401],
        "percent_terms_to_match" : 0.3,
        "min_term_freq" : 2,
        "max_query_terms" : 25,
        "stop_words" : [],
        "min_doc_freq" : 5,
        "min_word_length" : 0,
        "max_word_length" : 0
    }
  }
}
'
```

## 結果

いくつかのパターンで試してみました。

| Num | fields | percent_terms_to_match | min_term_freq | max_query_terms | min_doc_freq | min_word_length | max_word_length |
|:--|:---------------  |:--|:--|:--|:--|:--|:--|
|1  |title             |0.3|2  |25 |5  |0  |0  |
|2  |tags              |0.3|2  |25 |5  |0  |0  |
|3  |content           |0.3|2  |25 |5  |0  |0  |
|4  |title,tags        |0.3|2  |25 |5  |0  |0  |
|5  |title,content     |0.3|2  |25 |5  |0  |0  |
|6  |tags,content      |0.3|2  |25 |5  |0  |0  |
|7  |title,tags,content|0.3|2  |25 |5  |0  |0  |

### その１

| id | title | score |
|:----|:------|:---------|
|25868|[親子で楽しく体験！社会科見学におすすめなスポット８選｜東京＆近郊](https://feature.cozre.jp/25868)|1.5020734 |
|66721|[子どもと一緒に遊んで学ぼう！茨城県の社会科見学スポット３選](https://feature.cozre.jp/66721)|1.4598341 |
|6905 |[国会議事堂、皇居を見学しよう♪](https://feature.cozre.jp/6905)|1.1526392 |
|7903 |[子どもと一緒にワクワクがいっぱいのフジテレビ見学へおでかけしてみよう！](https://feature.cozre.jp/7903)|0.82331365|
|66732|[うなぎパイの工場見学も！静岡県の子どもと楽しむ工場見学4選](https://feature.cozre.jp/66732)|0.76972544|

### その２

ヒット0件

### その３

| id | title | score |
|:----|:------|:---------|
|63721|[千葉の工場見学はすごい！親子でうれしい体験がたくさんのスポット４選](https://feature.cozre.jp/63721)|0.5898313 |
|43002|[人気&定番のおでかけ！親子で学べる自動車の工場見学４選｜西日本編](https://feature.cozre.jp/43002)|0.56126255|
|64369|[ワクワク体験が待っている！子連れでドキドキ工場見学にゴー｜愛知](https://feature.cozre.jp/64369)|0.55624   |
|59128|[【コメタパン育児絵日記（7）】1歳児が抱えきれるボールの数は・・・](https://feature.cozre.jp/59128)|0.5017484 |
|64126|[大人も子どもも思わず夢中になってしまう工場見学のススメ４選｜大阪](https://feature.cozre.jp/64126)|0.48392826|

### その４

| id | title | score |
|:----|:------|:---------|
|25868|[親子で楽しく体験！社会科見学におすすめなスポット８選｜東京＆近郊](https://feature.cozre.jp/25868)|1.5020734 |
|66721|[子どもと一緒に遊んで学ぼう！茨城県の社会科見学スポット３選](https://feature.cozre.jp/66721)|1.4598341 |
|6905 |[国会議事堂、皇居を見学しよう♪](https://feature.cozre.jp/6905)|1.1526392 |
|7903 |[子どもと一緒にワクワクがいっぱいのフジテレビ見学へおでかけしてみよう！](https://feature.cozre.jp/7903)|0.82331365|
|66732|[うなぎパイの工場見学も！静岡県の子どもと楽しむ工場見学4選](https://feature.cozre.jp/66732)|0.76972544|

### その５

| id | title | score |
|:----|:------|:---------|
|6905 |[国会議事堂、皇居を見学しよう♪](https://feature.cozre.jp/6905)|1.1300747 |
|64369|[ワクワク体験が待っている！子連れでドキドキ工場見学にゴー｜愛知](https://feature.cozre.jp/64369)|0.94125086|
|63721|[千葉の工場見学はすごい！親子でうれしい体験がたくさんのスポット４選](https://feature.cozre.jp/63721)|0.8591745 |
|33949|[冬休みおでかけ必見！山梨の人気工場見学といえば桔梗屋の信玄餅](https://feature.cozre.jp/33949)|0.8464964 |
|43002|[人気&定番のおでかけ！親子で学べる自動車の工場見学４選｜西日本編](https://feature.cozre.jp/43002)|0.8333704 |

### その６

| id | title | score |
|:----|:------|:---------|
|63721|[千葉の工場見学はすごい！親子でうれしい体験がたくさんのスポット４選](https://feature.cozre.jp/63721)|0.5928047 |
|43002|[人気&定番のおでかけ！親子で学べる自動車の工場見学４選｜西日本編](https://feature.cozre.jp/43002)|0.56401855|
|64369|[ワクワク体験が待っている！子連れでドキドキ工場見学にゴー｜愛知](https://feature.cozre.jp/64369)|0.55624   |
|59128|[【コメタパン育児絵日記（7）】1歳児が抱えきれるボールの数は・・・](https://feature.cozre.jp/59128)|0.5017484 |
|64126|[大人も子どもも思わず夢中になってしまう工場見学のススメ４選｜大阪](https://feature.cozre.jp/64126)|0.48392826|

### その７

| id | title | score |
|:----|:------|:---------|
|6905 |[国会議事堂、皇居を見学しよう♪](https://feature.cozre.jp/6905)|1.1300747 |
|64369|[ワクワク体験が待っている！子連れでドキドキ工場見学にゴー｜愛知](https://feature.cozre.jp/64369)|0.94125086|
|63721|[千葉の工場見学はすごい！親子でうれしい体験がたくさんのスポット４選](https://feature.cozre.jp/63721)|0.8591745 |
|33949|[冬休みおでかけ必見！山梨の人気工場見学といえば桔梗屋の信玄餅](https://feature.cozre.jp/33949)|0.8464964 |
|43002|[人気&定番のおでかけ！親子で学べる自動車の工場見学４選｜西日本編](https://feature.cozre.jp/43002)|0.8333704 |

## 感想

* 「見学」というtermが含まれる記事のスコアが高めに算出されている印象
 * min_term_freqあたりを調整した方がいいかも
 * 特にtitleは同じtermが2回以上使われることが少ない
* titleでmin_term_freq=1にする場合は、stop_wordsも指定した方がいいかもしれない
 * 例えば「子ども」とか、よくtitleに使われるけど、類似性の判断に使うのは微妙なので
* fieldsにtagsを指定したものが機能していない
 * これもmin_term_freqを調整しないと。tagsで同じtermが重複することはまずない
* 類似検索としては正しいけど、「おすすめ記事」としてはそのままは使えなさそう
 * 感覚的に、掃除機を買ったら別の掃除機をおすすめされている感じがする
* 今回は件数を5件に絞ってしまったけど、実際使うにはスコアで足切りした方がいいかも

ということで、今度は[パラメータを調整しての検証](http://qiita.com/nabe/items/2b65679d467d39f6f5ff)をやってみたいと思います。

## さいごに

More Like This Queryの結果をそのまま「おすすめ記事」として組み込むというよりは、得られたスコアをベースにして、別の方法で算出したスコアと組み合わせてユーザ毎の「おすすめ記事」を生成しようかなと思っています。
More Like This Query自体のチューニング、別のスコア算出方法の検討、スコアの組み合わせ方のチューニングなど、まだまだサービスに導入するには色々ありそうです。