ElasticSearch&Lucene

百亿级数据搜索引擎,你所要的Elasticsearch聚合查询都

2020-01-18  本文已影响0人  javap

基本概念

简单来说就是满足特定条件的文档的集合。当聚合开始被执行,每个文档里面的值通过计算来决定符合哪个桶的条件。如果匹配到,文档将放入相应的桶并接着进行聚合操作。

指标

桶能让我们划分文档到有意义的集合,但是最终我们需要的是对这些桶内的文档进行一些指标的计算。分桶是一种达到目的的手段:它提供了一种给文档分组的方法来让我们可以计算感兴趣的指标。
大多数 指标 是简单的数学运算(例如最小值、平均值、最大值,还有汇总),这些是通过文档的值来计算。在实践中,指标能让你计算像平均薪资、最高出售价格、95%的查询延迟这样的数据。

桶和指标

聚合 是由桶和指标组成的。 聚合可能只有一个桶,可能只有一个指标,或者可能两个都有。也有可能有一些桶嵌套在其他桶里面。

聚合

聚合框架目的就是使搜索查询能够提供聚合数据。它是基于称为聚合的简单构建块,它可以帮助我们构建复杂的数据。Elasticsearch有不同类型的聚合,每种聚合都有自己的目的和最终的数据输出。为了更好地理解这些类型,通常将它们分为四种主要类型:

"aggregations" : {
   "<aggregation_name>" : {
      "<aggregation_type>" : {
         <aggregation_body>
      },
      [, "meta" : { [<meta_data_body>] } ]
      [, "aggregations" : { ..... } ]
   }
   [, "<aggregation_name_2>": {......} ]
}

指标聚合

该聚合是根据要聚合的文档提取出来字段值来进行指标计算。

平均值(avg)

单值指标聚合,用于计算从文档中提取的数值的平均值。这些值可以从文档中的数字字段中提取,也可以由脚本生成。

$ curl -XPOST "localhost:9200/bank/_search?pretty&size=0" -d'
{
   "aggs": {
      "avg_agg" : { "avg" : { "field": "balance" } }
   }
}'

# 由脚本生成指标
$ curl -XPOST "localhost:9200/bank/_search?pretty&size=0" -d'
{
   "aggs": {
      "avg_agg" : {
     "avg" : {
        "script": {
           "source": "doc.balance.value"
        }
         }
      }
   }
}'

求和(sum)

单指标聚合,对文档中字段的数值进行求和。

$ curl -XPOST "localhost:9200/bank/_search?pretty&size=0" -d'
{
   "aggs": {
      "sum_agg" : { "sum" : { "field": "balance" } }
   }
}'

最大值(max)

单指标聚合,可返回从聚合文档中提取的数值中的最大值。

$ curl -XPOST "localhost:9200/bank/_search?pretty&size=0" -d'
{
   "aggs": {
      "max_agg" : { "max" : { "field": "balance" } }
   }
}'

最小值(min)

单指标聚合,可返回从聚合文档中提取的数值中的最小值。

$ curl -XPOST "localhost:9200/bank/_search?pretty&size=0" -d'
{
   "aggs": {
      "min_agg" : { "min" : { "field": "balance" } }
   }
}'

地理边界(geo_bounds)

用于计算包含一个字段geo_point值的边界框。

$ curl -XPOST "localhost:9200/restaurants/_search?pretty&size=0" -d'
{
   "query": {
      "match": { "name": "restaurant" }
   },
   "aggs": {
      "geo_restaurant" : {
         "geo_bounds" : {
             "field": "location"
         }
      }
   }
}'

地理中心(geo_centroid)

根据要聚合文档求出地理位置上的的中心点。

curl -XPOST "localhost:9200/restaurants/_search?pretty&size=0" -d'
{
   "aggs": {
      "geo_restaurant" : {
         "geo_centroid" : {
            "field": "location"
         }
      }
   }
}'

百分比(perecentiles)

多指标聚合,该聚合针对从聚合文档中提取的数值计算一个或多个百分位数。假设日志数据包含接口响应时间,对管理员来说平均和中位数加载时间并无太大用处,最大值可能更有用。

curl -XPOST "localhost:9200/bank/_search?pretty&size=0" -d'
{
   "aggs": {
      "age_outlier" : {
         "percentiles" : {
            "field": "age",
         }
      }
   }
}'

# 指定某个百分比范围
curl -XPOST "localhost:9200/bank/_search?pretty&size=0" -d'
{
   "aggs": {
      "age_outlier" : {
         "percentiles" : {
            "field": "age",
            "percens": [90, 95, 99]
         }
      }
   }
}'

# 指定TDigest compression
curl -XPOST "localhost:9200/bank/_search?pretty&size=0" -d'
{
   "aggs": {
      "age_outlier" : {
        "percentiles" : {
           "field": "age",
           "tdigest":{
              "compression": 200
           }
         }
      }
   }
}'

# 指定HDR Histogram
$ curl -XPOST "localhost:9200/bank/_search?pretty&size=0" -d'
{
   "aggs": {
      "age_outlier" : {
         "percentiles" : {
            "field": "age",
               "hdr": {
                  "number_of_significant_value_digits": 5
               }
            }
         }
      }
   }
}'

百分比等级(percentile_ranks)

多指标聚合,该聚合根据从聚合文档中提取的数值计算出一个或多个百分数等级。

curl -XPOST "localhost:9200/bank/_search?pretty&size=0" -d'
{
   "aggs": {
      "age_outlier" : {
         "percentile_ranks" : {
            "field": "balance",
            "values": [25000, 50000]
         }
      }
   }
}'

基数聚合(cardinality)
单值指标聚合,用于计算不同值的近似计数。它提供一个字段的基数,即该字段的 distinct 或者 unique 值的数目。可以以如下SQL来帮助理解该聚合:

SELECT COUNT(DISTINCT age) FROM account

去重是一个很常见的操作,可以应用于很多的业务场景:
每天/每月/每年访问网站的独立IP有多少?

curl -XPOST "localhost:9200/bank/_search?pretty&size=0" -d'
{
   "aggs": {
      "name_count" : {
         "cardinality" : {
            "field": "firstname.keyword"
         }
      }
   }
}'

precision_threshold接受 0–40,000 之间的数字,更大的值还是会被当作 40,000 来处理。
HyperLogLog
HyperLogLog 可以接受多个元素作为输入,并给出输入元素的基数估算值。基数:集合中不同元素的数量,比如 {'apple', 'banana', 'cherry', 'banana', 'apple'} 的基数就是 3 。
估算值:算法给出的基数并不是精确的,可能会比实际稍微多一些或者稍微少一些,但会控制在合理的范围之内
HyperLogLog 的优点是,即使输入元素的数量或者体积非常非常大,计算基数所需的空间总是固定的、并且是很小的。
bitmap存储1一亿个统计数据大概需要12M内存;而在HLL中,只需要不到1K内存就能做到;redis中实现的HyperLogLog,只需要12K内存,在标准误差0.81%的前提下,能够统计2^64个数据。

Top聚合(top_hits)

top_hits聚合可以有效地用于通过桶聚合器按某些字段对结果集进行分组。该聚合器主要用作子聚合,以便可以按存储分区汇总最匹配的文档。它有3个重要的参数:

curl -XPOST "localhost:9200/bank/_search?size=0&pretty=true" -d'
{
   "aggs": {
      "top_tags": {
         "terms": {
            "field": "lastname.keyword",
            "size": 6
         },
         "aggs": {
            "top_balance_hits": {
               "top_hits": {
                  "sort": [
                     {
                        "balance": {
                           "order": "desc"
                        }
                     }
                  ],
                  "_source": {
                     "includes": [ "firstname", "lastname", "age", "balance" ]
                  },
                  "size" : 1
               }
            }
         }
      }
   }
}'

分桶聚合

桶聚合不像指标聚合那样计算字段的值,而是创建文档存储桶。每个桶都与一个标准/查询(取决于聚合类型)相关联,该标准/查询确定文档是否“落入”其中,存储桶有效地定义了文档集。除了存储桶本身之外,桶聚合还计算并返回“落入”每个存储桶的文档数量。
过滤聚合(filter)
返回当前文档集中与指定过滤器匹配的文档的单个桶。通常,用于将当前聚合的文档再进一步缩小到一组特定的文档。

curl -XPOST "localhost:9200/bank/_search?size=0&pretty=true" -d'
{
   "aggs": {
      "gender_filter": {
         "filter": { "term" : { "gender.keyword" : "M" } },
         "aggs": {
            "balance_price": {
               "avg": {
                  "field": "balance"
               }
            }
         }
      }
   }
}'

多过滤聚合(filters)
定义一个多桶聚合,其中每个桶都与一个过滤器相关联。每个存储桶将收集与其关联的过滤器匹配的所有文档。

curl -XPOST "localhost:9200/bank/_search?size=0&pretty=true" -d'
{
   "aggs": {
      "bank_filter": {
         "filters": {
            "filters": {
               "state": { "match": { "state.keyword" : "AZ" } },
               "name": { "match": { "lastname.keyword" : "Hess" } }
            }
         }
      }
   }
}'


curl -XPOST "localhost:9200/bank/_search?size=0&pretty=true" -d'
{
   "aggs": {
      "bank_filter": {
         "filters": {
            "other_bucket": true,
            "filters": {
               "state": { "match": { "state.keyword" : "AZ" } },
               "name": { "match": { "lastname.keyword" : "Hess" } }
            }
         }
      }
   }
}'

范围聚合(range)
基于多桶的聚合,使用户能够定义一组范围,每个范围代表一个桶。在汇总过程中,将按每个范围检查每个文档中提取的值,并“存储”相关/匹配的文档。请注意,此聚合包括起始值,但不包括每个范围的起始值。

# 范围聚合
curl -XPOST localhost:9200/bank/_search?size=0 -d'
{
   "aggs": {
      "age_range": {
         "range": {
            "field": "age",
            "ranges": [
               { "to": 30 },
               { "from": 20, "to": 25 },
               { "from": 40 }
            ]
         }
      }
   }
}'

# 在桶内再进行操作
curl -XPOST localhost:9200/bank/_search?size=0 -d'
{
   "aggs": {
      "age_range": {
         "range": {
            "field": "age",
               "ranges": [
                  { "to": 30 },
                  { "from": 20, "to": 25 },
                  { "from": 40 }
               ]
            },
            "aggs": {
               "balance_max": {
                  "max": { "field": "balance" }
            }
         }
      }
   }
}'

日期范围聚合(date_range)
该聚合与其他范围聚合之间的主要区别在于,可以在Date Math表达式中指定from和to值,还可以指定一种日期格式,通过该日期格式将返回from和to响应字段。请注意,此聚合包括起始值,但不包括每个范围的起始值。

curl -XPOST localhost:9200/logstash-*/_search?size=0 -d'
{
   "aggs": {
      "age_range": {
         "date_range": {
            "field": "utc_time",
            "format": "yyyy-MM",
            "ranges": [
               { "to": "now+10M" },
               { "from": "now-10M" }
            ]
         }
      }
   }
}'

格式字母其定义如下:

字母缩写 描述 类型 默认值
C century of era (>=0) number 20
Y year of era (>=0) year 1996
x weekyear year 1996
w week of weekyear number 27
e day of week number 2
E day of week text Tuesday; Tue
y year year 1996
D day of year number 189
M month of year month July; Jul; 07
d day of month number 10
a halfday of day text PM
K hour of halfday (0~11) number 0
h clockhour of halfday (1~12) number 12
H hour of day (0~23) number 0
k clockhour of day (1~24) number 24
m minute of hour number 30
s second of minute number 55
S fraction of second number 978
z time zone text Pacific Standard Time; PST
Z time zone offset/id zone -0800; -08:00; America/Los_Angeles
' escape for text delimiter ''

日期直方图聚合(date_histogram)
直方图的多桶聚合,但只能应用于日期值。可以通过日期/时间表达式指定间隔。

curl -XPOST "localhost:9200/logstash-*/_search?size=0&pretty" -d'
{
   "aggs": {
      "utc_time_agg": {
         "date_histogram" : {
            "field": "utc_time",
            "interval": "day",
            "format": "yyyy-MM-dd",
            "offset": "+6h"
         }
      }
   }
}'
curl -XPOST "localhost:9200/restaurants/_search?size=0&pretty" -d'
{
   "aggs": {
      "geo_range": {
         "geo_distance": {
            "field": "location",
            "origin": "52.3760, 4.894",
            "ranges": [
               { "to": 100000 },
               { "from": 100000, "to": 300000 },
               { "from": 300000 }
            ]
         }
      }
   }
}'

默认情况下,距离单位为m(米),但也可以设置为:mi(英里),in(英寸),yd(码),km(公里),cm(厘米),mm(毫米)。

curl -XPOST "localhost:9200/restaurants/_search?size=0&pretty" -d'
{
   "aggs": {
      "geo_range": {
         "geo_distance": {
            "field": "location",
            "origin": "52.3760, 4.894",
            "unit": "km",
            "ranges": [
               { "to": 100000 },
               { "from": 100000, "to": 300000 },
               { "from": 300000 }
            ]
         }
      }
   }
}'

该聚合有两种距离计算模式:弧(arc,默认)和平面(plane)。弧计算是最准确的。平面最快但最不准确。当搜索跨越较小的地理区域(约5公里)时,请考虑使用平面。如果在非常大的区域内进行搜索(例如跨大陆搜索),平面将返回较高的误差范围。

curl -XPOST "localhost:9200/restaurants/_search?size=0&pretty" -d'
{
   "aggs": {
      "geo_range": {
         "geo_distance": {
            "field": "location",
            "origin": "52.3760, 4.894",
            "unit": "km",
            "distance_type": "plane",
            "ranges": [
               { "to": 100000 },
               { "from": 100000, "to": 300000 },
               { "from": 300000 }
            ]
         }
      }
   }
}'

地理网格聚合(geohash_grid)
适用于geo_point字段的多桶聚合。将地理坐标点分组到以网格为单元的桶中。每个单元格都使用可定义精度的geohash进行标记,聚合中使用的地理哈希可以在1到12之间选择精度(长度为12的最高精度geohash仅覆盖不到一平方米的区域,因此就RAM和结果大小而言,高精度geohash可能会更加昂贵)

curl -XPOST "localhost:9200/restaurants/_search?size=0&pretty" -d'
{
   "aggs": {
      "geo_hash_agg": {
         "geohash_grid": {
            "field": "location",
            "precision": 12
         }
      }
   }
}'

curl -XPOST "localhost:9200/restaurants/_search?size=0&pretty" -d'
{
   "aggs": {
      "zoom_filter": {
         "filter" : {
            "geo_bounding_box": {
               "location": {
                  "top_left": "52.4, 4.9",
                  "bottom_right": "52.3, 5.0"
               }
            }
         },
         "aggs": {
            "zoom_agg": {
               "geohash_grid": {
                  "field": "location",
                  "precision": 8
               }
            }
         }
      }
   }
}'

GEOHash长度和区域范围对应表:

序号 计算公式
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

词项聚合(terms)
多指标聚合,根据索引库中的文档动态构建桶。基于词项的聚合,如果聚合字段是text的话,会对一个一个的词根进行聚合基于多桶的聚合,每个桶一个对应一个唯一值。

curl -XPOST "localhost:9200/bank/_search?size=0&pretty" -d'
{
   "aggs": {
      "name_agg": {
         "terms" : {
            "field": "firstname.keyword",
            "size": 3
         }
      }
   }
}'

默认情况下,词项聚合将返回doc_count排序的前十个词项的存储桶。可以通过设置size参数来更改此默认行为。
排序
可以通过设置Order参数来设置桶的顺序。默认情况下,桶会按照doc_count降序排列。不建议按_count升序或按子聚合排序,因为这会增加文档计数上的错误。

curl -XPOST "localhost:9200/bank/_search?size=0&pretty" -d'
{
   "aggs": {
      "name_agg": {
         "terms" : {
            "field": "firstname.keyword",
            "order": { "_term": "desc" }
         },
         "aggs": {
            "max_balance": { "max": { "field": "balance" } }
         }
      }
   }
}'

过滤
对桶进行过滤。可以设置include和exclude参数来完成,参数接受正则表达式或精确值数组。此外,include子句还可以使用分区表达式。

# 使用include和exclude进行过滤
curl -XPOST "localhost:9200/bank/_search?size=0&pretty" -d'
{
   "aggs": {
      "name_agg": {
         "terms" : {
            "field": "address.keyword",
            "include": ".*Gatling.*",
            "exclude": ".*Street"
         }
      }
   }
}'

# 精确过滤
curl -XPOST "localhost:9200/bank/_search?size=0&pretty" -d'
{
   "aggs": {
      "name_agg": {
         "terms" : {
            "field": "address.keyword",
            "include": ["105 Onderdonk Avenue", "Street"]
         }
      }
   }
}'

# 分区过滤
curl -XPOST "localhost:9200/bank/_search?size=0&pretty" -d'
{
   "aggs": {
      "name_agg": {
         "terms" : {
            "field": "account_number",
            "include": {
               "partition": 0,
               "num_partitions": 20
            },
            "size": 20,
            "order": {
               "max_balance": "desc"
            }
         },
         "aggs": {
            "max_balance": {
               "max": { "field": "balance" }
            }
         }
      }
   }
}'

搜集模式
对于具有许多唯一术语和少量所需结果的字段,将子聚合的计算延迟到最上层父级aggs被聚合之前,可能会更有效。通常,聚合树的所有分支都以深度优先的方式进行扩展,然后才进行修剪。在某些情况下,这可能非常浪费,并且会遇到内存限制。如:在电影数据库中查询10个最受欢迎的演员及其5个最常见的联合主演:

curl -XPOST "localhost:9200/bank/_search?size=0&pretty" -d'
{
   "aggs": {
      "name_agg": {
         "terms" : {
            "field": "firstname.keyword",
            "size": 10,
            "collect_mode": "breadth_first"
         },
         "aggs": {
            "tops": {
               "terms": {
                  "field": "firstname.keyword",
                  "size": 5
               }
            }
         }
      }
   }
}'

管道聚合

管道聚合主要目的是将信息添加到输出树中,而不是文档集所产生的输出。管道聚合有很多不同的类型,每种类型都与其他聚合计算不同的信息,但是可以将这些类型分为两类:

AGG_SEPARATOR       =  '>' ; # 聚合分隔符
METRIC_SEPARATOR    =  '.' ; # 指标分隔符
AGG_NAME            =  <聚合名称> ;
METRIC              =  <指标名称> ;
PATH                =  <AGG_NAME> [ <AGG_SEPARATOR>, <AGG_NAME> ]* [ <METRIC_SEPARATOR>, <METRIC> ] ;

管道聚合没有子聚合功能,但是根据其类型,可以引用buckets_path中的另一个管道,从而可以链接管道聚合。
平均值聚合
兄弟管道聚合,用于计算兄弟聚合中指定指标的平均值。指定的指标必须是数字,并且同级聚合必须是多桶聚合。

curl -XPOST "localhost:9200/logstash-*/_search?size=0&pretty" -d'
{
   "aggs": {
      "utc_time_agg": {
         "date_histogram" : {
            "field": "utc_time",
            "interval": "day",
            "format": "yyyy-MM-dd"
         },
         "aggs": {
            "sum_bytes": {
               "sum": {
                  "field": "bytes"
               }
            }
          }
      },
      "max_day_bytes": {
         "avg_bucket": {
            "buckets_path": "utc_time_agg>sum_bytes"
         }
      }
   }
}'

常用参数

curl -XPOST "localhost:9200/logstash-*/_search?size=0&pretty" -d'
{
   "aggs": {
      "utc_time_agg": {
         "date_histogram" : {
            "field": "utc_time",
            "interval": "day",
            "format": "yyyy-MM-dd"
         },
         "aggs": {
            "sum_bytes": {
               "sum": {
                  "field": "bytes"
               }
            },
            "jpg_aggs": {
               "filter": {
                  "term": {
                     "extension.keyword": "jpg"
                  }
               },
               "aggs": {
                  "jpg_bytes": {
                     "sum": {
                        "field": "bytes"
                     }
                  }
               }
            },
            "jpg_percentage": {
               "bucket_script": {
                  "buckets_path": {
                     "jpgBytes": "jpg_aggs>jpg_bytes",
                     "totalBytes": "sum_bytes"
                  },
                  "script": "params.jpgBytes/params.totalBytes*100"
               }
            }
         }
      }
   }
}'

分桶选择聚合

curl -XPOST "localhost:9200/logstash-*/_search?size=0&pretty" -d'
{
   "aggs": {
      "utc_time_agg": {
         "date_histogram" : {
            "field": "utc_time",
            "interval": "day",
            "format": "yyyy-MM-dd"
         },
         "aggs": {
            "sum_bytes": {
               "sum": {
                  "field": "bytes"
               }
            },
            "bytes_bucket_filter": {
               "bucket_selector": {
                  "buckets_path": {
                     "totalBytes": "sum_bytes"
                  },
                  "script": "params.totalBytes>3650000"
               }
            }
         }
      }
   }
}'
上一篇下一篇

猜你喜欢

热点阅读