NEST Tips - Elasticsearch .NET Client

# 0. まえがき
- 川崎フロンターレ、J1初優勝おめでとうございます！等々力に行きたかったです。
- [FUJITSU Advent Calendar 2017](https://qiita.com/advent-calendar/2017/fujitsu) の 3日目の記事です。
- 遅刻しました。ごめんなさい。
- 記事は全て個人の見解であり、執筆内容は執筆者自身の責任です。所属する組織は関係ありません。
- .NETの言語でElasticsearch APIを叩く、なんてことをする人が少ないのかも知れませんが、[Elasticsearch公式ドキュメント](https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/introduction.html)を読んでも書き方がイマイチわからず、Google先生に何度も尋ねていくうちに培った内容を残そうと思い、本記事を書こうと思った次第です。
- nugetからインストール後、usingしてください。C#erなら言われずともわかることなので詳細は割愛します。
- 私が利用したクラスやメソッドを中心に記載しています。すべてを網羅している訳ではありません。

# 1. 接続編
この辺は公式ドキュメントでもわかる内容なので簡単に。
[Getting Started](https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/nest-getting-started.html)

## 1. インスタンスの初期化 
```C#:sample.cs
var client = new ElasticClient();
var client = new ElasticClient(settings);
```

引数にElasticsearchのエンドポイントとか接続設定とかを渡すことができるので、接続編に初期化を入れています。

## 2. 接続設定 `ConnectionSettings`
### 1. 基本形
```C#:sample.cs
var settings = new ConnectionSettings(new Uri("http://<host>:<port>"));
```

### 2. 主なメソッド
#### BASIC認証 `BasicAuthentication`
```C#:sample.cs
var settings = new ConnectionSettings(new Uri("http://<host>:<port>"))
    .BasicAuthentication("<username>", "<password>");
```
`password`は平文を渡す必要があるので、工夫しましょう。

#### Debug Logを残す `DisableDirectStreaming`
```C#:sample.cs
var settings = new ConnectionSettings(new Uri("http://<host>:<port>"))
    .DisableDirectStreaming();
```
Documentを登録するときのログとして、Debugレベルのログを残したい場合に重宝します。
Request BodyとResponse Bodyを見ることができます。
このメソッド名だとイマイチわからないですね。。。

# 2. Cat API
Elasticsearchを利用されたことのあるかたなら、誰もが叩いたことのあるだろうCat APIです。
こちらはマニュアルが無くとも使い方はわかりやすいです。

## 1. Elasticsearchの状態の取得 `CatHealth()`
```C#:sample.cs
// 単一ホストのクラスタの場合
var health = client.CatHealth().Records.SingleOrDefault();
// 複数ホストのクラスタの場合
var health = client.CatHealth().Records;
```
`Records`で`/_cat/health`の結果がKVP配列で返ってくると考えていただければ大丈夫です。
単一ホストのクラスタであれば1行しか返ってきませんので`SingleOrDefault()`しましょう。
その後`health.Status`してやれば`Green`やら`Yellow`やらが取得できます。

## 2. Indexの存在確認 `CatIndices()`
```C#:sample.cs
var index = client.CatIndices().Records.Where(e => e.Index == "<index name>").SingleOrDefault();
```
こちらも`/_cat/indices`の結果がKVP配列で返ってきます。
ラムダ式が使えるので楽ちんですね。

# 3. Index API
Indexの作成やら、ドキュメントの作成／更新ができる万能APIですね。
ドキュメントは1つずつしか作成できませんが。
私はIndexの作成だけで利用しました。
`CatIndices()`で存在確認して、無ければ作る、みたいな。

```C#:sample.cs
var result = client.CreateIndex("<index name>");
```
これだけです。
これのプロパティである`Acknowledged`に真偽値が入っています。
エラー処理等で使えますね。

Index Name以外にも引数を渡せたような気がします。

# 4. Bulk API
ドキュメントの作成／更新はBulk APIの方が処理が早いし、大量なドキュメントでも対応できるので、実質これ以外使わないと思います。
そして、私が今回一番ハマったところでもあります。

```C#:sample.cs
var res = client.Bulk(e => e
                    .Index("<index name>")
                    .Type("<type name>")
                    .IndexMany(docList));
```
こうしてみると「なんだ、簡単じゃん」と思うかも知れません。
私のハマりポイントをご紹介します。

## IDってどうやって指定するの？
上述の`Bulk()`メソッドですが、引数に`Index()`、`Type()`、`IndexMany()`の3つを渡していますね。
＃`IndexMany()`はドキュメント配列を全部Index(登録／更新)しますよ、というものです。

他にもいくつか渡せるのですが、その中に`Id()`とかこれに類するものがありませんでした。
ElasticsearchにおいてID指定は重複排除を行う上でとても大事です。

当プログラムでは、ドキュメントとして渡す文字列やオブジェクトのハッシュ値を求めて、それをIDとしていました。
1つのドキュメントだけを登録／更新する場合の`Index()`メソッドであれば、`Id()`メソッドが用意されていて、ここにハッシュ値を渡せるのです。
このままIDを指定せずに実行すると、整数が順番に振られます。重複排除なんてできませんね。

この`Bulk()`メソッドにおけるID管理ってどうするんだ・・・？ってところでハマりました。

結論としては、こうなります。

```C#:sample.cs
[ElasticsearchType(IdProperty = "DocumentId")]
class DocumentClass
{
    public string DocumentId { get; set; }
    ...
}
```
ドキュメントとして登録する際の型として用意したクラスに、`[ElasticsearchType(IdProperty = "DocumentId")]`を宣言してあげる必要があります。
ここで指定している`DocumentId`はクラス内で宣言しているプロパティのどれでも構いません。

`Bulk()`に渡すドキュメント配列を作る際に、この例でいう`DocumentId`にハッシュ値を渡してあげれば、ドキュメントごとに異なるIDが振られ、重複していればUpdate、無ければCreateしてくれます。

```C#:sample.cs
var docList = new List<DocumentClass>();
foreach (var hoge in hogeList)
{
    docList.Add(new DocumentClass
        {
            DocumentId = CreateSHA256Hash(hoge),
            ...
        }
    );
}
```
`foreach`でドキュメントの文字列配列やオブジェクト配列を回して、個々のドキュメントのハッシュ値を計算して(`CreateSHA256Hash()`というメソッドを別途作っています)、`DocumentId`に突っ込む、ということをしてあげれば、ドキュメントごとに異なるIDが振られる、というロジックです。

ここに至るまで調べに調べました。。。Stack Overflowにヒントがありました。
そのページをブックマークしておけば良かったのですが、解決した喜びでページを閉じてしまったので紹介できず。。。申し訳ない。。。

# 5. おわりに
- まんべんなく利用した訳ではありませんので、紹介している使い方が少なくて申し訳ありません。もし、他にもハマったポイント等出てきましたらUpdateしていきます。
- また、ハマったことがあるよーという方がおられましたら、コメントにて残していただければ、当方にて実際に試してみて、Updateしたいと思います。