欢迎访问我的博客,你的支持,是我最大的动力!

elasticsearch技术解析与实战(四) 聚合

ELK Stack 小马奔腾 11230℃ 评论

聚合是基于搜索的数据汇总,通过组合可以完成复杂的操作。聚合可以对文档的数据进行统计汇总、分组等。

聚合的分类
通常将聚合分为三大类:
度量聚合:在一组文档中对某一个数字型字段进行计算得出指标值
分组聚合:创建多个分组,每个分组都关联一个关键字和相关文档标准。当聚合执行时,所有分组会根据自身标准评估每一个符合的文档,当文档匹配分组时,会将文档划入相关的分组内。聚合进行完成时,会给出一个分组列表,每个分组都会有一组属于它的文档
管道聚合:这一类聚合的数据源是其他聚合的输出,然后进行相关指标的计算
此外,还可以在分组聚合的基础上添加聚合并执行,即聚合的嵌套,通过嵌套可以完成很多复杂的聚合操作分组聚合可以拥有子聚合(分组聚合或度量聚合),子聚合将计算由父聚合生成的分组,对于聚合嵌套的水平和深度没有硬性限制,可以在一个父聚合下嵌套一个或多个子聚合
聚合的基本结构:

聚合操作用aggregations简写为aggs关键字作为开头,操作的对象是JSON数据结构。
聚合值的数据源,一些聚合针对参与聚合的文档中提取出来的值进行,也可以定义一个脚本,针对每个文档生成这些值
当一个聚合里面同时包含文档字段和脚本时,脚本会被视为值脚本。当使用脚本时,可以定义lang和params,lang定义使用的语言,params表示参数
脚本也可对每个文档生成一个或多个值,当生成多个值时,可以使用script_values_sorted来表明这些值是否需要进行排序。实质上,es处理排序过的值时可以执行优化
度量聚合

度量聚合从文档中提取出来值并进行计算,这些值通常从文档中的字段(数据字段)中提取出来,也可以使用脚本进行计算
数字型度量聚合是一种特殊类型的度量聚合,输出数字类型的值。聚合输出一个数字指示(如平均值聚合)称为单值数字型度量聚合,产生多个指标值(如统计聚合)称为多值数字型度量聚合。当聚合直接作为一些分组聚合的子聚合时,单值和多值度量聚合的内容会发挥巨大作用,如分组聚合可以对度量聚合后的返回值进行排序

平均值聚合

计算平均值
{"aggs":{"avg_grade":{"avg":{"field":"grade"}}}}
POST secisland/_search {"aggs":{"avg_grade":{"avg":{"field":"input"}}}}

1、脚本
基于脚本计算
POST secisland/_search {"aggs":{"avg_grade":{"avg":{"script":"doc['input'].value"}}}}
#使用文件脚本
{"aggs":{"avg_grade":{"avg":{"script":{"file":"my_script","params":{"field":"grade"}}}}}}
若需要使用被索引脚本,仅需将file参数替换为id参数

2、文档值脚本
可以对文档中具体某列的值用脚本进行再次计算
{"aggs":{..."aggs":{"avg_corrected_grade":{"avg":{"field":"grade","script":{"inline":"_value*correction","params":{"correction":1.2}}}}}}}

3、默认值
missing定义文档缺失值时应该如何处理,默认这些文档会被忽略,但也可以设置一个默认值
{"aggs":{"grade_avg":{"avg":{"field":"grade","missing":10}}}}
当grade字段没有值时,系统会用10作为值进行计算

基数聚合
单值度量聚合,计算不同值的近似计数。值从文档的特定字段提取,也可以通过脚本生成
#统计符合条件的作者author
{"aggs":{"author_count":{"cardinality":{"field":"author"}}}}
#统计一共有多少个不同的ip
POST secisland/_search {"aggs":{"author_count":{"cardinality":{"field":"xffip.raw"}}}}
聚合支持precision_threshold选项
{"aggs":{"author_count":{"cardinality":{"field":"author_hash","precision_threshold":100}}}}
{"aggs":{"author_count":{"cardinality":{"field":"xffip.raw","precision_threshold":100}}}}
precision_threshold选项允许为了准确性而设置交换内存,并且定义了一种独特的计数。低于计数将接近准确,高于这个值计数可能会变得模糊。最大值为40000,高于40000时,产生的效果与40000相同。默认值取决于创造多分组的父聚合数量1、计数是近似值
计算精确计数要求加载值到一个哈希集并且返回它的大小,当工作在高基数集时,需要占用大量的内存空间,同时在节点间沟通每个分片集也会占用大量的集群资源,所以计算的效率比较低。基数聚合基于HyperLogLog++算法
2、脚本
基数聚合指标支持脚本,但会带来明显的性能损失
{"aggs":{"author_count":{"cardinality":{"script":"doc['author.first_name'].value+' '+doc['author.last_name'].value"}}}}
{"aggs":{"input_output":{"cardinality":{"script":"doc['input'].value+doc['output']"}}}}
也可以使用文件脚本:
{"aggs":{"author_count":{"cadinality":{"script":{"file":"my_script","params":{"first_name_field":"author.first_name","last_name_field":"author.last_name"}}}}}}
#索引脚本只需要将file参数替换为id参数
3、默认值
missing字段定义了文档缺失值时应如何处理,默认会忽略这些文档,但也可以认为它们有一个默认值存在:
{"aggs":{"tag_cardinality":{"cardinality":{"field":"tag","missing":"N/A"}}}}
#tag字段中没有内容的文档会划入相同的分组里,这些分组的默认值是N/A
最大值聚合
最大值聚合是一个单值度量聚合,记录和返回从聚合的文档中提取出的数字型值中的最大值
这些值可以从文档中特定数字型字段提取,也可以通过脚本生成
#计算最高价格price
{"aggs":{"max_price":{"max":{"field":"price"}}}}
POST secisland/_search {"aggs":{"max_input":{"max":{"field":"input"}}}}1、脚本
{"aggs":{"max_price":{"max":{"script":"doc['price'].value"}}}}
上面这条语句会解释为内联脚本:
{"aggs":{"max_price":{"max":{"script":{"file":"my_script","params":{"field":"price"}}}}}}
索引脚本只需将file参数替换为id参数
2、值脚本
#换算币种之间的汇率
{"aggs":{"max_price_in_euros":{"max":{"field":"price","script":{"inline":"_value*conversion_rate","patams":{"conversion_rate":1.2}}}}}}
最小值聚合
最小值聚合是一个单值度量聚合,记录和返回从聚合的文档中提取出的数字型值中的最小值。这些值可以从文档的特定数字型字段提取,也可以通过脚本生成
#计算最低价格
{"aggs":{"min_price":{"min":{"field":"price"}}}}
{"aggs":{"max_input":{"min":{"field":"input"}}}}
和聚合
和聚合是一个单值度量聚合,对聚合文档中提取的数字型进行求和,这些值可以从特定数字型字段提取,也可以通过脚本生成
{"aggs":{"intraday":{"sum":{"field":"change"}}}}
值计数聚合
值计数聚合是一个单值度量聚合,对聚合文档中提取的值进行计数。这些值可以从文档的特定数字型字段提取,也可以通过脚本生成
{"aggs":{"grades_count":{"value_count":{"field":"grade"}}}}
{"aggs":{"max_input":{"value_count":{"field":"input"}}}}
通常情况下,这个聚合会与其他单值聚合一块使用,如在计算平均聚合时,可能也需要知道有多少值参与了平均运算
统计聚合
统计聚合是一个多值度量聚合,对聚合文档提取数字型值进行统计计算,这些值可以从文档特定的数字型字段提取,也可以通过脚本生成
{"aggs":{"grades_stats":{"stats":{"field":"grade"}}}}
{"aggs":{"max_input":{"stats":{"field":"input"}}}}
#"count": 104427,
#"min": 150,
#"max": 1248,
#"avg": 659.397033334291,
#"sum": 68858854
统计聚合包含:最小值、最大值、和、计数、平均数
百分比聚合
百分比聚合是一个多值度量聚合,对聚合文档中提取的数字型值计算一个或多个百分比。这些值可以从文档的特定数字型字段提取,也可以通过脚本生成
百分比聚合得到的点是在特定比例下出现的观测值,如,95百分比对应值表示这个值大于95%的所有值
百分比聚合常用于寻找极端值,在正态分布中,0.13%和99.87%代表三个标准偏差的平均值,任何落在三个标准偏差之外的数据通常被认为是出现了异常
可以利用百分比聚合的结果评估数据分布,判断数据是否扭曲,分析数据是否符合双峰分布等
#网站加载时间的百分比聚合
{"aggs":{"load_time_outlier":{"percentiles":{"field":"load_time"}}}}
{"aggs":{"max_input":{"percentiles":{"field":"input"}}}}
#"1.0": 617,
#"5.0": 617,
#"25.0": 617,
#"50.0": 673,
#"75.0": 678,
#"95.0": 688,
#"99.0": 688
通常管理员感兴趣的是极端的百分比
{"aggs":{"load_time_outlier":{"percentiles":{"field":"load_time","percents":[95,99,99.9]}}}}
{"aggs":{"max_input":{"percentiles":{"field":"input","percents":[95,99,99.9]}}}}
百分比分级聚合
百分比分级聚合是一个多值度量聚合,对聚合文档中提取的数字型值计算一个或多个级别的百分比。这些值可以从文档的特定数字型字段提取,也可以通过脚本生成
百分比等级表示测试值低于某一特定值的百分比,如一个值大于或等于所有测试值的95%,就表示这个值在第95百分比等级
#网站加载时间,规定95%的页面在15ms内完成加载,99%的页面在30ms内完成加载
{"aggs":{"load_time_outlier":{"percentlie_ranks":{"field":"load_time","values":[15,30]}}}}
{"aggs":{"max_input":{"percentile_ranks":{"field":"input","values":[600,650]}}}}
#"600.0": 0.32348885865620347,
#"650.0": 32.34875328113122
最高命中排行聚合
最高命中排行聚合会在聚合文档中找出相关度最高的文档,这个聚合用来作为子聚合使用,这样每个分组中相关度最高的文档就可以聚合在一起
选项参数:
from 第一个结果的偏移量
size 每个分组返回的命中文档的最大数量,默认返回前三个命中文档
sort 指定最高命中文档应该如何排序,默认情况下按照主查询的分数排序
脚本度量聚合

利用脚本执行的度量聚合,提供一个指标输出
{"query":{"match_all":{}},"aggs":{"profit":{"scripted_metric":{"init_script":"_agg['transactions']=[]","map_script":"if (doc['type'.value == \"sale\"){_agg.transactions.add(doc['amount'].value)} else {_agg.transactions.add(-1*doc['amount'].value)}","combine_script":"profit =0;for (t in _agg.transactions){profit +=t};return profit","reduce_script":"profit=0;for (a in _aggs){profit+=a};return profit"}}}}

map_script是唯一必要的参数,上面的示例演示了如何使用脚本聚合通过销售和交易成本来计算总利润,可能的响应是"profit":{"value":170}
也可以指定使用的脚本文件:
{"query":{"match_all":{}},"aggs":{"profit":{"scripted_metric":{"init_script":{"file":"my_init_script"},"map_script":{"file":"my_map_script"},"combine_script":{"file":"my_combine_script"},"params":{"field":"amount"},"reduce_script":{"file":"my_reduce_script"}}}}}
其中init、map和combine脚本的参数必须在全局params对象中指定,这样才可以在脚本间共享参数

1、允许的返回值类型
当一个脚本同时包含多个有效脚本对象时,脚本对象必须返回或存储以下类型的_agg对象:
原始类型;字符串;图(只包含这里列出类型的键和值);数组(只包含这里列出的类型的元素)
2、脚本的作用域
脚本度量聚合的执行有4个阶段:
1)初始化脚本init_script:在任何文档的收集之前执行,允许聚合设置任何初始化状态
2)映射脚本map_script:每个被采集的文档都会执行一次脚本,这是唯一必须要有的脚本。如果没有指定联合脚本combine_script,结果状态需要存储在一个名为_agg的对象中
3)联合脚本combine_script:每个分片会在文档采集结束时执行一次脚本,允许聚合从每个分片中统一状态,如果没有提供联合脚本,联合阶段就会返回聚合变量
4)归纳脚本reduce_script:在所有分片返回结果之后,请求节点执行一次脚本,这个脚本用于访问_aggs变量,_aggs变量是每个分片执行联合脚本之后的结果数组。如果没有提供归纳脚本,归纳阶段会返回_aggs变量
注:page231有对上面脚本的实例分析,分析每个阶段的动作
其他参数说明:
params:可选参数,可以作为参数传递给初始化脚本、映射脚本和联合脚本对象,用于控制聚合行为和存储脚本执行的中间状态,如果没有指定,则提供默认值"params":{"_agg":{}}
reduce_params:可选参数,可以作为参数传递给归纳脚本的对象,用于控制归纳阶段的行为,如果没有指定,该变量在归纳脚本执行时不会定义

地理边界聚合
地理边界聚合是一个度量聚合,为一个字段计算包含所有地点值的边界框
{"query":{"match":{"business_type":"shop}},"aggs":{"viewport":{"geo_bounds":{"field":"location","wrap_longitude":true}}}}
#展示查询所有业务类型为商店的文档,并根据文档中位置字段计算出边界框
地理边界聚合指定一个字段用来获得边界
wrap_longitude是可选参数,用于指定边界框是否允许与国际日期变更线重叠,默认为true
地理重心聚合
地理重心聚合是一个度量聚合,从文档的地理点数据类型字段获取的所有坐标值中计算出有利的矩心,注,字段必须是地理点数据类型
{"query":{"match":{"crime":"burglary"}},"aggs":{"centroid":{"geo_centroid":{"field":"location"}}}}
#查询所有犯罪类型为盗窃的文档,并根据文档中位置字段计算出矩心
地理重心聚合经常作为子聚合与其他分组聚合结合使用
{"query":{"match":{"crime":"burglary"}},"aggs":{"towns":{"terms":{"field":"town"},"aggs":{"centroid":{"geo_centroid":{"field":"location"}}}}}}
#用地理重心聚合作为索引分组聚合的子聚合来找出每个城镇中犯盗窃罪最严重的位置
分组聚合

分组聚合不像度量聚合那样通过字段进行计算,而是根据文档创建分组。每个聚合都关联一个标准,决定一个文档是否划入该分组中。分组实际上定义一个文档集,将文档分组,并计算每组文档的数量
分组聚合可以拥有子聚合,子聚合将父聚合的分组进一步细分

子聚合
子聚合是一个特殊的单分组聚合,通过父类型文档的分组聚合产生子类型的分组。子聚合依赖映射中的_parent字段,只有一个选项type,表示父空间的分组应该被映射为哪种子类型
#假如有一个包含问题和答案两个类型的文档的索引,答案类型有_parent字段:{"answer":{"_parent":{"type":question"}}}
#问题类型文档拥有一个标签字段,答案类型文档拥有一个所有者字段。两个字段分别存在于两个不同类型的文档中,通过子聚合可以把一个问题类型的标签分组映射到答案类型文档的所有者分组
{"aggs":{"top-tags":{"terms"{"field":"tags","size":10},"aggs":{"to-answers":{"children":{"type":"answer"},"aggs":{"top-names":{"terms":{"field":"owner.display_name","size":10}}}}}}}}
type指向名为answer的类型/映射
直方图聚合
直方图聚合是一个多分组聚合,可以应用于从文档中提取的数值。在数值上动态创建固定大小(区间)的分组。将数值放入固定区间的分组中,取整算法为:
rem = value % interval
if (rem<0) {rem += interval}
bucket_key =value - rem
#间隔interval必须为整数,分组前数值会被转换为整数,这会造成负浮点数划分分组时的右移问题
{"aggs":{"prices":{"histogram":{"field":"price","interval":50}}}}
POST secisland/_search {"aggs":{"input-value":{"histogram":{"field":"input","interval":"50"}}}}1、最小文档计数
默认的,响应会用空分组填补直方图的空白区间,可以利用min_doc_count设置来修改分组,用以过滤掉空区间
{"aggs":{"input-value":{"histogram":{"field":"input","interval":"50","min_doc_count":1}}}}
默认情况下,直方图返回的分组的范围基于数据本身,即数据中最大值和最小值决定了区间的两个边界,当请求空分组时,会导致混乱,特别是在数据被过滤的情况下,即过滤的范围超出了边界时
extended_bounds可以强制直方图聚合从指定的最小值开始,只有min_doc_count参数为0时,extended_bounds参数才有意义,min_doc_count大于0时,永远不会返回空区间
当extended_bounds.min比文档的值还大时,依然会正常创建分组
{"query":{"constant_score":{"filter":{"range":{"price":{"to":500}}}}},"aggs":{"prices":{"histogram":{"field":"price","interval":50,"extended_bounds":{"min":0,"max":500}}}}}2、排序
返回的分组默认按照它们的键升序排序,当然,也可以通过order来控制排序行为
#通过分组键排序-降序
{"aggs":{"prices":{"histogram":{"field":"price","interval":50,"order":{"_key":"desc"}}}}}
{"aggs":{"input-value":{"histogram":{"field":"input","interval":"50","order":{"_key":"desc"}}}}}
#通过分组键排序-升序
{"aggs":{"prices":{"histogram":{"field":"price","interval":50,"order":{"_cunt":"asc"}}}}}
{"aggs":{"input-value":{"histogram":{"field":"input","interval":"50","order":{"_count":"asc"}}}}}
#如果直方图聚合有一个直接的指标子聚合,后者可以决定分组的排序方式
{"aggs":{"prices":{"histogram":{"field":"price","interval":50,"order":{"price_stats.min":"asc"}},"aggs":{"price_stats":{"stats":{}}}}}}
排序参数"price_stats.min":"asc"会根据名为price_stats的子聚合里的min值对分组进行排序
不需要为price_stats聚合配置价格字段,它会默认从直方图爷聚合中继承3、偏移
分组的区间默认从0开始然后以interval的值为间隔步进,可以使用offset选项来改变分组的范围
日期直方图聚合
日期直方图聚合是一个多分组聚合,只能应用于日期型的值
主要是解决时间间隔的不固定问题(每个月的天数不固定)
{"aggs":{"articles_over_time":{"date_histogram":{"field":"date","interval":"month"}}}}
可用的间隔表达式有:year/month/week/day/hour/quarter/minute/second1、时间键值
指定日期格式
{"aggs":{"articles_over_time":{"date_histogram":{"field":"date","interval":"1M","format":"yyyy-MM-dd"}}}}
返回结果中会不key和key_as_string,前者为Unix时间戳,后者为格式化的时间2、时间区间
默认时间为UTC,可以通过time_zone设置时区,可以为+08:00或者America/Los_Angeles两种格式
{"aggs":{"by_day":{"date_histogram":{"field":"date","interval":"day","time_zone":"+08:00"}}}}3、偏移
offset参数通过+-偏移来修改每个分组的时间起始值,1h代表1小时,1M代表1个月
{"aggs":{"by_day":{"date_histogram":{"field":"date","interval":"day","offset":"+6h"}}}}
时间范围聚合
时间范围聚合是专门用于时间型数据的范围聚合,与普通范围聚合区别在于from和to参数值可以使用日期数学表达式,也可以指定返回的from和to字段的日期格式。
时间范围聚合在每个范围内包含from但不包含to
{"aggs":{"range":{"date_range":{"field":"date","format":"MM-yyyy","ranges":[{"to":"now-10M/M},{"from":"now-10M/M"}]}}}}
范围聚合
范围聚合是基于多组值来源的聚合,由用户定义一系列范围,每个范围代表一个分组
范围聚合的边界范围包含from,但不包含to
{"aggs":{"price_ranges":{"range":{"field":"price","ranges":[{"to":50},{"from":50,"to"100},{"from":100}]}}}}
{"aggs":{"web-input":{"range":{"field":"input","ranges":[{"to":10}]}}}} #小于10的
-- from -- to ---> from在左边,to在右边
过滤聚合
过滤聚合是一个单分组聚合,包含当前文档中所有匹配指定过滤条件的文档,常用于从当前聚合中得到一个具体的文档集
{"aggs":{"red_products":{"filter":{"term":{"color":"red"}},"aggs":{"avg_price":{"avg":{"filed":"price"}}}}}}
#计算所有红色产品的平均价格
{"aggs":{"post-method":{"filter":{"term":{"method":"post"}},"aggs":{"avg_input":{"avg":{"field":"input"}}}}}}
多重过滤聚合
多重过滤聚合定义一个多分组聚合,每个分组关联一个过滤条件,收集所有满足过滤条件的文档
{"aggs":{"messages":{"filters":{"filters":{"errors":{"term":{"body":"error"}},"warnings":{"term":{"body":"warning"}}}},"aggs":{"monthly":{"histogram":{"field":"timestamp","interval":"1M"}}}}}}
#创建两个关于日志数据的分组,一个收集包含错误信息的文档,另一个收集包含警告信息的文档,且每个分组会按月份划分1、匿名过滤
过滤字段可以作为过滤数组提供,经过过滤生成的分组会以请求的顺序返回2、其他分组
设置other_bucket参数可以在响应中添加一个分组,用来收集所有没有匹配到任何给定过滤条件的文档。接受false和true,false表示不计算其他分组,true表示返回other分组,如果是命名过滤聚合,该分组会默认命名为_other_,如果是匿名过滤聚合,该分组会作为最后一个返回
other_bucket_key参数可以修改默认的名称_other_,设置该参数即认为other_bucket为true
{"aggs":{"messages":{"filters":{"other_bucket_key":"other_messages","filters":{"error":{"term":{"body":"error"}},"warning":{"term":{"body":"warning"}}}},"aggs":{"monthy":{"histogram":{"field":"timestamp","interval":"1M"}}}}}}
空值聚合
空值聚合是一个基于字段数据的单分组聚合,对所有缺失字段值(字段值为空或者为null)的文档创建一个分组。通常和其他字段数据分组聚合一起使用,返回由于缺少字段值而不能放入其他任何分组中的所有文档的信息
{"aggs":{"products_without_a_price":{"missing":{"field":"price}}}}
POST secisland/_search {"aggs":{"miss-input":{"missing":{"field":"input"}}}}
嵌套聚合
嵌套聚合是一种特殊的单分组聚合,可以聚合嵌套的文档

嵌套聚合需要定义嵌入文档在顶级文档中的路径,可以在嵌入文档上定义任何类型的聚合
采样聚合
采样聚合是一个过滤聚合,用于子聚合限制得分最高的文档的样本。不同的设置可以用于限制共享一个共同值匹配的数量,如"作者"
使用环境:
重点分析高相关性的匹配而不是包含一大堆低质量的匹配
移除分析中的偏差以确保从不同来源获取内容的公平性
为减少聚合运行的成本,通过significant_tems只处理有用的结果
{"query":{"match":{"text":"iphone"}},"aggs":{"sample":{"sampler":{"shard_size":200,"field":"user.id"},"aggs":{"keywords":{"significant_terms":{"field":"text"}}}}}}
#每个分片只抽取200个结果,significant_terms聚合的结果不会被任何单一过度活跃的用户影响1、分片规模
shard_size参数限制每个分片执行采样的过程中多少高得分文档会被采集,默认值为1002、控制差异
可以任意使用字段或者脚本以及max_doc_per_value来控制任何分片采集具有相同值的文档的最大数量,值的选择来自于一个固定的字段或者由一个脚本动态派生
如果选择的字段或脚本为一个文档生成了多个值,聚合会报错3、字段
利用字段控制差异
{"aggs":{"sample":{"sampler":{"field":"author","max_docs_per_value":3}}}}
max_docs_per_value设置只为分片局部采样的目的应用在每个分片的基础上,不作为在整体搜索结果上提供去重功能的方法4、脚本
利用脚本控制差异
{"aggs":{"sample":{"sampler":{"script":"doc['author'].value+'/'+doc['genre'].value"}}}}
#使用max_docs_per_value的默认值1以结合author和genre字段来确保每个分片采集的样本最多有一个匹配author/genre对
重要索引词聚合
重要索引词聚合返回一组索引词中令人关注或不同寻常的事件
使用示例:
当用户在文本中搜索"禽流感"时,给出建议"H5N1"
从信用卡交易历史报告中,分析持卡人损失,从而确认商户共同特性
指出爆胎率超标的轮胎厂商1、单一分析
最简单的情况下,前台集是匹配查询条件的搜索结果,用于统计对比的后台集是聚合结果的索引
{"query":{"terms":{"force":["British Transport Police"]}},"aggregations":{"significantCrimeTypes":{"significant_terms":{"field":"crime_type"}}}}
#查询所有警察机关的犯罪指数,结果发现自行车盗窃案的比例不符合预期
寻找异常的犯罪类型2、多重分析
执行跨多个类别分析的简单方法,利用父级聚合来划分数据
{"aggregations":{"forces":{"terms":{"field":"force"},"aggregations":{"significantCrimeTypes":{"significant_terms":{"field":"crime_type"}}}}}}
索引词聚合

 

索引词聚合是一个基于聚合产生一个多组的值,每个分组都是由一个独特的值动态创建,类似于词云
{"aggs":{"terms":{"terms":{"field":"collectType"}}}}
POST secisland/_search {"aggs":{"terms":{"terms":{"field":"agent.raw"}}}}
在返回结果中:
doc_count_error_upper_bound代表每个索引词文档计数的错误上限
当存在大量不同索引词时,es只返回排名靠前的索引词
sum_other_doc_count代表所有没有在响应中分组里的文档计数之和
默认,索引词通过文档计数doc_count排序,条件聚合会返回前10的索引词生成的分组,可以通过设置size参数对返回结果的数量进行修改
{"aggs":{"terms":{"terms":{"size":20,"field":"agent.raw"}}}}

1、规模
通过设置size参数,可以定义需要返回的索引词分组的数量。默认,协调搜索进程的节点会请求每个分片提供顶层与规模大小相同的索引词分组,当所有分片响应完成时,节点会缩小最终结果的规模并返回给用户。这意味着如果不同索引词的数量大于规模,返回的列表会发生偏移和失真
如果规模设置为0,规模的大小会是Integer.MAX_VALUE

2、排序
通过设置order参数,可以自定义分组的排序方式。默认,分组根据doc_count参数降序排序
#根据分组doc_count升序排序
{"aggs":{"genders":{"terms":{"field":"gender","order":{"_count":"asc"}}}}}
#根据分组索引词字母顺序升序排序
{"aggs":{"genders":{"terms":{"field":"gender","order":{"_term":"asc"}}}}}
#通过单值指标子聚合排序(通过聚合名称进行定义)
{"aggs":{"genders":{"terms":{"field":"gender","order":{"avg_height":"desc"}},"aggs":{"avg_height":{"avg":{"field":"height"}}}}}}
#通过多值指标子聚合进行排序(通过聚合名称进行定义)
{"aggs":{"genders":{"terms":{"field":gender","order":{"height_stats.avg":"desc"}},"aggs":{"height_stats":{"stats":{"field":"height"}}}}}}
无论聚合路径上最后一个聚合是单分组还是多分组,只要聚合路径是一个单分组类型,就可以基于更“深”一层的聚合对分组进行排序
单分组类型排序会基于分组里文档的数字进行,多组类型应用相同的规则
路径的定义:
聚合分隔符为>
指标分隔符为.
聚合名为<聚合的名称>
指标为<指标的名称(如果是多值度量聚合)>
路径为<聚合名>[<聚合分隔符><聚合名>]*[<指标分隔符><指标>]
{"aggs":{"countries":{"terms":{"field":"address.country","order":{"females>height_stats.avg":"desc"}},"aggs":{"females":{"filter":{"term":{"gender":"female"}},"aggs":{"height_stats":{"stats":{"field":"height"}}}}}}}}
#基于女性人群的平均身高对国家这个指标进行排序
可以通过提供排序标准的数组,利用多个排序标准对分组进行排序:
{"aggs":{"countries":{"terms":{"field":"address.country","order":[{"females>height_stats.avg":"desc"},{"_count":"desc"}]},"aggs":{"females":{"filter":{"term":{"gender":{"female"}}},"aggs":{"height_stats":{"stats":{"field":"height"}}}}}}}}
如果两个分组在所有排序标准下拥有相同的值,将用分组的索引词进行首字母升序排序,确保分组排序一致性

3、过滤值
可以使用include和exclude对创建分组的索引词进行过滤,该过程基于正则表达式的字符串或一组精确值
{"aggs":{"tags":{"terms":{"field":"tags","include":["mazda","honda"],"exclude":"water_.*"}}}}
#include确定哪些值被聚合;exclude确定哪些值不能被聚合。exclude具有优化权,因此,同时包含include和exclude的索引词会被忽略

4、多字段索引词聚合
索引词聚合不支持在同一类型文档的多个字段中采集索引词,原因是索引词聚合不采集字符串索引词自身的值,而是利用全局序数来生成一个字段中所有唯一值的列表。全局序数可以提升性能,但不能跨多个字段
下面两个方式可以用于在多个字段上执行索引词聚合:
脚本,利用脚本在多个字段上重复进行索引词聚合,但会使全局序数优化失效,速度会更慢
copy_to字段,可以在映射中用copy_to创建一个专用字段包含这些字段的值,然后在这个字段上执行聚合,且不会影响全局序数的优化效果

5、采集模式
当字段拥有很多不同的索引词但必要的结果却很少时,可以在上层父聚合精简之后再执行子聚合的计算,这样会更有效率。
通常,聚合树的分支会优先在深度上扩展,然后才会进行精简,这在一些特殊情况下,会非常浪费系统资源且会受到内存的限制
#查询一个电影数据库,找出10个最受欢迎的演员和他们最常合作的5个演员
{"aggs":{"actors":{"terms":{"field":"actors","size":10},"aggs":{"costars":{"terms":{"field":"actors","size":5}}}}}}
#上面查询更优化的方案是:
{"aggs":{"actors":{"terms":{"field":"actors","size":10,"collect_mode":"breadth_first"},"aggs":{"costars":{"terms":{"field":"actors","size":5}}}}}}
当使用广度优先模式时,落入最上分组里的文档会被缓存以便随后复用。通常,默认的深度优先采集模式是最好的选择,但在某此特殊情况下,广度优先会效率显著
使用广度优先采集模式时,不可以在聚合中嵌入top_hits这种需要访问匹配得分信息的聚合,因为这么做需要缓存每个文档的浮点得分值,会造成非常昂贵的内存开销

总体聚合
总体聚合是在搜索执行的环境中定义一个包含所有文档的单分组,环境被搜索的索引和文档类型定义,不受搜索查询本身的影响
总体聚合只能作为顶级聚合使用,因为把总体聚合嵌入其他分组聚合中是没有意义的
{"query":{"match":{"title":"shirt"}},"aggs":{"all_products":{"global":{},"aggs":{"avg_price":{"avg":{"field","price"}}}}}}
{"query":{"match":{"method":"post"}},"aggs":{"all_input":{"global":{},"aggs":{"avp_input":{"avg":{"field":"input"}}}}}}  #会多一个文档总数字段
总体聚合的聚合体是空的
地理点距离聚合
作用于地理点类型字段上面的多组聚合,原理和范围聚合非常像。用户可以定义一个原点和一系列距离范围分组。聚合评估每个文档的值和原点之间的距离,然后基于范围决定文档属于哪个分组
{"aggs":{"rings_around_amsterdam":{"geo_distance":{"field":"location","origin":"52.3760,4.894","ranges":[{"to":100},{"from":100,"to":300},{"from":300}]}}}}
所指定的字段必须是地理点类型(在映射中明确设置)。也可以定义一个地理点类型的字段数组,在聚合期间会考虑所有字段。原点参数可以接受所有地理点类型支持的格式
对象格式:{"lat":52.3760,"lon":4.894},最安全的格式
字符串格式:"52.3760,4.894",第一个代表纬度,第二个代表经度
数组格式:[4.894,52.3760],基于GeoJSON标准,第一个数字代表经度,第二个数字代表纬度
默认距离的单位是米metres,也可以接受其他距离单位:英里miles,英寸inches,码yards,千米kilometers,厘米centimeters,毫米millimeters
{"aggs":{"rings":{"geo_distance":{"field":"location","origin":"52.3760,4.894","unit":"mi","ranges":[{"to":100},{"from":100,"to":300},{"from":300}]}}}}
有三种距离计算模式:sloppy_arc默认,arc最精确,plane最快,可使用distance_type参数设置
地理散列网格聚合
作用于地理点类型字段上的多组聚合,将各个点分配到代表网格中各个单元格的分组里。聚合结果网格可能很稀疏,且只包含匹配到数据的单元格,每个单元格复用自定义精度下产生的地理散列值进行标记,其特点有:
高精度地理散列字符串长度很长且每个网格覆盖很小的地理区域
低精度地理散列字符串长度比较短且每个网格覆盖很大的地理区域
这个聚合使用的地理散列有精度选择,1至12,默认精度是5
#以精度12创建的网格,覆盖的面积小于一平方米,会有非常高的内存开销,结果的体积也会非常大1、简单的低精度请求
{"aggregations":{"myLarge-GrainGeoHashGrid":{"geohash_grid":{"field":"location","precision":3}}}}2、高精度请求
{"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}}}}}}3、在赤道上单元格的面积
单元格面积随着纬度的增大而变小,赤道上是最大的
IPv4范围聚合
像专用的日期范围聚合一样,IPv4范围聚合专用于IPv4类型字段
{"aggs":{"ip_ranges":{"ip_range":{"field":"ip","ranges":[{"to":"10.0.0.5"},{"from":"10.0.0.5"}]}}}}
IP范围也可以用CIDR掩码来定义
{"aggs":{"ip_ranges":{"ip_range":{"field":"ip","ranges":[{"mask":"10.0.0.0/25"},{"mask":"10.0.0.127/25"}]}}}}
管道聚合
管道聚合利用其他聚合产生的结果,不是文档集,用于向输出树添加信息
分类:
父类聚合:在父聚合输出结果的基础上进行管道聚合,可以在现有分组上计算新的分组或聚合
兄弟聚合:在兄弟聚合输出结果的基础上进行管道聚合,可以计算与兄弟聚合相同等级的新聚合
通过buckets_path参数指定请求批标的路径,管道聚合可以引用需要的聚合来执行计算
管道聚合不能拥有子聚合,但可以在buckets_path参数中引入另一个管道聚合,使管道聚合链接起来1、buckets_path语法
大多数管道聚合需要另一个聚合作为它的输入,输入聚合通过buckets_path参数定义,格式为:
聚合分隔符为>
指标分隔符为.
聚合名为<聚合的名称>
指标名为<指标的名称(如果是多值度量聚合)>
路径为<聚合名>[<聚合分隔符><聚合名>]*[<指标分隔符><指标>]
如my_bucket>my_stats.avg会将my_stats中平均值指标作为my_bucket分组聚合的输入
路径跟管道聚合的位置是相对的,并不是绝对路径,路径不能沿聚合树向上返回
#在日期直方图聚合中嵌入移动平均值聚合,并指出兄弟聚合指标the_sum
{"my_date_histo":{"date_histogram":{"field":"timestamp","interval":"day"},"aggs":{"the_sum":{"sum":{"field":"lemmings"}},"the_movavg":{"moving_avg":{"backets_path":"the_sum"}}}}}
#buckets_path通过相对路径the_sum指出聚合指标
buckets_path也用于兄弟管道聚合,管道聚合"靠近"一系列分组聚合而不是嵌入它们里面
#max_bucket聚合利用buckets_path指定一个嵌入兄弟聚合的指标
{"aggs":{"sales_per_month":{"date_histogram":{"field":"date","interval":"month"},"aggs":{"sales":{"sum":{"field":"price"}}}},"max_monthly_sales":{"max_bucket":{"buckets_path":"sales_per_month>sales"}}}}2、特殊路径
buckets_path可以使用特殊的_count路径,让管道聚合使用文档计数作为输入参数。
#移动平均值聚合可以计算每个分组的文档计数而不是给出具体的指标
{"my_date_histo":{"date_histogram":{"field":"timestamp","interval":"day"},"aggs":{"the_movavg":{"moving_avg":{"buckets_path":"_count"}}}}}3、处理空数据
缺口策略是用来通知管道聚合当遭遇到数据缺口时应该执行哪种行为的机制。所有的管道聚合都支持gap_policy参数指定缺口策略,有两种缺口策略可以选择:
路过(skip),将缺失数据分组视为不存在,会跳过这个分组并使用下一个可用值继续计算
插入零值(insert_zeros),用零替代缺失值,管道聚合会照常计算
平均分组聚合
平均分组聚合会计算在一组聚合中指定指标的平均值。指定的指标必须是数字型而且这个组聚合必须是多组聚合。用avg_bucket表示
{"avg_bucket":{"buckets_path":"the_sum"}}
参数如下:
buckets_path,想要计算平均值的分组路径
gap_policy,缺口策略,默认为跳过
format,用于规范聚合输出值的格式,默认为null
#计算每月销售总额的平均值
{"aggs":{"sales_per_month":{"date_histogram":{"field":"date","interval":"month"},"aggs":{"sales":{"sum":{"field":"price"}}}},"avg_monthly_sales":{"avg_bucket":{"buckets_path":"sales_per_month>sales"}}}}
移动平均聚合
给出一组有序的数据,移动平均聚合会在数据上滑动一个固定大小的窗口并计算窗口的平均值
移动平均值是对数据序列进行平滑处理的简单方法,常用于基于时间的数据,如股票价格或服务指标,平滑可以用来消除高频波动或者随机噪声,使低频趋势变得更加容易发现
用moving_avg表示移动平均聚合
{"moving_avg":{"buckets_path":"the_sum","model":"holt","window":5,"gap_policy":"insert_zero","settings":{"alpha":0.8}}}
参数如下
buckets_path,指标路径
model,使用的移动平均加权模型
gap_policy,缺口策略,默认插入零值
window,滑动的窗口大小,默认为5
settings,模型的具体设置,不同的模型有不同的内容
移动平均聚合必须嵌入到一个直方图或者日期直方图聚合中使用
{"my_date_histo":{"date_histogram":{"field":"timestamp","interval":"day"},"aggs":{"the_sum":{"sum":{"field":"lemmings"}},"the_movavg":{"moving_avg":{"buckets_path":"the_sum"}}}}}
可以在直方图聚合里随意加入普通的度量聚合,最后把移动平均聚合嵌入到直方图中,然后buckets_path参数指出一个直方图聚合中的兄弟度量聚合
总和分组聚合
总和分组聚合用于计算一组聚合创建的所有分组中指定指标的和。指定的指标必须是数字型且这个组聚合必须是多组聚合
用sum_bucket表示
{"sum_bucket":{"buckets_path":"the_sum"}}
#计算所有月销售总额sales分组的和
{"aggs":{"sales_per_month":{"date_histogram":{"field":"date","interval":"month"},"aggs":{"sales":{"sum":{"field":"price"}}}},"sum_monthly_sales":{"sum_bucket":{"buckets_path":"sales_per_month>sales"}}}}
总和累计聚合
总和累计聚合父管道聚合,计算父直方图(或日期直方图)聚合中指定的指标的累计值,指定的指标必须是数字型且直方图聚合必须设置min_doc_count为0
用cumulative_sum表示总和累计聚合
{"cumulative_sum":{"buckets_path":"the_sum"}}
#计算所有月销售总额sales分组的累计和
{"aggs":{"sales_per_month":{"date_histogram":{"field":"date","interval":"month"},"aggs":{"sales":{"sum":{"field":"price"}},"cumulative_sales":{"cumulative_sum":{"buckets_path":"sales"}}}}}}
最大分组聚合
最大分组聚合所定义的分组包含一组聚合指定指标的最大值,且同时输出分组的键和值。指定的指标必须是数字型且这个组聚合必须是多组聚合
用max_bucket表示最大分组聚合
{"max_bucket":{"buckets_path":"the_sum"}}
#计算所有月份销售总额的最大值
{"aggs":{"sales_per_month":{"date_histogram":{"field":"date","interval":"month"},"aggs":{"sales":{"sum":{"field":"price"}}}},"max_monthly_sales":{"max_bucket":{"buckets_path":"sales_per_month>sales"}}}}
最小分组聚合
最小分组聚合所定义的分组包含一组聚合指定指标的最小值,并且同时输出分组的键和值。指定的指标必须是数字型且这个组聚合必须是多组聚合
用min_bucket表示最小分组聚合
{"min_bucket":{"buckets_path":"the_sum"}}
#计算所有月份销售总额的最小值
{"aggs":{"sales_per_month":{"date_histogram":{"field":"date","interval":"month"},"aggs":{"sales":{"sum":{"field":"price"}}}},"min_monthly_sales":{"min_bucket":{"bucket_path":"sales_per_month>sales"}}}}
统计分组聚合
统计分组聚合,在一组聚合的所有分组中对一个指标计算各种统计值。指定的指标必须是数字型,且这个组聚合必须是多组聚合
用stats_bucket表示统计分组聚合
{"stats_bucket":{"buckets_path":"the_sum"}}
#计算所有月份销售总额的统计值
{"aggs":{"sales_per_month":{"date_histogram":{"field":"date","interval":"month"},"aggs":{"sales":{"sum":{"field":"price"}}}},"stats_monthly_sales":{"stats_bucket":{"buckets_path":"sales_per_month>sales"}}}}
返回count/min/max/avg/sum的值
百分位分组聚合
百分位分组聚合是在一组聚合的所有分组中对一个指定的指标计算百分比。指定的指标必须是数字型且这个组聚合必须是多组聚合
用percentiles_bucket表示百分比分组聚合
{"percentiles_bucket":{"buckets_path":"the_sum"}}
#对所有月份的销售总额进行百分比计算
{"aggs":{"sales_per_month":{"date_histogram":{"field":"date","interval":"month"},"aggs":{"sales":{"field":"price"}}}},"sum_monthly_sales":{"percentiles_bucket":{"buckets_path":{"sales_per_month>sales","percints":[25.0,50.0,75.0]}}}}
差值聚合
差值聚合是计算一个指定的指标在两个分组之间的差值。指定的指标必须是数字型且必须设置直方图min_doc_count参数为0
用derivative表示差值聚合
{"derivative":{"buckets_path":"the_sum"}}1、一级差值
#计算所有月份销售总额的差值
{"aggs":{"sales_per_month":{"date_histogram":{"field":"date","interval":"month"},"aggs":{"sales":{"sum":{"field":"price"}},"sales_deriv":{"derivative":{"buckets_path":"sales"}}}}}}
需要至少两个数据点才有差值,所以返回的结果中第一个分组没有差值
差值单位默认和sales聚合以及父直方图相同2、二级差值
可以把差值管道聚合链接到另一个差值管道聚合的结果上,计算二级差值
{"aggs":{"sales_per_month":{"date_histogram":{"field":"date","interval":"month"},"aggs":{"sales":{"sum":{"field":"price"}},"sales_deriv":{"derivative":{"buckets_path":"sales"}},"sales_2nd_deriv":{"derivative":{"buckets_path":"sales_deriv"}}}}}}
二级差值聚合的参数buckets_path指向一级差值,一级聚合产生两个差值后,才会产生二级聚合的值

3、单位
差值聚合中,差值的单位是可以指定的,在响应中会返回一个额外的字段normalized_value,汇报在x轴单位下的差值
#计算每个月的销售额的差值,但差值单位是每天的销售总额
{"aggs":{"sales_per_month":{"date_histogram":{"field":"date","interval":"month"},"aggs":{"sales":{"sum":{"field":"price"}},"sales_deriv":{"derivative":{"buckets_path":"sales","unit":"day"}}}}}}
unit指定用于差值计算的x轴的单位,value原单位是每月,normalized_value单位为每天

分组脚本聚合
分组脚本聚合是父管道聚合,过程是执行一个脚本,使每个分组在父多组聚合的指定指标上执行计算。指标必须是数字型且脚本必须返回一个数字型的值
用bucket_script表示分组脚本聚合
{"bucket_script":{"buckets_path":{"my_var1":"the_sum","my_var2":"the_value_count"},"script":"my_var1 / my_var2"}}
my_var1是用在脚本中的分组路径变量名;the_sum是用于变量的指标路径;script是聚合运行的脚本。脚本可以是内联、文件或索引
#利用分组聚合计算每个月T恤销售额占总量的百分比
{"aggs":{"sales_per_month":{"date_histogram":{"field":"date","interval":"month"},"aggs":{"total_sales":{"sum":{"field":"price"}},"t-shirts":{"filter":"term":{"type":"t-shirt"}},"aggs":{"sales":{"sum":{"field":"price"}}}},"t-shirt-percentage":{"bucket_script":{"buckets_path":{"tShirtSales":"t-shirts>sales","totalSales":"total_sales"},"script":"tShirtSales / totalSales * 100"}}}}}}
串行差分聚合
串行差分聚合是一个机制,用时间序列上的值减去不同时间区间或时间周期的值。如,数据点f(x)=f(xt)-f(xt-n),其中n为经过的时间
时间段为1相当于没有时间规范的派生:只是把一个点换到下一个点。单时间周期可以用来移除常量,线性趋势,单周期也可以用来将数据转换为固定序列
用serial_dif表示串行差分聚合
{"serial_diff":{"buckets_path":"the_sum","lag":"7"}}
参数说明:
buckets_path,指标路径
lag,当前分组覆盖历史分组,必须是非零正整数,可选,默认1
gap_policy,缺口策略,可选,默认insert_zero
format,聚合输出值格式,可选,默认null
串行差分聚合必须嵌入到直方图或日期直方图聚合中使用
{"aggs":{"my_date_histo":{"date_histogram":{"field":"timestamp","interval":"day"},"aggs":{"the_sum":{"sum":{"field":"lemmings"}},"thirtieth_difference":{"serial_diff":{"buckets_path":"the_sum","lag":30}}}}}}
分组选择器聚合
分组选择器聚合是在聚合的结果执行一个脚本来决定当前分组是否应该保留。指定的指标必须是数字型且脚本必须返回一个布尔值。如果脚本语言是表达式,一个数字型的返回值也是全法的,在这种情况下,0.0视作假,其他值被视作真
用bucket_selector表示分组选择器聚合
{"bucket_selector":{"buckets_path":{"my_var1":"the_sum","my_var2":"the_value_count"},"script":"my_var1 > my_var2"}}
my_var1是用在脚本中的分组路径变量名,the_sum是用于变量的指标路径;script是聚合运行的脚本,可以是内联、文件或索引
#仅保留每月销售总额小于等于50的分组
{"aggs":{"sales_per_month":{"date_histogram":{"field":"date","interval":"month"},"aggs":{"total_sales":{"sum":{"field":"price"}},"sales_bucket_filter":{"bucket_selector":{"buckets_path":{"totalSales":"total_sales"},"script":"totalSales <= 50"}}}}}}

转载请注明:轻风博客 » elasticsearch技术解析与实战(四) 聚合

喜欢 (1)or分享 (0)