翻译自elasticsearch官方文档,整理了java api和rest api的文档部分,仅仅是整理翻译,不是逐字逐句,某些遗漏部分请移步官方文档。

目录

  1. 聚合
    1. 结构化聚合
    2. Metrics类型聚合
      1. min
      2. max
      3. sum
      4. avg
      5. count
      6. stats聚合
      7. Extended Stats聚合
      8. Percentiles聚合,百分比统计
      9. Percentile rank聚合
      10. Cardinality聚合
      11. Geo Bounds聚合
      12. Top hits聚合
      13. Scripted Metric聚合
    3. Bucket类型聚合
      1. Global聚合
      2. Filter聚合
      3. Filters聚合
      4. Missing聚合
      5. Nested聚合
      6. Reerse nested聚合
      7. Children聚合
      8. Terms聚合
      9. Significant Terms聚合
      10. Range聚合
      11. Date Range聚合
      12. IPv4 Range 聚合
      13. Histogram 聚合
      14. Date Histogram聚合
      15. Geo Distance聚合
      16. GeoHash grid 聚合

聚合

基础使用

1
2
3
4
5
6
7
8
9
10
11
12
13
SearchResponse sr = client.prepareSearch()
.setQuery(QueryBuilders.matchAllQuery())
.addAggregation(
AggregationBuilders.terms("agg1").field("field")
)
.addAggregation(
AggregationBuilders.dateHistogram("agg2")
.field("birth")
.interval(DateHistogramInterval.YEAR)
)
.execute().actionGet();
Terms agg1 = sr.getAggregations().get("agg1");
DateHistogram agg2 = sr.getAggregations().get("agg2");

结构化聚合

一个聚合中能够包含子聚合,聚合只能是metrics聚合或bucket聚合。例如,下面的聚合由三个子聚合组成

  • Terms 聚合 (bucket)
  • Date Histogram 聚合 (bucket)
  • Average 聚合 (metric)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    SearchResponse sr = node.client().prepareSearch()
    .addAggregation(
    AggregationBuilders.terms("by_country").field("country")
    .subAggregation(AggregationBuilders.dateHistogram("by_year")
    .field("dateOfBirth")
    .interval((DateHistogramInterval.YEAR)
    .subAggregation(AggregationBuilders.avg("avg_children").field("children"))
    )
    )
    .execute().actionGet();

Metrics类型聚合

min

1
2
3
MetricsAggregationBuilder aggregation = AggregationBuilders.min("agg").field("height");
Min agg = sr.getAggregations().get("agg");
double value = agg.getValue();

max

1
2
3
MetricsAggregationBuilder aggregation = AggregationBuilders.max("agg").field("height");
Max agg = sr.getAggregations().get("agg");
double value = agg.getValue();

sum

1
2
3
MetricsAggregationBuilder aggregation = AggregationBuilders.sum("agg").field("height");
Sum agg = sr.getAggregations().get("agg");
double value = agg.getValue();

avg

1
2
3
MetricsAggregationBuilder aggregation = AggregationBuilders.avg("agg").field("height");
Avg agg = sr.getAggregations().get("agg");
double value = agg.getValue();

count

1
2
3
MetricsAggregationBuilder aggregation = AggregationBuilders.count("agg").field("height");
ValueCount agg = sr.getAggregations().get("agg");
long value = agg.getValue();

stats聚合

直接stats聚合替代sum,avg等聚合

1
2
3
4
5
6
7
MetricsAggregationBuilder aggregation = AggregationBuilders.stats("agg").field("height");
Stats agg = sr.getAggregations().get("agg");
double min = agg.getMin();
double max = agg.getMax();
double avg = agg.getAvg();
double sum = agg.getSum();
long count = agg.getCount();

Extended Stats聚合

1
2
3
4
5
6
7
8
9
10
MetricsAggregationBuilder aggregation = AggregationBuilders.extendedStats("agg").field("height");
ExtendedStats agg = sr.getAggregations().get("agg");
double min = agg.getMin();
double max = agg.getMax();
double avg = agg.getAvg();
double sum = agg.getSum();
long count = agg.getCount();
double stdDeviation = agg.getStdDeviation(); //误差
double sumOfSquares = agg.getSumOfSquares();
double variance = agg.getVariance(); //方差

Percentiles聚合,百分比统计

多值metrics聚合,从被聚合的文档中提取数值计算一个或者多个百分比,这些数值可以是指定的字段,也可以由提供的脚本生成。详情参考官方说明

Percentiles表示被观察数值发生的具体概率。Percentiles通常用来寻找异常值。假设你的数据中包含网站的加载时间,网站加载时间的平均值和中值对于管理人员来说非常有用,最大值通常更令人关注,但是它非常容易被一个缓慢响应歪斜。

下面一个示例中,请求体是:

1
2
3
4
5
6
7
8
9
{
"aggs" : {
"load_time_outlier" : {
"percentiles" : {
"field" : "load_time"
}
}
}
}

返回结果可能是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
...
"aggregations": {
"load_time_outlier": {
"values" : {
"1.0": 15,
"5.0": 20,
"25.0": 23,
"50.0": 25,
"75.0": 29,
"95.0": 60,
"99.0": 150
}
}
}
}

聚合过程将会为每一个默认指定范围的百分比值返回一个计算后的值,假设返回的数值都是毫秒,从返回结果中就能明显看出网站的正常加载时间是15-30ms(约75%),少量加载时间是60-150ms(约4%)。

通常情况下,管理人员对更极端的数值更感兴趣,我们可以指定需要的百分比进行统计计算。

1
2
3
4
5
6
7
8
9
10
{
"aggs" : {
"load_time_outlier" : {
"percentiles" : {
"field" : "load_time",
"percents" : [95, 99, 99.9]
}
}
}
}

Java使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//使用默认指定的百分率范围
MetricsAggregationBuilder aggregation = AggregationBuilders.percentiles("agg").field("height");
//使用自定义百分率范围
MetricsAggregationBuilder aggregation = AggregationBuilders.percentiles("agg").field("height")
.percentiles(1.0, 5.0, 10.0, 20.0, 30.0, 75.0, 95.0, 99.0);
// searchResponse是查询请求返回的实例
Percentiles agg = searchResponse.getAggregations().get("agg");
//遍历
for (Percentile entry : agg) {
double percent = entry.getPercent(); // Percent
double value = entry.getValue(); // Value
logger.info("percent [{}], value [{}]", percent, value);
}

最后打印可能是

1
2
3
4
5
6
7
percent [1.0], value [0.814338896154595]
percent [5.0], value [0.8761912455821302]
percent [25.0], value [1.173346540141847]
percent [50.0], value [1.5432023318692198]
percent [75.0], value [1.923915462033674]
percent [95.0], value [2.2273644908535335]
percent [99.0], value [2.284989339108279]

Percentile rank聚合

Percentile rank表示被观察数据中小于指定数值的百分概率。假设你的数据中包含网站的加载时间,你可能有一个服务协议要求95%的页面加载时间在15ms以内,99%的页面加载时间在30ms以内。

请求数据如下:

1
2
3
4
5
6
7
8
9
10
{
"aggs" : {
"load_time_outlier" : {
"percentile_ranks" : {
"field" : "load_time",
"values" : [15, 30]
}
}
}
}

返回结果如下:

1
2
3
4
5
6
7
8
9
10
11
{
...
"aggregations": {
"load_time_outlier": {
"values" : {
"15": 92,
"30": 100
}
}
}
}

从上面的结果可以看出只有92%的页面加载时间在15ms以内,但是所有的页面加载时间都在30ms以内。

Java使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
MetricsAggregationBuilder aggregation = AggregationBuilders
.percentileRanks("agg")
.field("height")
.percentiles(1.24, 1.91, 2.22);
// searchResponse是查询请求返回的实例
PercentileRanks agg = sr.getAggregations().get("agg");
// For each entry
for (Percentile entry : agg) {
double percent = entry.getPercent(); // Percent
double value = entry.getValue(); // Value
logger.info("percent [{}], value [{}]", percent, value);
}

打印结果如下:

1
2
3
percent [29.664353095090945], value [1.24]
percent [73.9335313461868], value [1.91]
percent [94.40095147327283], value [2.22]

Cardinality聚合

对某个字段不同值计算近似的数量,类似于sql中select count( ) from (select distinct from table),如计算所有书中不同作者的数量

1
2
3
4
5
6
7
8
9
{
"aggs" : {
"author_count" : {
"cardinality" : {
"field" : "author"
}
}
}
}

注意:Cardinality聚合统计的是分词后的不同值的数量。在测试中,测试数据name字段有三列值,分别是[“张三”,”李四”,”张三”],按照传统关系型数据库的理解,结果应该返回 2,使用Cardinality聚合结果返回 4,因为在插入测试数据的时候name字段的值被分词了,被分解成[[“张”,”三”],[“李”,”四”],[“张”,”三”]],最终计算不同值的数量是4。

官方文档着重强调了Cardinality聚合计算的是近似值,具体详见官方文档

Geo Bounds聚合

根据指定字段的geo_point值计算边界

1
2
3
4
5
6
7
GeoBoundsBuilder aggregation = AggregationBuilders.geoBounds("agg").field("address.location").wrapLongitude(true);
// searchResponse是查询请求返回的实例
GeoBounds agg = searchResponse.getAggregations().get("agg");
GeoPoint bottomRight = agg.bottomRight();
GeoPoint topLeft = agg.topLeft();
logger.info("bottomRight {}, topLeft {}", bottomRight, topLeft);

打印结果是

1
bottomRight [40.70500764381921, 13.952946866893775], topLeft [53.49603022435221, -4.190029308156676]

wrapLongitude(true)是指边界是否允许和国际日期变更线重合,默认值是true。

Top hits聚合

top hits聚合能够追踪被聚合后的文档,通常用来作为其它聚合器的子聚合,它能从每个桶中聚合出最先匹配的文档。
可选的选项如下:

  1. from:偏移量,从的第一个聚合结果获取新的结果的偏移量。
  2. size:从每个桶中获取的最大数量,默认值 3。
  3. sort:排序方式,默认同主查询的排序方式一致。

top hits聚合就是作为一个聚合器的子聚合,在父聚合完成形成桶后,在桶中挑选排在前面的几个文档。size是指在每个桶中挑选的数量,并不是最终返回的数量。官方文档

Java使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
AggregationBuilder aggregation = AggregationBuilders
.terms("agg").field("gender") //先按照性别聚合
.subAggregation(
AggregationBuilders.topHits("top")
.setExplain(true)
.setSize(1) //从每个桶中取1条数据
.setFrom(10) //偏移量为10
);
...
// searchResponse是查询请求返回的实例
Terms agg = searchResponse.getAggregations().get("agg");
for (Terms.Bucket entry : agg.getBuckets()) {
String key = entry.getKey(); // bucket key
long docCount = entry.getDocCount(); // Doc count
logger.info("key [{}], doc_count [{}]", key, docCount);
// We ask for top_hits for each bucket
TopHits topHits = entry.getAggregations().get("top");
for (SearchHit hit : topHits.getHits().getHits()) {
logger.info(" -> id [{}], _source [{}]", hit.getId(), hit.getSourceAsString());
}
}

打印的数据是

1
2
3
4
5
6
7
8
key [male], doc_count [5107]
-> id [AUnzSZze9k7PKXtq04x2], _source [{"gender":"male",...}]
-> id [AUnzSZzj9k7PKXtq04x4], _source [{"gender":"male",...}]
-> id [AUnzSZzl9k7PKXtq04x5], _source [{"gender":"male",...}]
key [female], doc_count [4893]
-> id [AUnzSZzM9k7PKXtq04xy], _source [{"gender":"female",...}]
-> id [AUnzSZzp9k7PKXtq04x8], _source [{"gender":"female",...}]
-> id [AUnzSZ0W9k7PKXtq04yS], _source [{"gender":"female",...}]

Scripted Metric聚合

实验特性,后续版本可能会被移除,具体参考官方文档

Bucket类型聚合

Global聚合

在一个查询执行环境中为所有的文档定义一个全局的桶,这个执行环境就是你所查询的索引和类型定义的环境,它不受查询本身影响。

注意:Global聚合只能作为放在顶层的聚合器,嵌入的Global聚合器不能感知到嵌入的其它bucket聚合器。ps:顶层聚合器,原文是top level aggregators,意思是一个聚合器有多个子聚合器,Global聚合器必须放在第一位(代码顺序)

具体示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"query" : {
"match" : { "title" : "shirt" }
},
"aggs" : {
"all_products" : {
"global" : {}, //global聚合器是一个空结构体(必须放在第一位)
"aggs" : { //子聚合器
"avg_price" : { "avg" : { "field" : "price" } }
}
}
}
}

结果如下:

1
2
3
4
5
6
7
8
9
10
11
{
...
"aggregations" : {
"all_products" : {
"doc_count" : 100,
"avg_price" : {
"value" : 56.3
}
}
}
}

上面的聚合示例了怎样在查询环境中对所有文档进行聚合计算(对所有商品价格求平均值),同时忽略查询条件。在这示例中,查询条件是查出所有的衬衫,同时完成对所有的商品价格计算平均值。

Java示例

1
2
3
4
AggregationBuilders.global("agg").subAggregation(AggregationBuilders.terms("genders").field("gender"));
...
Global agg = searchResponse.getAggregations().get("agg");
agg.getDocCount(); // Doc count

Filter聚合

对当前环境的所有文档定义一个符合指定过滤条件的桶,常用来缩小当前聚合文档,得到一个符合特定条件的子文档集。

1
2
3
4
5
6
7
8
9
10
{
"aggs" : {
"red_products" : {
"filter" : { "term": { "color": "red" } },
"aggs" : {
"avg_price" : { "avg" : { "field" : "price" } }
}
}
}
}

结果如下:

1
2
3
4
5
6
7
8
9
{
...
"aggs" : {
"red_products" : {
"doc_count" : 100,
"avg_price" : { "value" : 56.3 }
}
}
}

在上面的例子中,计算所有红色商品的平均价格。

Java示例

1
2
3
4
AggregationBuilders.filter("agg").filter(QueryBuilders.termQuery("gender", "male"));
...
Filter agg = searchResponse.getAggregations().get("agg");
agg.getDocCount(); // Doc count

Filters聚合

定义一个多桶聚合,每一个桶关联一个过滤器,每一个桶都从符合查询条件文档中收集符合关联过滤器的文档(原文说是从所有文档中收集符合关联过滤器条件文档,测试时发现是从符合查询条件的结果中收集,原文说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"aggs" : {
"messages" : {
"filters" : {
"filters" : {
"errors" : { "term" : { "body" : "error" }},
"warnings" : { "term" : { "body" : "warning" }}
}
},
"aggs" : {
"monthly" : {
"histogram" : {
"field" : "timestamp",
"interval" : "1M"
}
}
}
}
}
}

返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
"aggs" : {
"messages" : {
"buckets" : {
"errors" : {
"doc_count" : 34,
"monthly" : {
"buckets" : [
... // the histogram monthly breakdown
]
}
},
"warnings" : {
"doc_count" : 439,
"monthly" : {
"buckets" : [
... // the histogram monthly breakdown
]
}
}
}
}
}
...

在上面分析日志信息的示例中,聚合器会对日志信息建立两个集合(桶),一个包含所有的错误日志,一个包含所有的警告日志,然后对每一个桶都根据日期进行再分组。

也可以使用数组,注意返回格式的不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"aggs" : {
"messages" : {
"filters" : {
"filters" : [
{ "term" : { "body" : "error" }},
{ "term" : { "body" : "warning" }}
]
},
"aggs" : {
"monthly" : {
"histogram" : {
"field" : "timestamp",
"interval" : "1M"
}
}
}
}
}
}

返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
"aggs" : {
"messages" : {
"buckets" : [
{
"doc_count" : 34,
"monthly" : {
"buckets : [
... // the histogram monthly breakdown
]
}
},
{
"doc_count" : 439,
"monthly" : {
"buckets : [
... // the histogram monthly breakdown
]
}
}
]
}
}
...

other_bucket参数:

other_bucket参数是一个Boolean值,默认false。如果为true,为返回结果添加一个包含所有文档(这里所有文档是指符合查询条件的文档)中不符合过滤条件的桶。与other_bucket参数配合的还有一个other_bucket_key参数,这个参数是为这个桶指定一个别名,默认名称是other,如果指定了other_bucket_key参数,则隐式指定other_bucket参数为true。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"query": {
"wildcard": {
"name": "test*"
}
},
"aggs": {
"all_products": {
"filters": {
"other_bucket_key": "other_messages",
"filters": {
"prefix_test222": {
"wildcard": {
"name": "test222*"
}
},
"id_1": {
"term": {
"id": "2"
}
}
}
}
}
}
}

返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
{
"took": 35,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1,
"hits": [ //符合查询条件的文档
{
"_index": "radiott",
"_type": "artiststt",
"_id": "AVx7JhglJRLJihedvp1b",
"_score": 1,
"_source": {
"name": "test333",
"id": "3"
}
},
{
"_index": "radiott",
"_type": "artiststt",
"_id": "AVw0PAb_xJt8Zgd-WGNi",
"_score": 1,
"_source": {
"id": "2",
"name": "test222"
}
}
]
},
"aggregations": { //聚合
"all_products": {
"buckets": {
"prefix_test222": { //前缀为test222的数量
"doc_count": 1
},
"id_1": { //id为1的数量
"doc_count": 1
},
"other_messages": { //其它文档的数量
"doc_count": 1
}
}
}
}
}

在上面的示例中,查询name字段所有以test开头的文档,查询结果共3条数据,在过滤聚合中分别统计前缀以test222开头、id为1和其它文档的数量。

Java使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
AggregationBuilder aggregation =
AggregationBuilders
.filters("agg")
.filter("men", QueryBuilders.termQuery("gender", "male"))
.filter("women", QueryBuilders.termQuery("gender", "female"));
...
Filters agg = searchResponse.getAggregations().get("agg");
for (Filters.Bucket entry : agg.getBuckets()) {
String key = entry.getKeyAsString(); // bucket key
long docCount = entry.getDocCount(); // Doc count
logger.info("key [{}], doc_count [{}]", key, docCount);
}

打印结果

1
2
key [men], doc_count [4982]
key [women], doc_count [5018]

Missing聚合

一个基于字段值的单桶聚合器,统计当前查询文档中指定字段没有字段值的文档(通常是null),该聚合器通常同其它基于字段值聚合器一起使用(如range聚合器),它不能放在其它聚合器里面,因为该聚合器统计的字段值为空。

1
2
3
4
5
6
7
8
9
{
"aggs": {
"miss_value": {
"missing": {
"field": "name"
}
}
}
}

返回结果

1
2
3
4
5
6
7
{
"aggregations": {
"miss_value": {
"doc_count": 2
}
}
}

java使用示例

1
2
3
4
AggregationBuilders.missing("agg").field("gender");
...
Missing agg = searchResponse.getAggregations().get("agg");
agg.getDocCount();

Nested聚合

一个特殊的但桶聚合器能够聚合嵌套文档。

例如,我们有一个所有商品的索引,每个商品都维护一个所有经销商的列表,每个经销商都有该商品的价格,结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
...
"product" : {
"properties" : {
"resellers" : { //经销商,这是一个数组
"type" : "nested",
"properties" : {
"name" : { "type" : "string" }, //经销商名称
"price" : { "type" : "double" }
}
}
}
}
}

取出所有商品的最低价格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"query" : {
"match" : { "name" : "led tv" }
},
"aggs" : {
"resellers" : {
"nested" : {
"path" : "resellers"
},
"aggs" : {
"min_price" : { "min" : { "field" : "resellers.price" } }
}
}
}
}

返回结果

1
2
3
4
5
6
7
8
9
{
"aggregations": {
"resellers": {
"min_price": {
"value" : 350
}
}
}
}

java 使用示例

1
2
3
4
AggregationBuilders.nested("agg").path("resellers");
...
Nested agg = searchResponse.getAggregations().get("agg");
agg.getDocCount(); // Doc count

Reerse nested聚合

暂时没看懂官方文档

Children聚合

一种特殊的单桶聚合器,能够从父文档类型的桶聚合到子文档类型的桶。该聚合依赖_parent字段,有一个type选项。

  • type:子文档需要关联的父文档的类型。例如,一个索引中有question和answer两个类型,answer类型必须有一个_parent字段。
    1
    2
    3
    4
    5
    6
    7
    {
    "answer" : {
    "_parent" : {
    "type" : "question"
    }
    }
    }

questions类型文档有一个tags字段,answer类型文档有一个owner字段。在一个请求中,使用children聚合,tags桶能够关联到owner桶,即便两个字段存在于不同类型的文档中。

一个question文档片段

1
2
3
4
5
6
7
8
9
{
"body": "<p>I have Windows 2003 server and i bought a new Windows 2008 server...",
"title": "Whats the best way to file transfer my site from server to a newer one?",
"tags": [
"windows-server-2003",
"windows-server-2008",
"file-transfer"
],
}

一个answer文档片段

1
2
3
4
5
6
7
8
9
{
"owner": {
"location": "Norfolk, United Kingdom",
"display_name": "Sam",
"id": 48
},
"body": "<p>Unfortunately your pretty much limited to FTP...",
"creation_date": "2009-05-04T13:45:37.030"
}

请求如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"aggs": {
"top-tags": {
"terms": {
"field": "tags",
"size": 10
},
"aggs": {
"to-answers": {
"children": {
"type" : "answer"
},
"aggs": {
"top-names": {
"terms": {
"field": "owner.display_name",
"size": 10
}
}
}
}
}
}
}
}

最终返回的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
"aggregations": {
"top-tags": {
"buckets": [
{
"key": "windows-server-2003",
"doc_count": 25365, //含有标签“windows-server-2003”的文档数
"to-answers": {
"doc_count": 36004, //含有标签“windows-server-2003”的问题的答案数量
"top-names": {
"buckets": [
{
"key": "Sam",
"doc_count": 274
},
{
"key": "chris",
"doc_count": 19
},
{
"key": "david",
"doc_count": 14
},
...
]
}
}
},
...
]
}
}
}

Java示例

1
2
3
4
5
6
AggregationBuilder aggregation = AggregationBuilders
.children("agg")
.childType("reseller");
...
Children agg = searchResponse.getAggregations().get("agg");
agg.getDocCount();

Terms聚合

基于文档动态建立的包含多个桶的聚合,每个桶包含一个独一无二的值。文档感觉有点拗口,实质就是针对指定字段,对该字段的重复值计算数量。

1
2
3
4
5
6
7
{
"aggs" : {
"genres" : {
"terms" : { "field" : "genre" }
}
}
}

返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
...
"aggregations" : {
"genres" : {
"doc_count_error_upper_bound": 0, //对文档每个term计算过程中发生的错误的上界
"sum_other_doc_count": 0, //当文档有很多非重复值,elasticsearch只会返回排在前面的几个terms,这个数字就是未返回的terms的总数
"buckets" : [
{
"key" : "jazz",
"doc_count" : 10
},
{
"key" : "rock",
"doc_count" : 10
},
{
"key" : "electronic",
"doc_count" : 10
},
]
}
}
}

  • size参数

默认情况下,terms聚合会根据doc_count数量排序并返回排在前面的terms,可以修改size参数改变返回的数量。假如size=5,在处理size过程中,节点会协调查询过程请求每个分片返回前5个terms,组合每个分片返回的 terms到一个列表中并排序,再返回列表中的前5个terms到客户端。如果每个分片的terms数量大于5,最终返回的实际结果会有一些偏差。实际结果并不精确。

注意:在2.4.0版本以前,设置size=0,实际会被处理成size=Integer.MAX_VALUE,在后续版本中已经废弃,将来会被移除。

  • shard_size 参数
    指定每个分片返回的size数量,能够在一定程度上提升数据的准确性,但这会加大资源的消耗(CPU、节点间数据传输消耗等等)。

    注意:shard_size值不能比size小,如果设置的值比size小,elasticsearch会用size的值覆盖。

  • order 参数

默认情况下,根据doc_count降序排列

注意:最好不要修改根据doc_count升序排列,这会加大错误的数量。

根据doc_count升序排列

1
2
3
4
5
6
7
8
9
10
{
"aggs" : {
"genres" : {
"terms" : {
"field" : "genre",
"order" : { "_count" : "asc" }
}
}
}
}

根据字母顺序升序

1
"order" : { "_term" : "asc" }

根据单值Metris子聚合排列(根据聚合名称识别)

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"aggs" : {
"genres" : {
"terms" : {
"field" : "genre",
"order" : { "avg_play_count" : "desc" }
},
"aggs" : {
"avg_play_count" : { "avg" : { "field" : "play_count" } }
}
}
}
}

根据单多值Metris子聚合排列(根据聚合名称识别)

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"aggs" : {
"genres" : {
"terms" : {
"field" : "genre",
"order" : { "playback_stats.avg" : "desc" }
},
"aggs" : {
"playback_stats" : { "stats" : { "field" : "play_count" } }
}
}
}
}

注意:Pipeline聚合不能用于排序

  • min_doc_count 参数

没看懂,参考官方文档

java示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
AggregationBuilders.terms("genders").field("gender")
//下面是几种排序方式
.order(Terms.Order.count(true))
//.order(Terms.Order.term(true))
//.order(Terms.Order.aggregation("avg_height", false))
//.subAggregation(
// AggregationBuilders.avg("avg_height").field("height")
//)
...
Terms genders = searchResponse.getAggregations().get("genders");
for (Terms.Bucket entry : genders.getBuckets()) {
entry.getKey(); // Term
entry.getDocCount(); // Doc count
}

Significant Terms聚合

返回感兴趣或者异常发生的terms。在数据量很大的索引中, Significant Terms聚合会很重。使用场景如下:

  • 在搜索禽流感时建议”H5N1”
  • 在新闻自动化分类中建议关于ATI公司股票的关键字
  • 找出轮胎制造商不成比例的爆胎数量

单结果集分析

1
2
3
4
5
6
7
8
9
10
{
"query" : {
"terms" : {"force" : [ "British Transport Police" ]}
},
"aggregations" : {
"significantCrimeTypes" : {
"significant_terms" : { "field" : "crime_type" }
}
}
}

返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
...
"aggregations" : {
"significantCrimeTypes" : {
"doc_count": 47347, //伦敦交警局的犯罪总数量
"buckets" : [
{
"key": "Bicycle theft",
"doc_count": 3640, //伦敦交警局的自行车犯罪总数量
"score": 0.371235374214817,
"bg_count": 66799 //所有警局的自行车偷盗犯罪总数量
}
...
]
}
}
}

从所有警局中查询犯罪记录,上面显示了伦敦交警处理的大量的不成比例的自行车偷盗犯罪。在一般情况下,自行车偷盗犯罪在所有犯罪中仅占1%(66799/5064554),而伦敦交警的工作范围仅仅在地铁和车站,自行车偷盗犯罪就占7%(3640/47347)。

多结果集分析

如果要对多个分类进行分析,一个简单的方式是先使用父聚合分割数据,在进行分析。

1
2
3
4
5
6
7
8
9
10
11
12
{
"aggregations": {
"forces": {
"terms": {"field": "force"},
"aggregations": {
"significantCrimeTypes": {
"significant_terms": {"field": "crime_type"}
}
}
}
}
}

返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
{
...
"aggregations": {
"forces": {
"buckets": [
{
"key": "Metropolitan Police Service",
"doc_count": 894038,
"significantCrimeTypes": {
"doc_count": 894038,
"buckets": [
{
"key": "Robbery",
"doc_count": 27617,
"score": 0.0599,
"bg_count": 53182
},
...
]
}
},
{
"key": "British Transport Police",
"doc_count": 47347,
"significantCrimeTypes": {
"doc_count": 47347,
"buckets": [
{
"key": "Bicycle theft",
"doc_count": 3640,
"score": 0.371,
"bg_count": 66799
},
...
]
}
}
]
}
}

上面的示例展示了分别对各个警局的异常犯罪进行分析。

同样的也可以使用其它父聚合分割数据再进行分析,如通过地理位置分析不同类型犯罪的热点区域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"aggs": {
"hotspots": {
"geohash_grid" : {
"field":"location",
"precision":5,
},
"aggs": {
"significantCrimeTypes": {
"significant_terms": {"field": "crime_type"}
}
}
}
}
}

这个例子使用geohash_grid聚合创建地理位置区域的桶然后在每个桶中再对犯罪类型进行分析。

更多详细说明参见官方文档

Java使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
AggregationBuilder aggregation = AggregationBuilders
.significantTerms("significant_countries")
.field("address.country");
// Let say you search for men only
SearchResponse sr = client.prepareSearch()
.setQuery(QueryBuilders.termQuery("gender", "male"))
.addAggregation(aggregation)
.get();
...
SignificantTerms agg = sr.getAggregations().get("significant_countries");
for (SignificantTerms.Bucket entry : agg.getBuckets()) {
entry.getKey(); // Term
entry.getDocCount(); // Doc count
}

Range聚合

基于多桶数据集的聚合,它能够为每一个桶定义范围。在聚合过程中,从每一个文档中提取的值都将和桶的范围进行比较,然后统计到对应的桶中。在比较边界数据时包含下界不包含上界,即包含from的值不包含to的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"aggs" : {
"price_ranges" : {
"range" : {
"field" : "price",
"ranges" : [
{ "to" : 50 },
{ "from" : 50, "to" : 100 },
{ "from" : 100 }
]
}
}
}
}

返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
...
"aggregations": {
"price_ranges" : {
"buckets": [
{
"to": 50,
"doc_count": 2
},
{
"from": 50,
"to": 100,
"doc_count": 4
},
{
"from": 100,
"doc_count": 4
}
]
}
}
}

设置key:true参数key为每一个桶设置一个名字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"aggs" : {
"price_ranges" : {
"range" : {
"field" : "price",
"keyed" : true,
"ranges" : [
{ "to" : 50 },
{ "from" : 50, "to" : 100 },
{ "from" : 100 }
]
}
}
}
}

返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
...
"aggregations": {
"price_ranges" : {
"buckets": {
"*-50.0": {
"to": 50,
"doc_count": 2
},
"50.0-100.0": {
"from": 50,
"to": 100,
"doc_count": 4
},
"100.0-*": {
"from": 100,
"doc_count": 4
}
}
}
}
}

也可以设置自定义名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"aggs" : {
"price_ranges" : {
"range" : {
"field" : "price",
"keyed" : true,
"ranges" : [
{ "key" : "cheap", "to" : 50 },
{ "key" : "average", "from" : 50, "to" : 100 },
{ "key" : "expensive", "from" : 100 }
]
}
}
}
}

更多关于script方式的使用参见官方文档

java使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
AggregationBuilder aggregation = AggregationBuilders
.range("agg")
.field("height")
.addUnboundedTo(1.0f) // from -infinity to 1.0 (excluded)
.addRange(1.0f, 1.5f) // from 1.0 to 1.5 (excluded)
.addUnboundedFrom(1.5f); // from 1.5 to +infinity
...
Range agg = searchResponse.getAggregations().get("agg");
for (Range.Bucket entry : agg.getBuckets()) {
String key = entry.getKeyAsString(); // Range as key
Number from = (Number) entry.getFrom(); // Bucket from
Number to = (Number) entry.getTo(); // Bucket to
long docCount = entry.getDocCount(); // Doc count
logger.info("key [{}], from [{}], to [{}], doc_count [{}]", key, from, to, docCount);
}

输出结果

1
2
3
key [*-1.0], from [-Infinity], to [1.0], doc_count [9]
key [1.0-1.5], from [1.0], to [1.5], doc_count [21]
key [1.5-*], from [1.5], to [Infinity], doc_count [20]

Date Range聚合

一个处理日期类型数据的聚合器,和range聚合器最大的不同是from和to的值可以是Date Math表达式,也可以设置返回结果需要转换的日期格式,桶range一样,日期比较时包含from不包含to。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"aggs": {
"range": {
"date_range": {
"field": "date",
"format": "MM-yyy",
"ranges": [
{ "to": "now-10M/M" }, //当前时间减10个月,向下取整到月份开始的日期
{ "from": "now-10M/M" }
]
}
}
}
}

返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"aggregations": {
"range": {
"buckets": [
{
"to": 1.3437792E+12,
"to_as_string": "08-2012",
"doc_count": 7
},
{
"from": 1.3437792E+12,
"from_as_string": "08-2012",
"doc_count": 2
}
]
}
}
}

日期转换格式同基本的y,M,d,m,h/H,s一致,还有一些更精细的日期格式,详见官方文档

java使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
AggregationBuilder aggregation = AggregationBuilders
.dateRange("agg")
.field("dateOfBirth")
.format("yyyy")
.addUnboundedTo("1950") // from -infinity to 1950 (excluded)
.addRange("1950", "1960") // from 1950 to 1960 (excluded)
.addUnboundedFrom("1960"); // from 1960 to +infinity
...
Range agg = searchResponse.getAggregations().get("agg");
for (Range.Bucket entry : agg.getBuckets()) {
String key = entry.getKeyAsString(); // Date range as key
DateTime fromAsDate = (DateTime) entry.getFrom(); // Date bucket from as a Date
DateTime toAsDate = (DateTime) entry.getTo(); // Date bucket to as a Date
long docCount = entry.getDocCount(); // Doc count
logger.info("key [{}], from [{}], to [{}], doc_count [{}]", key, fromAsDate, toAsDate, docCount);
}

打印结果

1
2
3
key [*-1950], from [null], to [1950-01-01T00:00:00.000Z], doc_count [8]
key [1950-1960], from [1950-01-01T00:00:00.000Z], to [1960-01-01T00:00:00.000Z], doc_count [5]
key [1960-*], from [1960-01-01T00:00:00.000Z], to [null], doc_count [37]

IPv4 Range 聚合

同date range聚合,IPv4 Range 聚合专门处理IPv类型的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"aggs" : {
"ip_ranges" : {
"ip_range" : {
"field" : "ip",
"ranges" : [
{ "to" : "10.0.0.5" },
{ "from" : "10.0.0.5" }
]
}
}
}
}

返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"aggregations": {
"ip_ranges": {
"buckets" : [
{
"to": 167772165,
"to_as_string": "10.0.0.5",
"doc_count": 4
},
{
"from": 167772165,
"from_as_string": "10.0.0.5",
"doc_count": 6
}
]
}
}
}

更多详见官方文档

java使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
AggregationBuilder aggregation = AggregationBuilders
.ipRange("agg")
.field("ip")
.addUnboundedTo("192.168.1.0") // from -infinity to 192.168.1.0 (excluded)
.addRange("192.168.1.0", "192.168.2.0") // from 192.168.1.0 to 192.168.2.0 (excluded)
.addUnboundedFrom("192.168.2.0"); // from 192.168.2.0 to +infinity
...
Range agg = searchResponse.getAggregations().get("agg");
for (Range.Bucket entry : agg.getBuckets()) {
String key = entry.getKeyAsString(); // Ip range as key
String fromAsString = entry.getFromAsString(); // Ip bucket from as a String
String toAsString = entry.getToAsString(); // Ip bucket to as a String
long docCount = entry.getDocCount(); // Doc count
logger.info("key [{}], from [{}], to [{}], doc_count [{}]", key, fromAsString, toAsString, docCount);
}

打印结果

1
2
3
key [*-192.168.1.0], from [null], to [192.168.1.0], doc_count [13]
key [192.168.1.0-192.168.2.0], from [192.168.1.0], to [192.168.2.0], doc_count [14]
key [192.168.2.0-*], from [192.168.2.0], to [null], doc_count [23]

Histogram 聚合

主要处理数值类型的多桶值聚合。Histogram聚合根据字段值动态创建固定数量(即interval)的桶。例如,一个文档有一个字段是商品的价格,在聚合执行过程中,我们配置聚合每隔5(假设表示5$)建立一个桶,每一个文档的价格都将被评估然后向下取整到最近的一个桶。加入一个商品的价格是32,它将被向下取整到30并将它放到key值是30的桶中,向下取整的方法如下:

1
2
3
4
5
rem = value % interval
if (rem < 0) {
rem += interval
}
bucket_key = value - rem

从上面的方法可以看出,interval的值必须是一个整数。

注意:字段值在放进桶之前会被转换成整数,如果是一个负浮点数可能会被放进错误的桶中。例如在interval为2时-4.5会被转换成-4,所以应该用-4<val<-2的桶替代-6<al<-4的桶。

1
2
3
4
5
6
7
8
9
10
{
"aggs" : {
"prices" : {
"histogram" : {
"field" : "price",
"interval" : 50
}
}
}
}

返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"aggregations": {
"prices" : {
"buckets": [
{
"key": 0,
"doc_count": 2
},
{
"key": 50,
"doc_count": 4
},
{
"key": 100,
"doc_count": 0
},
{
"key": 150,
"doc_count": 3
}
]
}
}
}

min_doc_count

在上面的结果中,没有商品的价格是在100-150区间的,默认情况下,聚合会用一个空桶填充这种“空隙”,如果需要返回的桶中有一个最小的数量,可以设置min_doc_count参数。

1
2
3
4
5
6
7
8
9
10
11
{
"aggs" : {
"prices" : {
"histogram" : {
"field" : "price",
"interval" : 50,
"min_doc_count" : 1
}
}
}
}

返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"aggregations": {
"prices" : {
"buckets": [
{
"key": 0,
"doc_count": 2
},
{
"key": 50,
"doc_count": 4
},
{
"key": 150,
"doc_count": 3
}
]
}
}
}

默认情况下,histogram聚合会返回所有数据范围的桶,文档的最小值会决定桶的key的最小值,文档的最大值会决定桶的key的最大值。常有的情况是我们会请求到空的桶,这常会令人困惑,特别是在聚合前数据被过滤的情况下。

假如我们从文档中取值是0-500的数据,设置interval为50分割数据,设置min_doc_count为0返回所有的空桶,假如所有的商品的最低价格是100,那么我们获得的第一个桶的key值是100,这会令人困惑,因为我希望获得0~100间的桶(即key=0和key=50)而实际上并没有。

extended_bounds

使用extended_bounds参数,能强制histogram聚合构建从min到max间的所有桶,即便是中间的某些桶没有任何数据。要使extended_bounds参数生效,必须设置min_doc_count为0。注意extended_bounds参数并不过滤任何桶,如果extended_bounds.min比文档的最小值要大,桶的最小key值仍由文档的最小值决定(这种情况同样适用于max)。如果需要过滤桶,可以使用query过滤器或是将histogram聚合嵌套到filter聚合中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"query" : {
"constant_score" : { "filter": { "range" : { "price" : { "to" : "500" } } } }
},
"aggs" : {
"prices" : {
"histogram" : {
"field" : "price",
"interval" : 50,
"extended_bounds" : {
"min" : 0,
"max" : 500
}
}
}
}
}

order
默认情况下,桶的排列顺序是根据key值升序排列,如果需要自定义排列可以设置order参数

1
2
3
4
5
6
7
8
9
10
11
12
{
"aggs" : {
"prices" : {
"histogram" : {
"field" : "price",
"interval" : 50,
"order" : { "_key" : "desc" } //根据key降序
//"order" : { "_count" : "asc" } //根据文档数量升序
}
}
}
}

histogram聚合可以包含metrics子聚合,子聚合可以决定桶的排列顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"aggs" : {
"prices" : {
"histogram" : {
"field" : "price",
"interval" : 50,
"order" : { "price_stats.min" : "asc" }
},
"aggs" : {
"price_stats" : { "stats" : {} } //可以不指定field字段,它将从父聚合继承
}
}
}
}

还可以使用层级更深的子聚合进行排序,只要这个子聚合是一个单值桶聚合或metrics聚合。如果是单值桶,最终histogram聚合会根据这个桶的文档数量(doc_count)排序,如果是包含多个度量值的metrics聚合(如states),需要在路径中指定度量metrics名称,如果是单值的metrics聚合,将根据度量值排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"aggs" : {
"prices" : {
"histogram" : {
"field" : "price",
"interval" : 50,
"order" : { "promoted_products>rating_stats.avg" : "desc" } //指定子聚路径和度量名(这里是avg)
},
"aggs" : {
"promoted_products" : {
"filter" : { "term" : { "promoted" : true }},
"aggs" : {
"rating_stats" : { "stats" : { "field" : "rating" }}
}
}
}
}
}
}

上面的例子根据推广产品的平均价格降序排列。

offset

默认情况下桶的标记范围是从0开始,以interval逐步递增。例如interval为10,第一个桶(假设有值)是[0-9],第二个是[10-19],第三个是[20-29],….。使用offset参数能够偏移桶的边界。举个例子,有10个文档,其值是5-14,interval为10,这会产生两个桶,每个桶有5个文档,如果设置offset为5,则只会产生一个包含所有文档的桶[5-14]。

keyed 结果格式化

默认情况下,结果会返回一个排好序的数组,可以设置keyed:true指定使用桶的key值作为返回结果的哈希键。

1
2
3
4
5
6
7
8
9
10
11
{
"aggs" : {
"prices" : {
"histogram" : {
"field" : "price",
"interval" : 50,
"keyed" : true
}
}
}
}

返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"aggregations": {
"prices": {
"buckets": {
"0": {
"key": 0,
"doc_count": 2
},
"50": {
"key": 50,
"doc_count": 4
},
"150": {
"key": 150,
"doc_count": 3
}
}
}
}
}

missing

如果文档并没有值,missing参数决定了这个值该如何处理。默认下这个文档将被忽略。

1
2
3
4
5
6
7
8
9
10
11
{
"aggs" : {
"quantity" : {
"histogram" : {
"field" : "quantity",
"interval": 10,
"missing": 0
}
}
}
}

如果quantity字段没有值,它将被当做值为0(missing参数指定值)处理。

Java使用示例

1
2
3
4
5
6
7
8
9
10
11
12
AggregationBuilder aggregation = AggregationBuilders
.histogram("agg")
.field("height")
.interval(1);
...
Histogram agg = sr.getAggregations().get("agg");
for (Histogram.Bucket entry : agg.getBuckets()) {
Long key = (Long) entry.getKey(); // Key
long docCount = entry.getDocCount(); // Doc count
logger.info("key [{}], doc_count [{}]", key, docCount);
}

Date Histogram聚合

类似于histogram聚合,它只能处理日期类型的字段值。因为在elasticsearch内部,日期类型数据被当做long类型处理,所以也可以用histogram聚合处理,但这会在精度上有所妥协,原因是基于时间的interval无法确定(思考下闰年的每月天数)。因此我们需要一种特殊的方式处理日期数据。从功能性的观点来看,date histogram聚合同普通的histogram聚合一样支持同样的特性。两者最大的不同是date histogram支持使用date/time表达式设置interval。

1
2
3
4
5
6
7
8
9
10
{
"aggs" : {
"articles_over_time" : {
"date_histogram" : {
"field" : "date",
"interval" : "month"
}
}
}
}

可以使用的interval有:year, quarter, month, week, day, hour, minute, second。

部分数值能够使用seconds, minutes, hours, days and weeks,如interval为1.5h

1
2
3
4
5
6
7
8
9
10
{
"aggs" : {
"articles_over_time" : {
"date_histogram" : {
"field" : "date",
"interval" : "1.5h"
}
}
}
}

能够使用的缩写列表参考 time-units。目前有y(year),M(month),w(Week),d(day),h(hour),m(minute),s(second),ms(milli-second)。

keys

elasticsearch使用64 bit数字表示timestamp,这个timestamp也将作为桶的key,key_as_string表示使用format指定的格式转换后的值。

1
2
3
4
5
6
7
8
9
10
11
{
"aggs" : {
"articles_over_time" : {
"date_histogram" : {
"field" : "date",
"interval" : "1M",
"format" : "yyyy-MM-dd"
}
}
}
}

返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"aggregations": {
"articles_over_time": {
"buckets": [
{
"key_as_string": "2013-02-02",
"key": 1328140800000,
"doc_count": 1
},
{
"key_as_string": "2013-03-02",
"key": 1330646400000,
"doc_count": 2
},
...
]
}
}
}

time zone

具体参考文档 Time Zone

offset

offset参数用来修改桶的起始范围,可以使用+,-符号,1h代表一小时,1M代表一个月。例如设置interval为day,每一个桶的起始范围是午夜到午夜,设置offset为+6h,修改桶的范围是6am到6am。

1
2
3
4
5
6
7
8
9
10
11
{
"aggs": {
"by_day": {
"date_histogram": {
"field": "date",
"interval": "day",
"offset": "+6h"
}
}
}
}

返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"aggregations": {
"by_day": {
"buckets": [
{
"key_as_string": "2015-09-30T06:00:00.000Z",
"key": 1443592800000,
"doc_count": 1
},
{
"key_as_string": "2015-10-01T06:00:00.000Z",
"key": 1443679200000,
"doc_count": 1
}
]
}
}

Scripts

同histogram,文档参考 Scripts

missing

同histogram

1
2
3
4
5
6
7
8
9
10
11
{
"aggs" : {
"publish_date" : {
"date_histogram" : {
"field" : "publish_date",
"interval": "year",
"missing": "2000-01-01"
}
}
}
}

java使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
AggregationBuilder aggregation = AggregationBuilders
.dateHistogram("agg")
.field("dateOfBirth")
.interval(DateHistogramInterval.YEAR); //设置为year
//.interval(DateHistogramInterval.days(10)); //设置为10day
...
Histogram agg = searchResponse.getAggregations().get("agg");
for (Histogram.Bucket entry : agg.getBuckets()) {
DateTime key = (DateTime) entry.getKey(); // Key
String keyAsString = entry.getKeyAsString(); // Key as String
long docCount = entry.getDocCount(); // Doc count
logger.info("key [{}], date [{}], doc_count [{}]", keyAsString, key.getYear(), docCount);
}

打印结果

1
2
3
4
5
6
7
key [1942-01-01T00:00:00.000Z], date [1942], doc_count [1]
key [1945-01-01T00:00:00.000Z], date [1945], doc_count [1]
key [1946-01-01T00:00:00.000Z], date [1946], doc_count [1]
...
key [2005-01-01T00:00:00.000Z], date [2005], doc_count [1]
key [2007-01-01T00:00:00.000Z], date [2007], doc_count [2]
key [2008-01-01T00:00:00.000Z], date [2008], doc_count [3]

Geo Distance聚合

一个多桶值聚合,处理geo_point数据,从概念上类类似于range聚合。用户定义一个圆点并设置桶的距离范围,聚合会计算文档的geo_point点到圆点间的距离然后决定应该归属到哪一个桶。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"aggs" : {
"rings_around_amsterdam" : {
"geo_distance" : {
"field" : "location",
"origin" : "52.3760, 4.894",
"ranges" : [
{ "to" : 100 },
{ "from" : 100, "to" : 300 },
{ "from" : 300 }
]
}
}
}
}

返回数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"aggregations": {
"rings" : {
"buckets": [
{
"key": "*-100.0",
"from": 0,
"to": 100.0,
"doc_count": 3
},
{
"key": "100.0-300.0",
"from": 100.0,
"to": 300.0,
"doc_count": 1
},
{
"key": "300.0-*",
"from": 300.0,
"doc_count": 7
}
]
}
}
}

指定的字段必须是geo_point类型,也可以是数组类型(数组值必须是get_point)类型,如果是这种情况,数组中的每一个值都将被计算,圆点能够接受以下geo_point type

  • 对象格式:{ "lat" : 52.3760, "lon" : 4.894 },这也是最安全的方式
  • 字符串格式:"52.3760, 4.894",第一个数字是lan,第二个数字是lon
  • 数组格式:[4.894, 52.3760],第一个时lon,第二个是lan,==注意与上面两种方式的不同==。

默认距离单位是m(米),也能接受mi(英里),in (英寸), yd (码), km (千米), cm (厘米), mm (毫米)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"aggs" : {
"rings" : {
"geo_distance" : {
"field" : "location",
"origin" : "52.3760, 4.894",
"unit" : "mi", //设置距离单位为英里
"ranges" : [
{ "to" : 100 },
{ "from" : 100, "to" : 300 },
{ "from" : 300 }
]
}
}
}
}

distance_type

Geo Distance聚合有三种计算距离的方式,通过distance_type参数设置

  • sloppy_arc:默认方式,速度较快,精度稍差
  • arc:速度最慢,精度最高。
  • plane:速度最快,精度最低。推荐在小的地域使用,如城市或国家。在跨洲计算时误差很大
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    {
    "aggs" : {
    "rings" : {
    "geo_distance" : {
    "field" : "location",
    "origin" : "52.3760, 4.894",
    "distance_type" : "plane",
    "ranges" : [
    { "to" : 100 },
    { "from" : 100, "to" : 300 },
    { "from" : 300 }
    ]
    }
    }
    }
    }

java使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
AggregationBuilder aggregation = AggregationBuilders
.geoDistance("agg")
.field("address.location")
.point(new GeoPoint(48.84237171118314,2.33320027692004))
.unit(DistanceUnit.KILOMETERS)
.addUnboundedTo(3.0)
.addRange(3.0, 10.0)
.addRange(10.0, 500.0);
...
Range agg = sr.getAggregations().get("agg");
for (Range.Bucket entry : agg.getBuckets()) {
String key = entry.getKeyAsString(); // key as String
Number from = (Number) entry.getFrom(); // bucket from value
Number to = (Number) entry.getTo(); // bucket to value
long docCount = entry.getDocCount(); // Doc count
logger.info("key [{}], from [{}], to [{}], doc_count [{}]", key, from, to, docCount);
}

打印结果如下

1
2
3
key [*-3.0], from [0.0], to [3.0], doc_count [161]
key [3.0-10.0], from [3.0], to [10.0], doc_count [460]
key [10.0-500.0], from [10.0], to [500.0], doc_count [4925]

GeoHash grid 聚合

一个多值桶聚合,将geo_point数据代表的地理点根据设定的精密度划分成一个网格,每一个桶代表一个单元格。单元格的精密度用geohash表示。精密度越高,每个单元格代表的区域越小。GeoHash grid 聚合采用的geohash精密度用1-12表示,数值越大,精密度越高。

注意:12级的最高精密度下,每个单元格代表的实际区域不到一平方米,因此高精密度的数据请求会过多的消耗内存,在请求高精密度的数据前请尽量使用过滤聚合成小的地理单位。

指定的字段必须是geo_point类型,也可以是数组类型(数组值必须是get_point)类型,如果是这种情况,数组中的每一个值都将被计算。

低密度示例

1
2
3
4
5
6
7
8
9
10
{
"aggregations" : {
"myLarge-GrainGeoHashGrid" : {
"geohash_grid" : {
"field" : "location",
"precision" : 3
}
}
}
}

返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"aggregations": {
"myLarge-GrainGeoHashGrid": {
"buckets": [
{
"key": "svz",
"doc_count": 10964
},
{
"key": "sv8",
"doc_count": 3198
}
]
}
}
}

高精密度示例。在高精密度聚合前,使用了geo_bounding_box过滤器将计算的地理区域变小,否则可能会创建数百万的桶。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"aggregations" : {
"zoomedInView" : {
"filter" : {
"geo_bounding_box" : {
"location" : {
"top_left" : "51.73, 0.9",
"bottom_right" : "51.55, 1.1"
}
}
},
"aggregations":{
"zoom1":{
"geohash_grid" : {
"field":"location",
"precision":8
}
}
}
}
}
}

精密度表如下:

等级 地理区域
1 5,009.4km x 4,992.6km
2 1,252.3km x 624.1km
3 156.5km x 156km
4 39.1km x 19.5km
5 4.9km x 4.9km
6 1.2km x 609.4m
7 152.9m x 152.4m
8 38.2m x 19m
9 4.8m x 4.8m
10 1.2m x 59.5cm
11 14.9cm x 14.9cm
12 3.7cm x 1.9cm

选项

  • field:必填,geo_point数据的字段名
  • precision:可选,默认5,精密度等级
  • size:可选,默认10000,返回的桶的数量。桶包含的文档数越多优先权越高。
  • shard_size:可选,默认返回数量是max(10,size*切片数量),每个切片返回的桶的数量。

Java使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
AggregationBuilder aggregation = AggregationBuilders
.geohashGrid("agg")
.field("address.location")
.precision(4);
...
GeoHashGrid agg = searchResponse.getAggregations().get("agg");
for (GeoHashGrid.Bucket entry : agg.getBuckets()) {
String keyAsString = entry.getKeyAsString(); // key as String
GeoPoint key = (GeoPoint) entry.getKey(); // key as geo point
long docCount = entry.getDocCount(); // Doc count
logger.info("key [{}], point {}, doc_count [{}]", keyAsString, key, docCount);
}

打印結果

1
2
3
4
5
key [gbqu], point [47.197265625, -1.58203125], doc_count [1282]
key [gbvn], point [50.361328125, -4.04296875], doc_count [1248]
key [u1j0], point [50.712890625, 7.20703125], doc_count [1156]
key [u0j2], point [45.087890625, 7.55859375], doc_count [1138]
...