聚集查询(Aggregation)提供了针对多条文档的统计运算功能,它不是针对文 档本身内容的检索,而是要将它们聚合到一起运算某些方面的特征值。

聚集查询与SQL语言中的聚集函数非常像,聚集函数在Elasticsearch中相当于是聚集查询的一种聚集类型。比如在SQL中的avg函数用于求字段平均值, 而在Elasticsearch中要实现相同的功能可以使用avg聚集类型。

聚集查询也是通过_search接口执行,只是在执行聚集查询时使用的参数是aggregations或aggs。所以_search接口可以执行两种类型的查询,1种是通过query参数执行DSL,另一种则是通过aggregations执行聚集查询。

这两种查询方式还可以放在一起使用, 执行逻辑是先通过DSL检索满足查 询条件的文档,然后再使用聚集查询对DSL检索结果做聚集运算这一规则适用 于本章列举的所有聚集查询。聚集查询有着比较规整的请求结构,具体格式如下:

"aggregations/aggs" : {

"<聚集名称>":{

"<聚集类型>":{

<聚集体>

}

………

[,"aggregations/aggs" : ( [ <子聚集>]+ ) ]

}

[,"<聚集名称>”: (...) ]*

}

aggregations和aggs都是_search的参数,其中aggs是agregations的简写。每一个聚集查询都需要定义一个聚集名称,并归属于种聚集类型。聚集名称是用户自定义的,而聚集类型则是由Elasticsearch预先定义好。

聚集名称会在返回结果中标识聚集结果,而聚集类型则决定了聚集将如何运算。比如前面提到的avg 就是一种聚集类型。这里要特别强调的是,聚集中可以再包含子聚集。子聚集位于父聚集的名称中,与聚集类型同级,所以子聚集的运算都是在父聚集的环境中运算。Eaticsarch对子聚集的深度没有限制,所以理论上说可以包含无限深度的子聚集。

聚集类型总体上被分为四种大类型,即指标聚集(Metrics Aggregation)、桶型( Bucket Aggregation)、管道聚集( Pipeline Aggregation)和矩阵聚集( Matrix Aggregation)。

指标聚集是根据文档字段所包含的值运算某些统计特征值,如平均值、总和等,它们的结果一般都包含一个或多个数值,前面提到的avg聚集就是指标聚集的1种。桶型聚集根据一定的分组标准将文档归到不同的组中,这些分组在Elasticsearch中被称为桶( Bucken),桶型聚集与SQL中group by的作用类似,一般会与指标聚集嵌套使用。管道聚集可以理解为聚集结果的再聚集,它一般以另一个聚集结果作为输人,然后在此基础上再做聚集。矩阵聚集是Elasticsearch中的新功能,由于是针对多字段做多种运算,所以形成的结果类似于矩阵。

一、指标聚集

指标聚集是根据文档中某一字段做聚集运算,比如计算所有产品销量总和、 平均值等等。指标聚集的结果可能是单个值,这种指标聚集称为单值指标聚集; 也可能是多个值,称为多值指标聚集。

1、平均值聚集

平均值聚集是根据文档中数值类型字段计算平均值的聚集查询,包括avg聚集和weighted_avg聚集两种类型。avg聚集直接取字段值后计算平均值,而weighted_avg聚集则会在计算平均值时添加不同的权重。

1.1、avg聚集

avg聚集计算平均值,例如在示例中计算航班的平均延误时间:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"aggs": {

"delay_avg": {

"avg": {

"field": "FlightDelayMin"

}

}

}

}

在示例中使用了请求参数fiter _path将返回结果的其他字段过滤掉了,否则查询的结果中将包含kibana_sample_data_flights索引中的文档。加了filter_path之后返果为:

{

"aggregations" : {

"delay_avg" : {

"value" : 47.33517114633586

}

}

}

在返回结果中,aggregations是关键字,代表这是聚集查询的结果。其中的delay_avg则是在聚集查询中定义的聚集名称,value是聚集运算的结果。在示例中运算航班延误时间时会将所有文档都包含进来做计算,如果只想其 中一部分文档参与运算则可以使用query参数以DSL的形式定义查询条件。

例如在示例中就是只计算了飞往中国的航班平均延误时间:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"query": {

"match": {

"DestCountry": "CN"

}

},

"aggs": {

"delay_avg": {

"avg": {

"field": "FlightDelayMin"

}

}

}

}

在示例中请求_search接口时,同时使用了query与aggs参数。在执行检索时会先通过query条件过滤文档,然后再在符合条件的文档中运算平均值聚集。 weighted_avg无非就是根据某些条件对进行聚集的数据进行加权运算,和avg聚集没有本质差别。

2、计数聚集与极值聚集

计数聚集用于统计字段值的数量,而极值聚集则是查找字段的极大值和极小值。

2.1、计数聚集

value_count聚集和cardinality聚集可以归入计数聚集中,前者用于统计从字段中取值的总数,而后者则用于统计不重复数值的总数。例如:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"aggs": {

"country_code": {

"cardinality": {

"field": "DestCountry"

}

},

"total_country": {

"value_count": {

"field": "DestCountry"

}

}

}

}

在示例中,cardinality聚集统计了DestCountry字段非重复值的数量,类似于SQL中的distinct。value_count 聚集则统计了DestCountry字段所有返回值的数量,类似于SQL中的count。

需要注意的是,cardinality聚集的算法使用极小内存实现统计结果的基本准确。所以cardinality在数据量极大的情况下是不能保证完全准确的。

2.2、极值聚集

极值聚集是在文档中提取某一字段最大值或最小值的聚集,包括max聚集和min聚集

例如:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"aggs": {

"max_price": {

"max": {

"field": "AvgTicketPrice"

}

},

"min_price": {

"min": {

"field": "AvgTicketPrice"

}

}

}

}

上面例子聚集有max_price和min_price两个,它们分别计算了机票价格的最大的最小值。

3、统计聚集

统计聚集是一个多值指标聚集,也就是返回结果中会包含多个值,都是一些与统计相生的数据。统计聚集包含stats聚集和extended_stats聚集两种,前者返回的统计数据是一些比较基本的数值,而后者则包含一些比较专业的统计数值。

3.1、stats聚集

stats聚集返回结果中包括字段的最小值(min)、最大值(max)、总和(sum)、数业(coun)及平均值(avg)五项内容。例如,在示例中对机票价格做统计:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"aggs": {

"price_stats": {

"stats": {

"field": "AvgTicketPrice"

}

}

}

}

在示例中,stats聚集使用field参数指定参与统计运算的字段为AvgTicketPrice。

3.2、extended_stats聚集

extended_stats聚集增加了几项统计数据,这包括平方和、方差、标准方差 和标准方差偏移量。从使用的角度来看,extended_stats聚集与stats聚集完全 相同,只是聚集类型不同。

4、百分位聚集

百分位聚集根据文档字段值统计字段值按百分比的分布情况,包括pecrentiles聚集和percentile_ranks两种。前者统计的是百分比与值的对应关系,而后者正好相反统计值与百分比的对应关系。

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"aggs": {

"price_percentile": {

"percentiles": {

"field": "AvgTicketPrice",

"percents": [25,50,75,100]

}

},

"price_percentile_rank": {

"percentile_ranks": {

"field": "AvgTicketPrice",

"values": [ 600, 1200]

}

}

}

}

pecrentiles聚集通过pecrents参数设置组百分比,然后按值由小到大的顺序划分不同区间,每个区间对应一个百分比。percentile_ranks聚集则通过value参 数设置组值,然后根据这些值分别计算落在不同值区间的百分比。以示例返回的结果为例:

{

"aggregations" : {

"price_percentile_rank" : {

"values" : {

"600.0" : 45.342571446870195,

"1200.0" : 100.0

}

},

"price_percentile" : {

"values" : {

"25.0" : 410.0089175627301,

"50.0" : 640.3872852064159,

"75.0" : 842.2593898341107,

"100.0" : 1199.72900390625

}

}

}

}

在pecrentiles返回结果price_percentile中,“"25. 0" : 410. 0127977258341"代表的含义是25%的机票价格都小于410. 0127977258341,其他以此类推。在percentile_ranks返回结果price_percentile_rank中, 600.0": 45. 39892372745635"代表的含义是600.0以下的机票占总机票价格的百分比为45.39892372745635%。

二、使用范围分桶

如果使用SQL语言类比,桶型聚集与SQL语句中的group by子句极为相似。桶型聚集(Bucket Aggregation)是Elasticsearch官方对这种聚集的叫法,它起的作用是根据条件对文档进行分组。

可以将这里的桶理解为分组的容器,每个桶都与一个分组标准相关联,满足 这个分组标准的文档会落桶中。所以在默认情况下,桶型聚集会根据分组标准返回所有分组,同时还会通过doc_count字段返回每一桶中的文档数量。

由于单纯使用桶型聚集只返回桶内文档数量,意义并不大,所以多数情况下 都是将桶型聚集与指标聚集以父子关系的形式组合在起使用。桶型聚集作为父聚 集起到分组的作用。而指标聚集则以子聚集的形式出现在桶型聚集中, 起到分 组统计的作用。比如将用户按性别分组,然后统计他们的平均年龄。

按返回桶的数量来看,桶型聚集可以分为单桶聚集和多桶聚集。在多桶聚集中,有些桶的数量的固定的。而有些桶的数量则是在运算时动态决定。由于桶聚集基本都是将所有桶一次返回,返回了过多的通会影响性能,所以单个请求允许返间的最大桶数受search.max_bucket参数限制。

这个参数在7.0之前的版本中默认值为-1,代表无上限。但在Elasticsearch版本7中,这个参数的默认值已经更改为10000所以在做桶型聚集时要先做好数据验证,防止桶数量过多影响性能。 桶型聚集的种类非常多,我们分别来讲解。

1、数值范围

range、date_range 与 ip_range 这三种类型的聚集都用于根据字段的值范围 内对文档分桶,字段值在同一范围内的文档归入同一桶中。每个值范围都可通过 from 和 to 参数指定,范围包含 from 值但不包含 to 值,用数学方法表示就是[from, to)。 在设置范围时,可以设置一个也可以设置多个,范围之间并非一定要连续, 可以有间隔也可以有重叠

1.1、range聚集

range聚集使用ranges参数设置多个数值范围,使用field 参数指定1个数值 类型的字段。range聚集在执行时会将该字段在不同范围内的文档数量统计出来,并在返回结果的doc_count字段中展示出来。例如统计航班不同范围内的票价数 量,可以按示例的方式发送请求:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"aggs": {

"price_ranges": {

"range": {

"field": "AvgTicketPrice",

"ranges": [

{

"to": 300

},

{

"from": 300,

"to": 600

},

{

"from": 600,

"to": 900

},

{

"to": 900

}

]

}

}

}

}

在返回结果中,每个范围都会包含一个key字段,代表了这个范围的标识,它的基本格式是“<from>- <to>"。如果觉得返回的这种key格式不容易理解,可以通过在range聚集的请求中添加keyed和key参数定制返回结果的key字段值。其中keyed是range的参数,用于标识是否使用key标识范围,所以为布尔类型,key参数则是与from、to参数同级的参数,用于定义返回结果中的key字段值。

1.2、date_range聚集

date_range聚集与range聚集类似,只见范围和字段的类型为日期而非数值date_range聚集的范围指定也是通过ranges参数设置,具体的范围也是使用<from>- <to>两个参数,并且可以使用keyed和key定义返回结果的标识。date_range聚集多了个指定日期格式的参数format,可以用于指定from和to的目期格式。例如:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"aggs": {

"mar_flights": {

"date_range": {

"field": "timestamp",

"ranges": [

{

"from": "2019-03-01",

"to": "2019-03-30"

}

],

"format": "yyyy-MM-dd"

}

}

}

}

1.3、ip_range聚集

ip_ range聚集根据ip类型的字段统计落在指定IP范围的文档数量,使用的聚集类型名称为ip_ range。例如,统计了两个IP地址范围的文档数量:

POST /kibana_sample_data_logs/_search?filter_path=aggregations

{

"aggs": {

"local": {

"ip_range": {

"field": "clientip",

"ranges": [

{

"from": "157.4.77.0",

"to": "157.4.77.255"

},

{

"from": "105.32.127.0",

"to": "105.32.127.255"

}

]

}

}

}

}

2、间隔范围

histogram、date _ histogram与auto_date_histogram这三种聚集与上一节中使用数值定义范围的聚集很像,也是统计落在某一范围内的文档数量。 但与数值范围聚集不同的是,这三类座集统计范围由固定的间隔定义,也就是范围的结束值和起始值的差值是固定的。

2.1、histogram聚集

histogram聚集以数值为间隔定义数值范围,字段值具有相同范围的文档将落入同桶中。例如示例以100为间隔做分桶,可以通过返回结果的doc_count字 段获取票价在每个区间的文档数量:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"aggs": {

"price_histo": {

"histogram": {

"field": "AvgTicketPrice",

"interval": 100,

"offset": 50,

"keyed": false,

"order": {

"_count": "asc"

}

}

}

}

}

其中,interval参数用于指定数值问隔必须为正值,而offset参数则代表起始数值的偏移量,必须位于[0, interval) 范围内。order参数用于指定排序字段和顺序,可选字段为_key和_count。当keyed参数设置为true时,返回结果中每个桶会有一个标识,标识的计算公式为:

bucket_key = Math. floor( ( value- offset)/interval) * interval + offset。

2.2、date_histogram聚集

date_histogra聚集以时间为间隔定义日期范围,字段值具有相同日期范围的文档将落入同一桶中。同样,返回结果中也会包含每个间隔范围内的文档数量doc_count。例如统计每月航班数量:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"aggs": {

"month flights": {

"date_histogram": {

"field": "timestamp",

"interval": "month"

}

}

}

}

在示例使用参数 interval指定时间间隔为month,即按月划分范围。时间可以是还有:

毫秒:1ms 10ms

秒:second/1s 10s

分钟:minute/1m 10m

小时:hout/1h 2h

天:day 2d 不支持

星期:week/1w 不支持

月:month/1M 不支持

季度:quarter/1q 不支持

年:year/1y 不支持

不过这个参数将要过期,替代的是fixed_interval和calendar_interval,可以参考这个页面,有详细说明:

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-datehistogram-aggregation.html

简单来说,calendar_interval支持1年、1季度、1月、1周、1天、1小时、1分钟,不支持时间为复数,fixed_interval支持的时间单位为天、小时、分、秒、毫秒,允许复数,例如"fixed_interval" : "30d",表示为30天。

2.3、auto_date_histogram聚集

前述两种聚集都是指定间隔的具体值是多少,然后再根据间隔值返回每一桶中满足条件的文档数。最终会有多少桶取决于两个条件,即间隔值和字段值在所有文档中的实际跨度。反过来,如果预先指定需要返回多少个桶,那么间隔值 也可以通过桶的数量以及字段值跨度共同确定。auto_date_histogram聚集就是这 样一种聚集,它不是指定时间间隔值,而是指定需要返回桶的数量。例如在示例 中定义需要返回10个时间段的桶:

POST /kibana_sample_data_flights/_search?size=0

{

"aggs": {

"age_group": {

"auto_date_histogram": {

"field": "timestamp",

"buckets": 10

}

}

}

}

参数field设置通过哪一个字段做时间分隔,而参数buckets则指明了需要返回多少个桶。 默认情况下, buckets的数量为10。需要注意的是,buckets只是设置了期望返回桶的数量,但实际返回桶的数量可能等于也可能小于buckets设置的值。例如示例的请求中期望10个桶,但实际可能只返回6个桶。

auto_date_histogram聚集在返回结果中还提供了一个interval字段,用于说明实际采用的间隔时间。从实现的角度来说,不精确匹配buckets数量也有利于提升检索的性能。

3、子聚集(聚集嵌套)

前面介绍的桶型聚集,大部分都只是返回满足聚集条件的文档数量。在实际应用中,如果需要桶型聚集与SQL中的group by具有相同的意义,用于将文档分桶后计算各桶特定指标值,比如根据用户性别分组,然后分别求他们的平均年龄。Elasticsearch这样的功能通过子聚集(聚集嵌套)来实现。例如,示例中的请求就是先按月对从中国起飞的航班做了分桶,然后又通过聚集嵌套计算每月平均延误时间:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"query": {

"term": {

"OriginCountry": "CN"

}

},

"aggs": {

"date_price_histogram": {

"date_histogram": {

"field": "timestamp",

"interval": "month"

},

"aggs": {

"avg_price": {

"avg": {

"field": "FlightDelayMin"

}

}

}

}

}

}

在示例中,_search接口共使用了两个参数,query参数以term查询条件将所有OriginCountry字段是CN的文档筛选出来参与聚集运算。aggs参数则定义了一个名称为data_price_histogram的桶型聚集,这个聚集内部又嵌套了个名称为avg_ price的聚集。由于price这个聚集位于data_price histogram中,所以它会使用这个聚集的分桶结果做运算而不会针对所有文档。 所以,最终的效果就是将按月计算从中国出发航班的平均延误时间,使用嵌套聚集时要注意,嵌套聚集应该位于父聚集名称下而与聚集类型同级,并且需要通过参数再次声明。如果与父聚集一样位于aggs参数下,那么这两个聚集就是平级而非嵌套聚集。

三、使用词项分桶

使用字段值范围分桶主要针对结构化数据,比如年龄、IP地址等等。但对 于字符串类型的字段来说,使用值范围来分桶显然是不合适的。由于字符串类型 字段在编入索引时会通过分析器生成词项,所以字符申类型字段的分桶一般通过 词项实现。使用词项实现分桶的聚集,包括terms、significant_terms和significant_text聚集。由于使用词项分桶需要加载所有词项数据,所以它们在执 行速度上都会比较慢。为了提升性能,Elasticsearch提供了sampler和diversifed_sampler聚集,可通过缩小样本数量减少运算量。

1、terms聚集

terms聚集根据文档字段中的词项做分桶,所有包含同一词项的文档将被归 人同一桶中,聚集结果中包含字段中的词项及其词频,在默认情况下还会根据词 频排序,所以terms聚集也可用于热词展示,由于terms聚集在统计词项的词频数据时需要打开它的fielddata机制。fielddata机制对内存消耗较大且有导致内存溢出的可能,所以terms聚集一般针对keyword非text类型。

Fielddata:其实根据倒排索引反向出来的一个正排索引,即document到term的映射。只要我们针对需要分词的字段设置了fielddata,就可以使用该字段进行聚合,排序等。我们设置为true之后,在索引期间,就会以列式存储在内存中。为什么存在于内存呢,因为按照term聚合,需要执行更加复杂的算法和操作,如果基于磁盘或者OS缓存,性能会比较差。

fielddata堆内存要求很高,如果数据量太大,对于JVM来及回收来说存在一定的挑战,也就是对ES带来巨大的压力。所以doc_value的出现我们可以使用磁盘存储,他同样是和fielddata一样的数据结构,在倒排索引基础上反向出来 的正排索引,并且是预先构建,即在建倒排索引的时候,就会创建doc values。这会消耗额外的存储空间,但是对于JVM的内存需求就会减少。总体来看,DocValues只是比fielddata慢一点,大概10-25%,则带来了更多的稳定性。

cardlinality聚集可以统计字段中不重复词项的数量,而terms聚集则可以将 这些词项全部展示出来。与cardlinality聚集一样,terms聚集统计出来的词频也 不能保证完全精确。例如:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"aggs": {

"country_terms": {

"terms": {

"field": "DestCountry",

"size": 10

}

},

"country_terms_count": {

"cardinality": {

"field": "DestCountry"

}

}

}

}

在示例中定义了两个聚集,由于它们都是定义在aggs下,所以不是嵌套聚集。terms聚集的field参数定义了提取词项的字段为DestCountry,它的词项在返回结果中会按词频由高到低依次展示,词频会在返回结果的doc_count字段中展示。另一个参数size则指定了只返回10个词项,这相当把DestCountry字段中词频前10名检索出来。

2、significant_terms聚集

terms聚集统计在字段中的词项及其词频,聚集结果会按各词项总的词频排序,并讲现次数最多的词项排在最前面,这非常适合做推荐及热词类的应用。但按词频总数不一定可能是总是正确的选择,在一些检索条件已知的情况下,一些词频总数比较低的词项反而是更合适的推荐热词。

举例来说,假设在10000篇技术类文章的内容中提到Elasticsearch有200篇,占比为2%;但在文章标题含有NoSQL的1000篇文章中,文章内容提到Elasticsearch的为180篇,占比为18%。这种占比显著的提升,说明在文章标题 含有NoSQL的条件下,Elasticsearch变得更为重要。换句话说,如果一个词项在 某个文档子集中与在文档全集中相比发生了非常显著的变化,就说明这个词项在 这个文档子集中是更为重要的词项。

significant_terms聚集就是针对上述情况的一种聚集查询,它将文档和词项 分为前景集Foreground Set和背景集(Background Set)。前景集对应一个文档子集,面背景集则对应文档全集。significant_terms聚集根据query指定前景集,运算field参数指定字段中的词项在前景集和背景集中的词频总数,并在结果的doc_coumt和bg_coumt中保存它们。例如:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"query": {

"term": {

"OriginCountry": {

"value": "IE"

}

}

},

"aggs": {

"dest": {

"significant_terms": {

"field": "DestCountry"

}

}

}

}

在示例中,query参数使用DSL 指定了前景集为出发国家为IE (即爱尔兰)的 航班,而聚集查询中则使用significant_ terms统计到达国家的前景集词频和背景集词频。来看下返回结果:

{

"aggregations" : {

"dest" : {

"doc_count" : 119,

"bg_count" : 13059,

"buckets" : [

{

"key" : "GB",

"doc_count" : 12,

"score" : 0.19491470110905626,

"bg_count" : 449

},

{

"key" : "KR",

"doc_count" : 7,

"score" : 0.15232998092035055,

"bg_count" : 214

},

{

"key" : "PE",

"doc_count" : 4,

"score" : 0.15082268201398205,

"bg_count" : 80

},

{

"key" : "CO",

"doc_count" : 4,

"score" : 0.12852820507647145,

"bg_count" : 91

},

{

"key" : "IN",

"doc_count" : 8,

"score" : 0.0910024946739459,

"bg_count" : 373

},

{

"key" : "EC",

"doc_count" : 6,

"score" : 0.08965790336983642,

"bg_count" : 237

},

{

"key" : "PR",

"doc_count" : 3,

"score" : 0.04633841366161068,

"bg_count" : 116

},

{

"key" : "JP",

"doc_count" : 10,

"score" : 0.035111171691658886,

"bg_count" : 774

},

{

"key" : "US",

"doc_count" : 21,

"score" : 0.028200604970369705,

"bg_count" : 1987

},

{

"key" : "NO",

"doc_count" : 3,

"score" : 0.02141702682394835,

"bg_count" : 178

}

]

}

}

}

在返回结果中,前景集文档数量为119,背景集文档数量为13059。在buckets返回的所有词项中,国家编码为GB的航班排在第一位。它在前景集中的词频为12,占比约为10% (12/119); 而在背景集中的词频为449,占比约为3. 4% (445/13059)。

词项GB在前景集中的占比是背景集中的3倍左右,发生了显著变化,所以在这个前景集中GB可以被视为热词而排在第一位。GB代表的国家是英国,从爱尔兰出发去英国的航班比较多想来也是合情合理的。

除了按示例方式使用quey参数指定前景集以外,还可以将terms聚集与significant_terms聚集结合起来使用,这样可以一次性列出一个字段的所有前景集的热词。例如:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"aggs": {

"orgin_dest": {

"terms": {

"field": "OriginCountry"

},

"aggs": {

"dest": {

"significant_terms": {

"field": "DestCountry"

}

}

}

}

}

}

在示例中,使用terms聚集OriginCountry字段的词项全部查询出来做前景集,然后再与significant_terms聚集起查询它们的热词。

3、significant_text聚集

如果参与significant_terms聚集的字段为text类型,那么需要将字段的fielddata机制开启,否则在执行时会返回异常信息。significant_text聚集与significant_terms聚集的作用类型,但不需要开启字段的fielddata机制,所以可以把它当成是种专门为text类型字段设计的significant_terms聚集。例如在kibana_sample_data_logs中,message字段即为text类型,如果想在这个字段上做词项分析就需要使用significant_terms聚集:

POST /kibana_sample_data_logs/_search?filter_path=aggregations

{

"query": {

"term": {

"response": {

"value": "200"

}

}

},

"aggs": {

"agent_term": {

"significant_text": {

"field": "message"

}

}

}

}

在示例中,前景集为响应状态码response为200的日志,significant_text聚集则查看在这个前景集下message字段中出现异常热度的词项。返回结果片段:

{

"aggregations" : {

"agent_term" : {

"doc_count" : 12832,

"bg_count" : 14074,

"buckets" : [

{

"key" : "200",

"doc_count" : 12832,

"score" : 0.09559395920909229,

"bg_count" : 12846

},

{

"key" : "beats",

"doc_count" : 3460,

"score" : 0.004397033874757019,

"bg_count" : 3734

},

{

"key" : "x86_64",

"doc_count" : 6124,

"score" : 0.0037046431288825427,

"bg_count" : 6665

},

{

"key" : "20110421",

"doc_count" : 4931,

"score" : 0.003315842203821136,

"bg_count" : 5362

},

{

"key" : "6.0a1",

"doc_count" : 4931,

"score" : 0.003315842203821136,

"bg_count" : 5362

},

{

"key" : "rv",

"doc_count" : 4931,

"score" : 0.003315842203821136,

"bg_count" : 5362

},

{

"key" : "firefox",

"doc_count" : 4931,

"score" : 0.003315842203821136,

"bg_count" : 5362

},

{

"key" : "08",

"doc_count" : 6609,

"score" : 0.003266308663600317,

"bg_count" : 7203

},

{

"key" : "6.3.2",

"doc_count" : 5950,

"score" : 0.0027083663110559104,

"bg_count" : 6488

},

{

"key" : "styles",

"doc_count" : 2082,

"score" : 0.002051716645269952,

"bg_count" : 2255

}

]

}

}

}

通过展示的返回结果可以看出,排在第一位的词项200在前景集和背景集中的数量是一样的,这说明message中完整地记录了200状态码;而排在第二位的词项beats前景集和背景集分别为3462和3732。这说明请求“/beats" 地址的 成功率要远高于其他地址。

significant_text聚集之所以不需要开启fielddata机制是因为它会在检索时对text字段重新做分析,所以significant_text聚集在执行时速度比其他聚集要慢很多。如果希望提升执行效率,则可以使用sampler聚集通过减少前景集的样本数量降低运算量。

4、样本聚集

sampler聚集的作用是限定其内部嵌套聚集在运算时采用的样本数量。sampler提取样本时会按文档检索的相似度排序,按相似度分值由高到低的顺序提取。例如:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"query": {

"term": {

"OriginCountry": {

"value": "IE"

}

}

},

"aggs": {

"sample_data": {

"sampler": {

"shard_size": 100

},

"aggs": {

"dest_country": {

"significant_terms": {

"field": "DestCountry"

}

}

}

}

}

}

在示例中共定义了sample_ data和dest_country两个聚集,其中dest_country是sample_data聚集的子聚集或嵌套聚集,因此dest_country在运算时就只从分片上取一部分样本做运算。sampler聚集的shard_size就是定义了每个分片上提取样本的数量,这些样本会根据DSL查询结果的相似度得分由高到低的顺序提取。

执行后会发现,这次目的地最热的目的地国家由GB变成了KR,这就是样本范围缩小导致的数据失真。为了降低样本减少对结果准确性的影响,需要将些重复的数据从样本中剔除。换句话说就是样本更加分散,加大样本数据的多样性。Elasticsearch提供的diversified_sampler聚集提供了样本多样性的能力,它提供了field或script两个参数用于去除样本中可能重复的数据。由于相同航班的票价可能是相同的,所以可以将票价相同的航班从样本中剔除以加大样本的多样性,例如:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"query": {

"term": {

"OriginCountry": {

"value": "IE"

}

}

},

"aggs": {

"sample_data": {

"diversified_sampler": {

"shard_size": 100,

"field": "AvgTicketPrice"

},

"aggs": {

"dest_country": {

"significant_terms": {

"field": "DestCountry"

}

}

}

}

}

}

diversified_sampler通过field参数设置了AvgTicketPrice字段,这样在返回结果中GB就又重新回到了第一位。

四、单桶聚集

前面介绍的桶型聚集都是多桶型聚集,本节主要介绍单桶聚集单桶聚集在返回结果中只会形成一个桶,它们都有比较特定的应用场最。在Elasticsearch中,单桶聚集主要包括filter,global,missing等几种类型。另外还有一种filters聚集,它虽然属于多桶聚集,但与filter聚集很接近,所以放到一起说明。

1、过滤器聚集

过滤器聚集通过定义一个或多个过滤器来区分桶,满足过速器条件的文档将落入这个过滤器形成的桶中。过滤器聚集分为单桶和多桶两种,对应的聚集类型自然就是filter和filters。本来看filter桶型聚集,它属于单桶型聚集。一般会同时嵌套一个指标聚集,用于在过滤后的文档范围内计算指标,例如:

POST /kibana_sample_data_flights/_search?size=0&filter_path=aggregations

{

"aggs": {

"origin_cn": {

"filter": {

"term": {

"OriginCountry": "CN"

}

},

"aggs": {

"cn_ticket_price": {

"avg": {

"field": "AvgTicketPrice"

}

}

}

},

"avg_price": {

"avg": {

"field": "AvgTicketPrice"

}

}

}

}

在示例中一共定义了3个聚集,最外层是两个聚集,最后聚集为嵌套聚集,origin_cn聚集为单过滤器的桶型聚集,它将所有OriginCountry为CN的文档归入一桶。

origin_cn桶型聚集嵌套了cn_ticket_price指标聚集, 它的作用是计算当前桶内文档AvgTicketPrice字段的平均值。另一个外层聚集avg_price虽然也是计算AghckePie字段的平均值,但它计算的是所有文档的平均值。实际上使用query与agg结合起来也能实现类似的功能,区别在于过滤器不会做相似度计算,所以效率更高一些也更灵活一些。

多过滤器与单过滤器的作用类似,只是包含有多个过滤器,所以会形成多个桶。多过滤博型聚集使用filters参数接收过滤条件的数组,一般也是与指标聚集一同使用。例如使用两个过滤器计算从中国、美国出发的航班平均机票价格:

POST /kibana_sample_data_flights/_search?size=0&filter_path=aggregations

{

"aggs": {

"origin_cn_us": {

"filters": {

"filters": [

{

"term": {

"OriginCountry": "CN"

}

},

{

"term": {

"OriginCountry": "US "

}

}

]

},

"aggs": {

"avg_ price": {

"avg": {

"field": "AvgTicketPrice"

}

}

}

}

}

}

2、global聚集

global桶型聚集也是一种单桶型聚集, 它的作用是把索引中所有文档归入一个桶中。这种桶型聚集看似没有什么价值,但当global桶型聚集与query结合起来使用时,它不会受query定义的查询条件影响,最终形成的桶中仍然包含所有文档。global聚集在使用上非常简单,没有任何参数,例如:

POST /kibana_sample_data_flights/_search?size=0&filter_path=aggregations

{

"query": {

"term": {

"Carrier": {

"value": "Kibana Airlines"

}

}

},

"aggs": {

"kibana_avg_delay": {

"avg": {

"field": "FlightDelayMin"

}

},

"all flights": {

"global": {},

"aggs": {

"all_avg_delay": {

"avg": {

"field": "FlightDelayMin"

}

}

}

}

}

}

在示例中query使用term查询将航空公司为“Kibana Airline"的文档都检索出来,而kibana _avg delay定义的平均值聚集会将它们延误时间的平均值计算出来。但另个all_fights聚集由于使用了global聚集所以在嵌套的all_avg_delay聚集中计算出来的是所有航班廷误时间的平均值。

3、missing聚集

missing聚集同样也是一种单桶型聚集,它的作用是将某一字段缺失的文档归入一桶。missing聚集使用field参数定义要检查缺失的字段名称,例如:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"aggs": {

"no_price": {

"missing": {

"field": "AvgTicketPrice"

}

}

}

}

示例将kibana_sample_data_flights中缺失AvgTicketPrice字段的文档归入一桶,通过返回结果的doc_count查询数量也可以与指标聚集做嵌套,计算这些文档的某一指标值。

4、聚集组合

有两种比较特殊的多桶型聚集,它们是composite聚集和adjacency_matrix聚集。这两种聚集是以组合不同条件的形式形成新桶,只是在组合的方法和组件 的条件上存在着明显差异。

composite聚集可以将不同类型的聚集组合到一一起,它会从不同的聚集中 提取数据,并以笛卡尔乘积的形式组合它们,而每一个组合就会形成一个新桶。 例如想查看平均票价与机场天气的对应关系,可以这样:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"aggs": {

"price_weather": {

"composite": {

"sources": [

{

"avg_price": {

"histogram": {

"field": "AvgTicketPrice",

"interval": 500

}

}

},

{

"weather": {

"terms": {

"field": "OriginWeather"

}

}

}

]

}

}

}

}

在示例中,composite聚集中通过soures参数定义了两个需要组合的子聚集。第一个聚集avg_price是一个针对AvgTicketPrice以500为间隔的histogam聚集,第二个则聚集weather则一个针对OriginWeather的terms聚集。sources参数中还可以定义更多的聚集,它们会以笛卡儿乘积的形式组合起来。在返回结果中除了由各聚集组合形成的桶以外,还有一个after_key字段:

{

"aggregations" : {

"price_weather" : {

"after_key" : {

"avg_price" : 500.0,

"weather" : "Cloudy"

},

"buckets" : [

{

"key" : {

"avg_price" : 0.0,

"weather" : "Clear"

},

"doc_count" : 795

},

{

"key" : {

"avg_price" : 0.0,

"weather" : "Cloudy"

},

"doc_count" : 809

},

{

"key" : {

"avg_price" : 0.0,

"weather" : "Damaging Wind"

},

"doc_count" : 303

},

{

"key" : {

"avg_price" : 0.0,

"weather" : "Hail"

},

"doc_count" : 373

},

{

"key" : {

"avg_price" : 0.0,

"weather" : "Heavy Fog"

},

"doc_count" : 292

},

{

"key" : {

"avg_price" : 0.0,

"weather" : "Rain"

},

"doc_count" : 738

},

{

"key" : {

"avg_price" : 0.0,

"weather" : "Sunny"

},

"doc_count" : 744

},

{

"key" : {

"avg_price" : 0.0,

"weather" : "Thunder & Lightning"

},

"doc_count" : 357

},

{

"key" : {

"avg_price" : 500.0,

"weather" : "Clear"

},

"doc_count" : 1377

},

{

"key" : {

"avg_price" : 500.0,

"weather" : "Cloudy"

},

"doc_count" : 1365

}

]

}

}

}

它包含自前聚集结果中最后一个结果的key。所以请求下一页聚集结果就可以通过after和size参数值定,例如:

POST /kibana_sample_data_flights/_search?filter_path=aggregations

{

"aggs": {

"price_weather": {

"composite": {

"after": {

"avg_price": 500,

"weather": "Cloudy"

},

"sources": [

{

"avg_price": {

"histogram": {

"field": "AvgTicketPrice",

"interval": 500

}

}

},

{

"weather": {

"terms": {

"field": "OriginWeather"

}

}

}

]

}

}

}

}

adjacency_matrix又叫邻接矩阵,是图论中的概念,描述顶点之间的相邻关 系,adjacency_matrix聚集因为牵涉到这些概念,略过,感兴趣的同学可以自行研究。

第六章 ES高级搜索—聚集查询(上)相关推荐

  1. 《软件测试的艺术》第六章 更高级别的测试

    <软件测试的艺术>第六章 更高级别的测试 6.0 前言 软件开发过程模型 6.1 功能测试 6.2 系统测试 6.2.1 能力测试 6.2.2 容量测试 6.2.3 强度测试 6.2.4 ...

  2. Flask 教程 第十六章:全文搜索

    本文转载自:https://www.jianshu.com/p/56cfc972d372 这是Flask Mega-Tutorial系列的第十六部分,我将在其中为Microblog添加全文搜索功能. ...

  3. es高级客户端聚合查询api快速入门

    //聚合查询@Testvoid Collection_query() throws IOException {SearchRequest searchRequest = new SearchReque ...

  4. 第十六章 网络高级编程

    尽管大多数应用层协议都是基于TCP的,但除了编写像QQ服务器那样的服务端应用,很少直接使用 TCP Socket 进行编程.一般都编写基于应用层网络协议(HTTP.FTP等)的应用都是直接使用封装相应 ...

  5. C++学习:第六章Linux高级编程 - (七)信号、sigqueue、sigaction、IPC、管道、匿名管道

    回顾: 1. 信号的作用 2. 理解信号: 软中断 可靠与不可靠信号 kill -l 3. 信号发送与注册 kill/raise alarm setitimer signal 4. 信号的屏蔽 sig ...

  6. 网络营销教程—SEO 第六章提交你的网站(上)

    张栋伟 一.直接向搜索引擎提交  实施搜索引擎营销,首先就是要使得网站被搜索引擎收录.要被搜索引擎收录,除了等待搜索引擎的爬虫程序找到你的网站后进行索引之外,还可以主动向搜索引擎提交网站.目前国内各类 ...

  7. 【数据分析 R语言实战】学习笔记 第六章 参数估计与R实现(上)

    6.1点估计及R实现 6.1.1矩估计 R中的解方程函数: 函数及所在包:功能 uniroot()@stats:求解一元(非线性)方程 multiroot()@rootSolve:给定n个(非线性)方 ...

  8. 数据库9:联结表 高级联结 组合查询 全文本搜索

    第十五章联结表 Sql最强大的功能之一就是能在数据检索查询的执行中联结(join)表.联结是利用sql的select能执行的最重要的操作,能很好的理解联结及其语法是学习sql的一个极为重要的组成部分. ...

  9. elasticsearch高级搜索功能多维度分享

    目录 一.业务搜索核心功能 二.高级搜索匹配功能 三.搜索排序功能 elasticsearch高级搜索功能多维度分享,这也是实战的比较之路,此次我们全面分享常用的业务情景,全覆盖功能分享,让大家有一览 ...

最新文章

  1. flex4实现图片的动态切换
  2. 手撕一个spirng IoC的过程
  3. 让Sandcastle为你的类库生成类似于MSDN的帮助文档
  4. 3种关闭linux系统端口方法
  5. 斯坦福大学#深度多任务学习与元学习#视频及讲义下载
  6. linux dd 光标在闪,linux dd详解
  7. 如何强制子div为父div的高度的100%而不指定父级的高度?
  8. kindle底层系统详细说明
  9. 基于蚁群算法的二维路径规划算法
  10. mac下使用自带的svn客户端上传、下载文件到服务端
  11. 写给准备看CCNA题库的朋友们 希望有些帮助
  12. iPhone长截图快捷指令(科技兽修改版)
  13. 面试一个应届生,从不起眼到令人刮目相看
  14. 电大计算机网络考试,电大计算机组网技术试题及答案|2017电大统考计算机试题及答案...
  15. 推荐几个短网址赚钱站
  16. 音视频入门系列-音视频基础知识篇(录播、点播、直播)
  17. 区块链ICO是什么意思?回答所有你关于ICO的问题
  18. linux去重复程序,Linux下大文件的排序和去重复
  19. 读书印记 - 《心流:最优体验心理学》
  20. JAVA黑白棋之算法浅析

热门文章

  1. java 图片去除黑边,头顶黑边,求大神解决
  2. JEPF软件快速开发平台【免费发布】
  3. javaweb记账本系统
  4. 为什么电源中经常用肖特基二极管
  5. vue中textarea监听粘贴事件获取图片
  6. 狗年出生的宝宝取名的五大方法介绍,快来为你的宝宝起名吧
  7. c++ return返回值与内存
  8. 嵌入式输入系统应用编程
  9. 开源电子书阅读器 LibreraReader 编译环境与编译
  10. 原生拖拽太拉跨了,纯JS自己手写一个拖拽效果,纵享丝滑