はてだBlog(仮称)

私的なブログど真ん中のつもりでしたが、気づけばWebサイト系のアプリケーション開発周りで感じたこと寄りの自分メモなどをつれづれ述べています。2020年6月現在、Elasticsearch、pandas、CMSなどに関する話題が多めです。...ですが、だんだんとより私的なプログラムのスニペット置き場になりつつあります。ブログで述べている内容は所属組織で販売している製品などに関するものではなく、また所属する組織の見解を代表するものではありません。

ElasticsearchのSearchTemplate

Elasticsearchには、Search Templateという、よく使うクエリをElasticsearch自体に登録して、それをテンプレートにして検索クエリの一部を差し替えたような検索ができます。

使い所によって、共通化、タイプ量削減、設計方針の強制、コンセプトの共有、いろいろな捉え方ができると思います。

テンプレートのフォーマット指定は、mustache風です。

f:id:azotar:20200526222419p:plain

www.elastic.co

私の中では、存在は知っていたものの、(後半に示しているJSONをパラメータに受け取ってそのままプレースホルダーにはめ込みできるというところが特に)再発見でしたので、勉強メモとしてこちらにサンプル例を書き連ねてみます。

確認に使ったElasticsearchのバージョンは ver6.8です。

もっともシンプルな例

こうやって差し代わるのねという例。


# 「ID123」のテンプレートを登録

GET _scripts/ID123
{
  "script": {
    "lang": "mustache",
    "source": {
      "query": {
        "match_all": {}
      },
      "size": "{{my_size}}"
    }
  }
}

↓ (登録成功)

{
  "acknowledged" : true
}

↓ 実際に検索してみる


# 「ID123」のテンプレートを下敷きに、「my_size」プレースホルダーに「5」を設定して、検索する。


GET 検索したいインデックス名/_search/template
{
    "id" : "ID123",
    "params" : {
        "my_size" : 5
    }
}


↓ (これと同等)



GET 検索したいインデックス名/_search
{
   "query": {
        "match_all": {}
      },
      "size": 5
}


↓

★★★検索結果が戻る★★★

いろいろな例

以下、サーチテンプレートのkibana登録とそれを使った検索クエリの2つを対にして示しています。 (途中、検索シナリオとして見ると、リアリティの無い例も入っています。)

001(オーソドックス:検索ワードの当てはめ)

POST _scripts/my_searchTmpl001
{
    "script": {
        "lang": "mustache",
        "source": {
            "query": {
                "match": {
                    "name": "{{query_string}}"
                }
            }
        }
    }
}


GET /_search/template
{
  "id":"my_searchTmpl001",
  "params":{
    "query_string": "六本木"
  }
}

002 (001と同じだが、複雑なクエリも取り扱えるらしいことの再確認)


POST _scripts/my_searchTmpl002
{
  "script": {
    "lang": "mustache",
    "source": {
      "query": {
        "bool": {
          "must": {
            "match": {
              "name": "{{query_string}}"
            }
          }
        }
      }
    }
  }
}


GET /_search/template
{
  "id":"my_searchTmpl002",
  "params":{
    "query_string": "六本木"
  }
}

003 (プロパティ名やクエリDSLのキモになるシンタックス寄りのプロパティ名も外から与えられる)

POST _scripts/my_searchTmpl003
{
  "script": {
    "lang": "mustache",
    "source": {
      "query": {
        "bool": {
          "must": {
            "{{prop}}": {
              "name": "{{query_string}}"
            }
          }
        }
      }
    }
  }
}


GET /_search/template
{
  "id":"my_searchTmpl003",
  "params":{
    "query_string": "六本木",
    "prop":"match"
  }
}

004(mustacheなので、ループっぽいこともできなくない)

POST _scripts/my_searchTmpl004
{
  "script": {
    "lang": "mustache",
    "source": {
      "query": {
        "bool": {
          "must": {
            "match": {
              "name": " {{#lp}}{{val}} {{/lp}}"
            }
          }
        }
      }
    }
  }
}


GET /_search/template
{
  "id":"my_searchTmpl004",
  "params":{
    "lp":[
        {"val":"秋葉原"},
        {"val":"渋谷"},
        {"val":"品川"},
        {"val":"新宿"}
      ]
  }
}



005(ループのシンタックスシュガー的な例)



POST _scripts/my_searchTmpl005
{
  "script": {
    "lang": "mustache",
    "source": {
      "query": {
        "bool": {
          "must": {
            "{{prop}}": {
              "name": " {{#lp}}{{.}} {{/lp}}"
            }
          }
        }
      }
    }
  }
}


GET /ldgourmet/_search/template
{
  "id":"my_searchTmpl005",
  "params":{
    "prop":"match",
    "lp":[
        "秋葉原",
        "渋谷",
        "品川",
        "新宿"
      ]
  }
}

006(source部分を文字列とすると、もっとトリッキーなことができる。もちろん、その場でエラーに気づきにくくはなる。また、「toJson」というEsオリジナルのマジック命令がある。)

POST _scripts/my_searchTmpl006
{
  "script": {
    "lang": "mustache",
    "source": """{
      "query": {
       "bool": {
          "must": {{#toJson}}my_match_all{{/toJson}}
        }
      }
    }"""
  }
}


GET /ldgourmet/_search/template
{
  "id":"my_searchTmpl006",
  "params":{
    "my_match_all":[{
      "match_all":{}
    }]
  }
}

006_2(個人的にはこういう「カタチ」のものをkibanaでPoCの時にSearch Template活用するのかなという例)

POST _scripts/my_searchTmpl006_2
{
  "script": {
    "lang": "mustache",
    "source": """
    {
      "query": {
       "bool": {
          "must": [
                 {
                   "match": {
                    "address":{
                        "query": "{{area}}",
                        "operator": "or"
                     }
                   }
                 },
                 { 
                   "match": {
                     "stas":{
                        "query": "{{area}}",
                        "operator": "or"
                     }
                   }
                }
          ]
        }
      }
    }
"""
  }
}


GET /ldgourmet/_search/template
{
  "id":"my_searchTmpl006_2",
  "params":{
    "area":"大崎 目黒 池袋"
  }
}

006_3(ループのネスト風)


POST _scripts/my_searchTmpl006_3
{
  "script": {
    "lang": "mustache",
    "source": """
    {
      "query": {
       "bool": {
          "must": [
            {{#my_searchFields}}
                 {
                   "match": {
                    "{{.}}":{
                        "query": "{{area}}",
                        "operator": "or"
                     }
                   }
                 },
           {{/my_searchFields}}
                 { "match_all": {} }
          ]
        }
      }
    }
"""
  }
}



GET /ldgourmet/_search/template
{
  "id":"my_searchTmpl006_3",
  "params":{
    "area":"大崎 目黒 池袋",
    "my_searchFields":[
           "name",
           "address",
           "stas"
      ]
  }
}

007(大きく骨組みのクエリDSLのカタチを定めておき、後からサブのクエリDSLを差し込む)

今までよく知らなかったけど、これはプロダクトの実際のクエリに使ってみたい気持ちとなった例。本当はあと一声なんだが、それをEsやmustacheに求めるのは筋違いかも。

POST _scripts/my_searchTmpl007
{
  "script": {
    "lang": "mustache",
    "source": """
    {
      "query": {
        "bool": {
          "must": {{#toJson}}arrayOfQueries{{/toJson}}
        }
      }
    }
"""
  }
}

GET /ldgourmet/_search/template
{
  "id":"my_searchTmpl007",
  "params":{
    "arrayOfQueries":[
                 {"match": {  "address":{ "query": "東京 大阪 名古屋"}}},
                 {"match": {  "stas":      { "query": "東京 大阪 名古屋"}}},
                 {"match": {  "name":    { "query": "東京 大阪 名古屋"}}}
      
      ]
  }
}