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の違い

実際の検索

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-FileExport-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あるじゃん!という話

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に変換する方法