首页 > 解决方案 > Elasticsearch - 获取所有文档的所有嵌套对象

问题描述

让我们想象一下 Elasticsearch 索引,其中每个文档代表一个国家。Country 有cities字段,定义为nested

示例映射(为了本示例的简洁而简化):

{
  "properties": {
    "name": {
      "type": "text",
      "fields": {
        "keyword": {
          "type": "keyword",
          "ignore_above": 256
        }
      } 
    },
    "cities": {
      "type": "nested",
      "properties": {
        "name": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        }
        // other properties are omitted for brevity
      }
    }
  }
}

我插入索引的文档如下所示:

{
  "name": "Slovakia",
  "cities": [
    {
      "name": "Bratislava"
    },
    {
      "name": "Zilina"
    },
    ...
  ]
}

{
  "name": "Czech Republic",
  "cities": [
    {
      "name": "Praha"
    },
    {
      "name": "Brno"
    },
    ...
  ]
}

是否可以编写一个返回所有城市(所有国家)并支持排序和分页的查询?作为回应,我想要完整的嵌套对象 + 父对象的一些字段(这样我就可以显示该城市属于哪个国家/地区)。

返回的第一个页面(响应)将包含来自捷克共和国的 10 个城市,第二个页面将包含 10 个城市,其中四个(最后一个)来自捷克共和国,六个来自斯洛伐克。

我正在研究复合聚合,但我不知道如何将国家名称添加到sources

{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "nested_aggs": {
      "nested": {
        "path": "cities"
      },
      "aggs": {
        "by_name": {
          "composite": {
            "sources": [
              {
                "cityName": {
                  "terms": {
                    "field": "cities.name.keyword",
                    "order": "asc"
                  }
                }
              }
            ]
          }
        }
      }
    }
  }
}

是否可以在不修改 Elasticsearch 映射的情况下编写此类查询?

标签: elasticsearch

解决方案


复合聚合的所有成员都需要在相同的上下文中定义——你不能混合nested和非嵌套的上下文。

最简单的选择是首先对国家进行汇总,然后对城市进行汇总:

{
  "size": 0,
  "aggs": {
    "by_country": {
      "terms": {
        "field": "name.keyword",
        "size": 10
      },
      "aggs": {
        "nested_cities": {
          "nested": {
            "path": "cities"
          },
          "aggs": {
            "by_cities": {
              "terms": {
                "field": "cities.name.keyword",
                "size": 10
              }
            }
          }
        }
      }
    }
  }
}

如果您确实可以选择更改映射,则可以利用该include_in_root功能使您能够执行复合聚合,例如:

{
  "size": 0,
  "aggs": {
    "by_name": {
      "composite": {
        "sources": [
          {
            "countryName": {
              "terms": {
                "field": "name.keyword",
                "order": "asc"
              }
            }
          },
          {
            "cityName": {
              "terms": {
                "field": "cities.name.keyword",
                "order": "asc"
              }
            }
          }
        ]
      }
    }
  }
}

可以很容易地进行分页

结果如下:

...
"aggregations" : {
  "by_name" : {
    "after_key" : {
      "countryName" : "Slovakia",
      "cityName" : "Zilina"
    },
    "buckets" : [
      {
        "key" : {
          "countryName" : "Czech Republic",
          "cityName" : "Brno"
        },
        "doc_count" : 1
      },
      {
        "key" : {
          "countryName" : "Czech Republic",
          "cityName" : "Praha"
        },
        "doc_count" : 1
      },
      {
        "key" : {
          "countryName" : "Slovakia",
          "cityName" : "Bratislava"
        },
        "doc_count" : 1
      },
      {
        "key" : {
          "countryName" : "Slovakia",
          "cityName" : "Zilina"
        },
        "doc_count" : 1
      }
    ]
  }
}

推荐阅读