はてだBlog(仮称)

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

Elasticsearch AggregationsとPost filter(post_filter)

一つ前のこちら↓の記事で、ElasticsearchのAggregationsのシンタックス例を挙げてみました。ElasticsearchのAggregationsでしばしば話題になるトピックを忘れていましたので、補足します。

itdepends.hateblo.jp

具体的には、Post filterの話です。

www.elastic.co

Aggregationsはいくつか用途がありますが、直接的な用途の一例としては、次の図のように検索結果のドリルダウンなどのための絞り込みインタフェースの提供です。

f:id:azotar:20200602210159p:plain

では、Post filterとはなんぞやという話です。

上図では、検索結果の「東京都」をさらに絞り込むような例でしたが、サービスや提供したい検索IFによっては、横移動(DWHなどの世界観ではダイス?)させたい場合があります。

同じく、レストラン検索の例でいうと、東京都で検索させて、検索結果リストは東京都のフレンチの一覧としつつも、エリアの絞り込み(実際はエリアの移動)は、神奈川県や千葉県、埼玉県といった、東京都と同じ階層の住所の条件を変更できるようなUX/UIとしたいという例になります。

このような、複数の検索条件による検索結果(hits)を得たいが、一方で、ある検索条件は使わず、残りの条件のもとでのAggregationsを取得したいという用途には対応できるのでしょうか?

はい、できますというのがPost filterになります。

f:id:azotar:20200602210817p:plain

クエリの実例

データセットlivedoorレストランデータセットです。 ここではデータの深追い説明はしませんが、次の項目があるとして、例を示します。

  1. pref.raw そのレストランの住所の都道府県
  2. cates.raw そのレストランのカテゴリ/ジャンル
  3. area_name.raw そのレストランの所在地を表すエリア名(全国を約200程度のエリアに分割しているようです)

先に、前絞り(非Post filter)を示して、その後、後絞り(Post filter)の例を示します。

(1) 前絞り(非Post filter)

Post filterを使わない、どちらかといえば、こちらが基本の方法かと思いますが、対比のために示します。

クエリ例

東京都のフレンチを検索し、得られた結果を「エリア」ごとに件数分類する。

ポイントとしては、前絞りの例に比べると、後絞り扱いにしたい検索条件を「post_filter」にぶら下げるシンタックスとなります。


POST /ldgourmet/_search?filter_path=agg*.*.buc*,hits.total
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "cates.raw": {
              "value": "フレンチ"
            }
          }
        },
        {
          "term": {
            "pref.raw": {
              "value": "13__東京都"
            }
          }
        }
      ]
    }
  },
  "aggs": {
    "a1": {
      "terms": {
        "field": "area_name.raw",
        "size": 20
      }
    }
  }
}

検索結果

{
  "hits" : {
    "total" : 1816
  },
  "aggregations" : {
    "a1" : {
      "buckets" : [
        {
          "key" : "六本木・麻布・広尾・白金",
          "doc_count" : 219
        },
        {
          "key" : "銀座、新橋、有楽町",
          "doc_count" : 169
        },
        {
          "key" : "東急沿線",
          "doc_count" : 158
        },
        {
          "key" : "代官山、恵比寿-五反田",
          "doc_count" : 113
        },
        {
          "key" : "原宿・表参道・青山",
          "doc_count" : 106
        },
        {
          "key" : "赤坂・溜池山王",
          "doc_count" : 95
        },
        {
          "key" : "四ツ谷-飯田橋・神楽坂",
          "doc_count" : 92
        },

....

東京都の配下のエリアごとのレストラン件数が得られています。

また、今回は検索結果データは折りたたみにしていますが、hits.totalが1816件となっており、「東京都のフレンチ」の件数が、1816件となっているというデータが得られたということがわかります。

(2) 後絞り(Post filter )

東京都のフレンチを検索する。また、全国のフレンチのお店のうち、都道府県ごとの件数一覧を取得する。

クエリ例

POST /ldgourmet/_search?filter_path=agg*.*.buc*,hits.total
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "cates.raw": {
              "value": "フレンチ"
            }
          }
        }
      ]
    }
  },
  "aggs": {
    "a1": {
      "terms": {
        "field": "pref.raw",
        "size": 47
      }
    }
  },
  "post_filter": {
    "term": {
      "pref.raw": {
        "value": "13__東京都"
      }
    }
  }
}

検索結果

{
  "hits" : {
    "total" : 1816
  },
  "aggregations" : {
    "a1" : {
      "buckets" : [
        {
          "key" : "13__東京都",
          "doc_count" : 1816
        },
        {
          "key" : "27__大阪府",
          "doc_count" : 421
        },
        {
          "key" : "14__神奈川県",
          "doc_count" : 314
        },
        {
          "key" : "28__兵庫県",
          "doc_count" : 256
        },
        {
          "key" : "26__京都府",
          "doc_count" : 223
        },
        {
          "key" : "40__福岡県",
          "doc_count" : 199
        },
        {
          "key" : "23__愛知県",
          "doc_count" : 197
        },
        {

得られるhits.totalは1816件で、前絞り版のクエリのものと同じになっていることに注目ください。

一方、aggsの情報として得られるのは、全国のフレンチの都道府県ごとの件数になっていますね。