Elasticsearch でつまづいた話 (2)

今日のテーマは複数の Nested Field があるときの Aggregations 。

Nested Field とは

日本語での説明は このあたり に譲るが、簡単に言うと親子関係が維持された Array である。データの見た目は Array と同じだ。

  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]

クエリはこんな感じになる。

{
   "query" : {
      "nested" : {
         "path" : "user",
         "query" : {
            "match" : {
               "user.first" : "John"
            }
         }
      }
   }
}

Array だったら↓で済むので少し複雑になっている。

{
   "query" : {
      "match" : {
         "user.first" : "John"
      }
   }
}

Aggregation ならこうなる。

{
   "aggs" : {
      "nested_by_resellers" : {
         "nested" : {
            "path" : "resellers"
         },
         "aggs" : {
            "aggs_by_resellers_id" : {
               "terms" : {
                  "field" : "reseller.id"
               }
            }
         }
      }
   }
}

Nested Field が複数ある場合もある。

{
  "mappings": {
    "product" : {
        "properties" : {
            "resellers" : { 
                "type" : "nested",
                "properties" : {
                    "id" : { "type" : "keyword" },
                    "name" : { "type" : "text" },
                    "price" : { "type" : "double" }
                }
            }, 
            "sources" : {
               "type" : "nested",
               "properties" : {
                  "id" : { "type" : "keyword" },
                  "name" : { "type" : "text" }
               }
            }
        }
    }
  }
}

このとき resellersid で集約し、更に sourcesid でも集約したい場合どうするか。 これが今回のつまづいた話だ。 たとえば↓のように書きたくなるが、これではうまくいかない。

{
   "aggs" : {
      "nested_by_resellers" : {
         "nested" : {
            "path" : "resellers"
         },
         "aggs" : {
            "aggs_by_resellers_id" : {
               "terms" : {
                  "field" : "reseller.id"
               },
               "aggs" : {
                  "nested_by_sources" : {
                     "nested" : {
                        "path" : "sources"
                     },
                     "aggs" : {
                        "aggs_by_sources_id" : {
                           "terms" : {
                              "field" : "sources.id"
                           }
                        }
                     }
                  }
               }
            }
         }
      }
   }
}

なぜか? reseller の下に sources がいないからだ。そりゃそうだ。

じゃどうするか。 reverse nested というものを使う。これによって nested を抜け出すことができる。

{
   "aggs" : {
      "nested_by_resellers" : {
         "nested" : {
            "path" : "resellers"
         },
         "aggs" : {
            "aggs_by_resellers_id" : {
               "terms" : {
                  "field" : "reseller.id"
               },
               "aggs" : {
                  "reverse" : {
                     "reverse_nested" : {},
                     "aggs" : {
                        "nested_by_sources" : {
                           "nested" : {
                              "path" : "sources"
                           },
                           "aggs" : {
                              "aggs_by_sources_id" : {
                                 "terms" : {
                                    "field" : "sources.id"
                                 }
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
      }
   }
}

reverse_nested を挟むことによって、 resellers の下にいた状態から抜け出して、更に sources の下に入ることができた。 めでたしめでたし。

... とはいえ、複雑な json だよなぁ。

参考 :

Reverse nested Aggregation | Elasticsearch Reference [6.0] | Elastic