Elasticsearch利用まとめ2018

# はじめに
classiでは全文検索でElasticsearchを使用しています。今年を振り返って改めてclassiでの使われ方を紹介したいと思います。
ちなみに実装言語としてruby、フレームワークとしてRailsを使っています。mappingの定義やindexの定義の仕方はelasticsearch-rails(elasticsearch-model)での書き方で書いてます。ご了承ください。 :bow: 

# どのような使い方をしているか
classiでは主に以下の機能でElasticsearchを使っています。

- テスト、問題、アンケート、自分であげた画像に対するキーワード検索
- 先生・生徒のコミニケーション機能の各メッセージに対してのキーワード検索
- 動画コンテンツのキーワード検索

では順にどのような使い方をしているかを説明します。

## テスト、問題、アンケート、自分であげた画像に対するキーワード検索
ここでは検索対象のデータをjson形式にしてElasticsearchに登録しています(普通の使い方ですね)。versionもまだ2系( :bow: )なので、mappingも以下のような形（例)で定義しています。

``` 
indexes :id,  type: 'long', index: 'not_analyzed' 
indexes :name,  type: 'string', analyzer: 'kuromoji_analyzer', fields: { raw: { type: 'string', index: :not_analyzed } } 
indexes :owner_id, type: 'long'
indexes :sentence, type: 'string', analyzer: 'kuromoji_analyzer' 
```

versionが５系であれば、<code>type: 'string'</code>でなく<code>type: 'text'</code>になりますが、まだversion２系のままなので、<code>string</code>になっています。
また検索クエリーではキーワード検索のところで<code>simple_query_string</code>を使っています。

```
simple_query_string: 
  { query: "\"#{@condition_params[:keyword]}\"",
    fields: ['name', 'sentence'],
    default_operator: 'and',
  }
```

[ここ](https://www.elastic.co/guide/en/elasticsearch/reference/2.3/full-text-queries.html)にあるようにキーワード検索クエリにはいくつか種類がありますが、ファイル名にアンダーバーがある場合が結構あったため、<code>simple_query_string</code>を使うことにしました。

上でも書いている通りversionが２系なので、近々バージョアップをしていく予定です。その場合は、mappingの定義<code>string</code>と書いているところを<code>text</code>に変更するなど多少手直しが必要になりますが、バージョンを古いままにしておくわけにはいかないので、対応していくつもりです。

## 先生・生徒のコミニケーション機能の各メッセージに対してのキーワード検索
ここでも検索対象となる投稿本文のデータをjson形式にしてElasticsearchに入れています。データの形式はメッセージとそれに対するコメントが1対多になっています。検索時にはそのメッセージとコメントの中をみて検索キーワードに該当するものを抽出します。そのため今回はメッセージとコメントを一つのjsonに入れてElasticsearchに登録しています。
mappingの書き方としては、以下のようにします。

```例（一部抜粋)
indexes :id,             type: 'long'
indexes :body,           type: 'text', analyzer: 'kuromoji_analyzer'
indexes :comments, type: 'nested' do
  indexes :id,           type: 'long'
  indexes :body,         type: 'text', analyzer: 'kuromoji_analyzer'
end
```

nestedを使うことで1対多のデータを一つのjsonとしてElasticsearchに入れることができます。
検索時にはmessageのbodyとcommentsのbodyをそれぞれみて検索するようにします。こういう形式のデータでも手軽に検索できるのはElasticsearchのような検索エンジンの利点だと思います。

### バージョンアップ
この機能で使っているのはversionが5系です。最新は6系なのでバージョンアップをしなければなりません。Amaozon Elasticsearch Serviceでは簡単にアップデートできる仕組みを提供しています。

![スクリーンショット 2018-12-09 12.18.28.png](https://qiita-image-store.s3.amazonaws.com/0/1686/3e5eec05-7cfa-fec5-fb7f-e25ddf7bc649.png)

ここの青い枠で囲っている「ドメインのアップグレード」からElasticsearchのバージョンアップができます。自分が試して見たところノーメンテで出来ました。

## 動画コンテンツのキーワード検索
こちらは私が直接担当しているものではないのですが、以前mapping定義やqueryを見たところ検索時に各要素にscore(キーワードに対するマッチ度)を付けて検索結果の並び順を導き出しています。scoreの出し方はqueryを以下のように書いてやっています。例ですが、functionsの中に独自でscoreの計算方法を独自で設定する場合は定義していきます。

```
        query: {
          function_score: {
            score_mode: 'sum', # functionsないのスコアの計算方法
            boost_mode: 'multiply', # クエリの合計スコアとfunctionのスコアの計算方法
            query: {
              bool: {
                must: {~~~~~~},
              }
            },
            functions: [
              {
                field_value_factor: {
                  field: "field1",
                  factor: 2,
                  modifier: "square",
                  missing: 1
                },
                weight: 5
              },
              {
                field_value_factor: {
                  field: "field2",
                  factor: 3,
                  modifier: "sqrt", # squt: ルート
                  missing: 1
                },
                weight: 2
              }
            ]
          }
        },
```

これだと<code>field1</code>の値を２乗して<code>factor</code>の値をかけます。そしてその値にweightとして5をかけます。
もう一つの<code>field2</code>の値のルート値に<code>factor</code>の値をかけます。そしてweightとして2をかけます。
以上の値を足し合わせ、さらにquery実行時に出たscoreの値を全て<code>sum</code>（足し合わせる)することで検索結果で算出されるscoreが出ます。

以上です。

# 今後やりたいこと
来年やりたいこととして、まずは古いElasticsearchのバージョンを上げようと思っています。６系に揃えたいと思います。
個人的にはpluginを作って機能拡張をしてみたいという気持ちがあります。間に合えば今回の記事にそれを含めたいと思っていました。が、すみません、間に合いませんでした。
その件は後日また。