PowerShellでElasticsearch検索する(Invoke-RestMethod)

# PowerShellでElasticsearch検索する

Elasticsearchに投入したデータをなんやかや検索したり集計したりする場合の基本的なところ。
今回は検索のみで集計(aggregations)の話は無し。


## 前提

本記事はlocalhostのつもりで書く。外部から投げる場合は、投げつけ先のElasticsearchが外部からの問合せに対応していること。yml書いたりFirewall開けたり。X-Pack等でセキュリティ設定している場合はそれに合わせて。


## curlとInvoke-RestMethod

JSONを投げてJSONを返してもらうのがElasticsearchの基本。
こういう操作で使うのは、Linuxならおなじみ`curl`。
PowerShellでは`Invoke-RestMethod`コマンドレットを使う。

似たコマンドレット`Invoke-WebRequest`のことを「PowerShell版curlです」と説明してる例をしばしば目にするが、実際に使ってみると全然違う。エイリアスになってるのが邪魔なくらい。
curl・wgetとInvoke-RestMethod・Invoke-WebRequestは全く異なるものとして新たに覚えるほうが、変な思い込みが元のトラブルを避けられて良い。

[curl, wget そしてInvoke-WebRequestの違い](https://qiita.com/tatsuki1112/items/dec473f93ca1bda239fb)


## 実際の検索

### JSON

```JSON:json.json
{
  "size": 3,
  "query": {
    "bool": {
      "must": [
        {"range": {"requestedAt": {"to": "now-1d/d","from": "now-1d/d"}}},
        {"range": {"queryTime": {"gte": 5000}}}
      ]
    }
  },
  "_source": ["queryTime","requestedAt","searchWord"],
  "sort": [{"queryTime": {"order": "desc"}}]
}
```

対象は検索ログ。クエリタイム遅かったものを探したい。
昨日一日の検索ログから、クエリタイム5000ms以上かかったものを検索。
クエリタイム・リクエスト時刻・サーチワードを選択。
結果はクエリタイム遅い順3件表示。

なおタイムゾーン指定は省略している。Elasticsearchの格納データは基本UTCなので、日本時間と+09:00ずれる。問題になる場合は自分で操作すること。(そんなことはアプリケーション側でやれというのがElasticsearchの言い分のようだ)


### PowerShell版

```Post_Search.ps1
# Elasticsearchに問い合わせ
$QueryParam = @{
  ContentType = "application/json"
  Method      = "Post"
  uri         = "http://localhost:9200/search_log/_search/"
  InFile      = ".\json.json"
}
$resp = Invoke-RestMethod @QueryParam

# 結果をコンソールに表示
$resp | ConvertTo-Json -Depth 4

# ファイル出力する
$resp | ConvertTo-Json -Depth 4 | Out-File -LiteralPath .\output.txt -Encoding default

```

現verのElasticsearchではContentType指定が必須なので必ず書く。
別ファイルから読み込むときは`InFile`パラメータ、ベタ貼りや`Get-Content`で読み込むときは`Body`パラメータ。この程度のJSONならベタ貼りでもいいけど、今回はこの後のcurl.exe版比較のため書き方を合わせて別ファイルにした。

取得結果は変数に格納しておくと後であれやこれやするのが楽。
PowerShellらしく`Select-Object | Format-*`で表示したり、`ConvertTo-JSON`で丸ごと表示したり、組み立てて`Out-File`や`Export-Csv`でファイル出力したり。



単に表示だけでよくて、ワンライナーなら↓こんなの。

```Invoke-RestMethod -ContentType "application/json" -Method "Post" -uri "http://localhost:9200/search_log/_search/" -InFile ".\json.json" | ConvertTo-Json -Depth 4```


### curl版

Windows版curl.exeを使うならこんな↓感じ。
別途インストールするものがある他、Windows10 ver1803以降には標準で入っているらしい。→[Windows10にcurlあるじゃん！という話](https://qiita.com/tatsuki1112/items/c230bf3ca4c7e654a10a)

```curl -H "Content-Type: application/json" -XPOST "localhost:9200/search_log/_search?pretty" -d @json.json```

ちなみにJSONを別ファイルにせずワンライナーで書こうとすると、エスケープ無限地獄に落ちるのでよそう。

```\{\"range\":\{\"queryTime\":\{\"gte\":5000\}\}\} # ～以下略```

か、書きたくない～。


## Invoke-RestMethodとcurl.exeどちらを使うか

Windows系なら、どうしてもcurl.exeでなければならない理由があるのでなければInvoke-RestMethodのほうが便利、なはず。
なんたってオブジェクトの形で返ってくるので、後からいくらでも操作し放題だから。curl.exeだと文字列で帰ってくるので、後でJSON読み込んで操作できる何かに渡さないとならない。だったら最初からPowerShell使えばいいよね。(自力で文字列切り張りなんていう非人道的行為は止そう)



## 応用

問合せのJSONは、連想配列で作って`ConvertTo-JSON`で変換する方法もあり。どっかからパラメータ貰ってきてJSONにして検索とか。JSONを文字列で組み立てるよりはるかに健康的だと思う。
[ハッシュからJSON,JSONからPSCustomObjectに変換する方法](https://win.just4fun.biz/?PowerShell/%E3%83%8F%E3%83%83%E3%82%B7%E3%83%A5%E3%81%8B%E3%82%89JSON%2CJSON%E3%81%8B%E3%82%89PSCustomObject%E3%81%AB%E5%A4%89%E6%8F%9B%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95)