@johtaniの日記 2nd

@johtani ‘s blog 2nd edition

Validate APIの利用

久しぶりに翻訳ではないブログを。書こうと思いながらかけてなかったので。。。

今回はvalidate APIの紹介です。

背景

ElasticsearchのクエリはQuery DSLというJSONで クエリを定義できるものを提供しています。 これは、様々なクエリフィルタを定義するために必要です。

自分の望んでいる条件を記述するために、JSONのネストと格闘することも必要となります。。。 また、クエリ、フィルタには様々なパラメータが用意されています。 これらのパラメータをすべて覚えるのは無理でしょうし、タイプミスなどもありますよね。 タイプミスやカッコのミスマッチなどで格闘して1時間が経過してしまったなどもあると思います。

そんな時に便利なAPIとして用意されているのがvalidate APIです。

利用方法

APIが用意されています。

1
http://ホスト名:ポート番号/インデックス名/タイプ名/_validate/query

インデックス名タイプ名は省略可能ですが、マッピングが異なると思うので、タイプ名まで指定するほうが良いと思います。 上記のAPIに対してクエリを送信するだけです。

クエリの確認

たとえば、こちらのGistにあるようなマッピングのインデックスに対して 検索クエリを組み立てていて、エラーが出るとします。 ※このクエリはmatch_allのところをmatch_alと、lが1文字足りないクエリになっています。

検索クエリのリクエスト(エラーあり)

1
2
3
4
5
6
GET pref_aggs/_search
{
  "query": {
    "match_al": {}
  }
}

実行結果のレスポンス

1
2
3
4
{
   "error": "SearchPhaseExecutionException[Failed to execute phase [query], all shards failed; shardFailures {[rwkb01chTZq2V7FD0Tlwrw][pref_aggs][0]: SearchParseException[[pref_aggs][0]: from[-1],size[-1]: Parse Failure [Failed to parse source [{\n  \"query\": {\n    \"match_al\": { }\n  }\n}\n]]]; nested: QueryParsingException[[pref_aggs] No query registered for [match_al]]; }{[rwkb01chTZq2V7FD0Tlwrw][pref_aggs][1]: SearchParseException[[pref_aggs][1]: from[-1],size[-1]: Parse Failure [Failed to parse source [{\n  \"query\": {\n    \"match_al\": { }\n  }\n}\n]]]; nested: QueryParsingException[[pref_aggs] No query registered for [match_al]]; }]",
   "status": 400
}

とこんなかんじで、エラーが帰っては来るのですが、非常に読みづらいです。

そこで、validate APIを利用します。 リクエスト先を/_searchから/_validate/queryに変更します。

validate API

1
2
3
4
5
6
GET pref_aggs/_validate/query
{
  "query": {
    "match_al": {}
  }
}

validate APIのレスポンス

1
2
3
4
5
6
7
8
{
   "valid": false,
   "_shards": {
      "total": 1,
      "successful": 1,
      "failed": 0
   }
}

すると、非常にシンプルな結果が返ってきます。 "valid": falseとなっているため、クエリに問題があることがわかります。

エラーの詳細

問題がある事自体はわかりましたが、エラーの内容も知りたいですよね? その場合は、explainというパラメータを追加します。 (正しくはexplain=trueを追加しますが、=trueを省略可能です。)

validate API(explainあり、クエリ自体は省略)

1
2
GET pref_aggs/_validate/query?explain
{...}

validate APIのレスポンス

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
   "valid": false,
   "_shards": {
      "total": 1,
      "successful": 1,
      "failed": 0
   },
   "explanations": [
      {
         "index": "pref_aggs",
         "valid": false,
         "error": "org.elasticsearch.index.query.QueryParsingException: [pref_aggs] No query registered for [match_al]"
      }
   ]
}

explanationsという項目が追加されました。 ここにerrorという項目として、エラーの詳細が返ってきます。_searchの時よりも見やすいですね。 今回のエラーは、match_allが正しいクエリですの、match_alというクエリは登録されていないというエラーでした。 では、クエリを修正して実行しましょう。

validate API(エラー無し)

1
2
3
4
5
6
GET pref_aggs/_validate/query?explain
{
  "query": {
    "match_all": {}
  }
}

validate APIのレスポンス

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
   "valid": true,
   "_shards": {
      "total": 1,
      "successful": 1,
      "failed": 0
   },
   "explanations": [
      {
         "index": "pref_aggs",
         "valid": true,
         "explanation": "ConstantScore(*:*)"
      }
   ]
}

今度はクエリに問題はありません。"valid": trueです。 そして、explanationsの項目には、errorの代わりにexplanationという項目が返ってきました。 これが、実際にElasticsearch内部で実行されるクエリになります。

実際のクエリに利用される単語の確認

この機能はこの他に、クエリの解析にも利用できます。 思ったとおりに検索にヒットしない場合があって、困ったことはないですか? フィールドに指定されたアナライザによっては、単語を変形したりするものが存在します。

サンプルマッピング

1
2
3
4
5
6
7
8
9
10
11
12
PUT /validate_sample
{
  "mappings": {
    "several_analyzer": {
      "properties": {
        "title": {"type": "string"},
        "body_ja": {"type": "string", "analyzer": "kuromoji"},
        "body_en": {"type": "string", "analyzer": "english"}
      }
    }
  }
}

例えば、このようにkuromojienglish、デフォルト(standard)アナライザを利用したマッピングがあるとします。 このフィールドに対してpowerfulという単語で検索したとします。

validate API

1
2
3
4
5
6
7
8
9
GET /validate_sample/_validate/query?explain
{
  "query": {
    "multi_match": {
      "fields": ["body_en","body_ja","title"],
      "query": "powerful"
    }
  }
}

この場合、レスポンスは次のとおりです。

validate APIのレスポンス

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
   "valid": true,
   "_shards": {
      "total": 1,
      "successful": 1,
      "failed": 0
   },
   "explanations": [
      {
         "index": "validate_sample",
         "valid": true,
         "explanation": "(title:powerful | body_en:power | body_ja:powerful)"
      }
   ]
}

titlebody_jaについては入力された単語がそのままクエリとして利用されています。 body_enについては、powerという単語に変換されて実行されています。 これは、englishアナライザがステミングを行った結果がクエリとして利用されるという意味です。 また、powerful秋葉原といった日本語に変更して実行すると次のようになります。 日本語はstandardアナライザなどでは、1文字ずつ区切られてしまうことがわかります。

validate APIのレスポンス

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
   "valid": true,
   "_shards": {
      "total": 1,
      "successful": 1,
      "failed": 0
   },
   "explanations": [
      {
         "index": "validate_sample",
         "valid": true,
         "explanation": "((title:秋 title:葉 title:原) | (body_en:秋 body_en:葉 body_en:原) | ((body_ja:秋葉 body_ja:秋葉原) body_ja:原))"
      }
   ]
}

このように、クエリの単語がどのような単語に変換されてクエリに利用されているかなども知ることが可能です。

また、クエリを組み立てて、ヒットするはずが、0件となってしまうという場合にも、どのようなクエリが組み立てられているかを確認するという点で、 validate APIが役立ちます。 検索がヒットするが、望んだクエリになっていないのでは?という場合は_search APIexplainパラメータを 利用すれば、クエリの構成がわかるのですが、検索結果が0件の場合はクエリの構成は表示されません。

解決できない問題は?

便利なvalidate APIですが、以下の問題に対しては残念ながら確認できません。

  • query以外の項目のvalidate不可
    • たとえば、_search APIsizeなどの項目についてはチェックできないです。
  • 存在しないフィールドの指定
    • 上記validate_sampleのマッピングの例でクエリにbody_engという存在しないフィールドを指定してもエラーとはなりません。

まとめ

書いたクエリがうまく動かない、JSONのタグがおかしいといった場合は、 まずはこのvalidate APIで確認してみるのがオススメです。

Comments