1.一般使用
基于 es-7.2版本
Terms
聚合一般情况下是针对字段类型为 keyword
的聚合(text
类型的字段需要开启 fielddata
),用于对指定字段进行唯一匹配,如下:
1 | GET indexName/_search |
indexName
为指定的索引名称;terms_test
为自定义名称,后端程序API
可以通过该名称获取到bucket
结果集;size
指定了返回的topK
条数。
其响应结果类似:
1 | ...... |
doc_count_error_upper_bound
表示没有在这次聚合中返回、但是可能存在的潜在聚合结果,其数量为7,可能会排在最后;sum_other_doc_count
表示未参与本次聚合的文档数量,因为 ES 是分布式部署的,每个分片只返回topK
的结果,其余部分不会扫描。这里设置的size
是3,所以每个分片只会聚合本分片中前三的数据。即有 292 个文档没有参与本次聚合,设置的size
越大,返回结果越准确。但同时也会增加计算成本;buckets
中的是本次term
聚合的结果,这里的doc_count
并不一定是准确的, 有时只是一个近似值,原因同上;
数据不准确的原因,本质上是因为分布式导致的,尤其是当获取 topK
数据时,节点内的 topK
准确不保证整体 topK
准确。 max
、min
、avg
的聚合可以获取准确的结果,但 terms
对于分片数据只能取近似值。
可以通过以下方式提高准确度:
- 不分片(实际情况不现实);
- 增加
size
的大小; - 设置
shard_size
,该参数可以最小化size
设置较大时的计算成本,设置后他会限制从每个分片取回的个数。例如size
设置的取5个,shard_size
设置为取10个。此时分片会返回10个数据减小误差。shard_size
不能小于size
(没有意义),当它比size
小时,es 会重写为size
的大小。
2. show_term_doc_count_error
查询中可以设置该参数为 true
,此时返回的每一个 bucket
都会包含一个误差值的范围 ,在按照升序排序或按照子聚合排序时,es会无法计算该误差值,并返回 -1.
1 | GET search_jw_words/_search |
在实际应用中其实会发现,排名越往后的数据,出现的误差越大,如下:
1 | "buckets" : [ |
3. 使用 include/exclude
来过滤结果中的指定内容
1 | GET indexName/_search |
在我测试的7.2版本中,两者不能同时使用,要么单独设置 exlude
,要么单独设置 include
。
也可以使用正则表达式的方式进行过滤(一开始我想通过正则去除单字的结果,然后后再过滤给定的词典,但并不能起到组合的效果):
1 | GET indexName/_search |
4. 自定义排序
默认状态下,聚合结果会根据返回 bucket
中的 doc_count
来进行降序排序,想要更改的话可以使用以下方式:
官方文档提示:不推荐根据
count
来降序排,本身聚合是从分片取topK
的行为,倒序会使准确度更低。
升序排序的查询方式:
1 | GET indexName/_search |
使用子聚合排序:
1 | GET indexName/_search |
1 | GET /_search |
注意:
pipeline
聚合由于本身机制问题不能用该方式。
官方文档声明为:子聚合的层级可以任意多,只要按照规定的命名方式,就可以完成排序,例如更深一层级的写法为:
1 | GET indexName/_search |
order
规则需满足以下条件:(即 sub1>sub2>....sub100.avg
)
- 聚合层级间使用 “>” ;
- 属性间使用 “.” ;
- 聚合名为自定义的名称。
另外 order
还可以使用数组的方式对多个内容进行排序:
1 | GET indexName/_search |
5. 使用 script
脚本进行聚合
es提供了脚本支持—-内置 painless
脚本语言。(下次会单独解释下脚本如何使用),如下脚本也可以实现对指定字段的聚合。
1 | GET indexName/_search |
要使用已经创建好的脚本的话:
1 | GET indexName/_search |
针对 value
的 script
脚本
1 | GET indexName/_search |
6. 多字段聚合
terms
聚合不支持多字段。想要进行多字段聚合需要使用 script
脚本进行处理,或者使用 copy_to
字段(copy_to
即老版本的 _all
字段,但更加灵活)。
7. collect_mode
collect_mode
分为深度优先搜索(depth_first
)和广度优先搜索(breadth_first
),一般情况下默认使用depth_mode
。但某些时候更适合breadth_first
场景例如:想要查询最受欢迎的10位演员,及其最常见的5位联合主演。虽然在这个数量级下只是获取50个结果,但每增加一个演员都会进行 n² 的增长:
1 | GET indexName/_search |
此时,可以使用 breath_first
模式来优化子聚合的加载:
1 | GET indexName/_search |
breath_first
即先确定好10名最受欢迎的演员,然后再取对应的联合主演。而不是每一个演员都先去确定其联合主演。
8. tips
在多个索引上聚合时,聚合字段的类型可能在每个索引中都不相同。某些类型彼此兼容(
integer
和long
或float
和double
),但是当类型混合使用十进制数和非十进制数时,terms聚合会将非十进制数提升为十进制数。这可能会导致数值的精度下降
9. API
本来想在这写下聚合的 API
,但发现单独扯出来不太完整。后续单独写下 restHighClient
的基本用法吧。