使用默认聚合时,开发人员通常无法获得预期的结果。 基本聚合功能也有局限性。 例如,如果要更改直方图的偏移值,就是这种情况。 由于 Elasticsearch 不提供此本机功能,因此我们使用脚本来获取所需的结果。 我们还将介绍其他使用脚本的聚合任务。在我之前的文章 “开始使用Elasticsearch (3)”,它里面也有一些涉及。在今天的文章中,我们我们来做更进一步的探讨。
这个系列的文章有两篇:
准备数据
为了支持下面的示例,我们提供了一个包含虚拟公司员工详细信息的文档集。 我们包括每个雇员的数据,包括姓名,年龄,职位和薪水。 我们创建一个 employee 索引:
PUT /employee/_doc/1
{
"name": "Bob",
"age": 35,
"about": "Bob joined the company as a full time technology consultant in the year 2012",
"position": "consultant",
"salary": 5000,
"experience": "3-years",
"married": 1,
"fullTime": true
}
PUT /employee/_doc/2
{
"name": "Jack",
"age": 30,
"about": "Jack joined the company as a part time management consultant in the year 2013",
"position": "Management consultant",
"salary": 3000,
"experience": "3-years",
"married": 0,
"fullTime": false
}
PUT /employee/_doc/3
{
"name": "Tom",
"age": 33,
"about": "Tom is serving as the operations manager of the firm from the year 2011",
"position": "Operations manager",
"salary": 7000,
"experience": "7-years",
"married": 1,
"fullTime": true
}
我们在 Kibana 中的 Dev Tools 里运行上面的三个命令。这样就生产了一个叫做 employee 的索引。 为简洁起见,在本教程中,我们仅索引三个文档。 当然,你可以更改这些示例中的值并为更多文档建立索引。
使用脚本更改默认直方图值
假设我们的主管需要一个直方图,以便她可以了解每个指定工资间隔(箱)中有多少员工。 直方图应分为 3,000 美元的间隔。 现在,我们有了间隔和数据来执行直方图聚合。 这种聚合存在一个问题:由于间隔为 3,000,因此简单的细分将导致分界点为 3,000、6,000、9,000,依此类推。我们可以使用正常的直方图统计如下:
GET employee/_search
{
"size": 0,
"aggs": {
"NAME": {
"histogram": {
"field": "salary",
"interval": 3000
}
}
}
}
上面的聚合显示的结果为:
"aggregations" : {
"NAME" : {
"buckets" : [
{
"key" : 3000.0,
"doc_count" : 2
},
{
"key" : 6000.0,
"doc_count" : 1
}
]
}
上面显示在 3000到 6000 之间有2个文档,在 6000 以上只有一个文档。
我们的主管澄清并告诉我们,我们需要细分数据,以便我们了解谁的薪水范围为 0-3,000,然后为 3,000-6,000,依此类推。 这是对直方图值的偏移操作,不能使用常规的 Elasticsearch 聚合功能来完成。 当然,本文的重点是我们确实可以使用脚本来完成此任务。
这是可以帮助我们的查询:
GET employee/_search
{
"size": 0,
"aggs": {
"histogramData": {
"histogram": {
"field": "salary",
"interval": 3000,
"script": "_value + 2000"
}
}
}
}
该脚本将值 2,000 添加到默认偏移值,然后根据给定的值(3,000)计算间隔。 这会将偏移值扩展到 8,000。 由于间隔步长为3,000,因此脚本将计算最后一个直方图间隔为 6000-9000。 现在,我们可以根据所需的时间间隔对员工进行明确的划分。
上面的显示结果为:
"aggregations" : {
"histogramData" : {
"buckets" : [
{
"key" : 3000.0,
"doc_count" : 1
},
{
"key" : 6000.0,
"doc_count" : 1
},
{
"key" : 9000.0,
"doc_count" : 1
}
]
}
关于这个 offset 的用法,你可以参阅我之前的文章 “Kibana:运用 agggregation 的高级设置来微调统计结果”。
使用脚本把字段中的值分拆
接下来,我们将从特定字段中仅提取特定数据进行聚合。 索引中的文档包含 experence 字段,其值的形式为“ x-year”(其中 “x” 为数字)。
如果我们尝试进行常规聚合,则我们获得的存储桶将具有 “3-years”,“4-years” 和 “7-years” 之类的名称。 但是,假设我们需要将存储段名称分别为 “ 3”,“4” 和 “7”。 可以通过字符“-”进行分拆,然后仅使用分拆后的第一个元素来完成此操作。如果我们想对字段进行拆分,我们需要把这个字段定义为 keyword 类型。为此,我们重新定义一个索引:
PUT employee_new
{
"mappings": {
"properties": {
"experience": {
"type": "keyword"
}
}
}
}
我们通过 reindex 的方法把之前的 employee 的数据导入:
POST _reindex
{
"source": {
"index": "employee"
},
"dest": {
"index": "employee_new"
}
}
我们聚合的脚本如下:
GET employee_new/_search
{
"size": 0,
"aggs": {
"urls": {
"terms": {
"field": "experience",
"script": "_value.substring(0,1)"
}
}
}
}
上面聚合显示的结果为:
"aggregations" : {
"urls" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "3",
"doc_count" : 2
},
{
"key" : "7",
"doc_count" : 1
}
]
}
在上面,我们看到它显示的 key 为 3 和 7,也就是 3 年及 7 年。我们也也可以把聚合修改为:
GET employee_new/_search
{
"size": 0,
"aggs": {
"urls": {
"terms": {
"script": "doc['experience'].get(0).substring(0,1)"
}
}
}
}
它和上面的结果是一样的。
使用脚本在多个字段上执行术语聚合
使用术语聚合时,通过对多个字段执行聚合,我们可能会获得更多好处。 假设我们要在 about 字段上进行术语聚合。 默认字词汇总只会为我们提供最热门字词的文档计数。 我们可能还需要在 position 字段执行另一个术语汇总,这将返回该字段的主要术语的文档计数。 进一步研究此示例,我们可以看到我们如何在两个字段上都需要一个术语聚合,这对于在相同存储桶中需要两个聚合结果的情况非常重要。
Elasticsearch 术语聚合中没有可用的此类选项。 因此,让我们尝试使用脚本,这实际上很简单。 这是我们如何在 “about” 和 “position” 字段上进行字词汇总的方法。
GET employee/_search
{
"size": 0,
"aggs": {
"union_demo": {
"terms": {
"size": 30,
"script": "doc['about.keyword'].value + ' ' + doc['position.keyword'].value"
}
}
}
}
请注意,在这里,我们给定了一个 size 参数,将其值设置为 30。之所以这样做,是因为因为 about 字段包含许多单词,所以存储桶的数量将大于 10。 Elasticsearch 术语聚合将仅显示 10。此脚本中的此查询将向我们显示从两个字段聚合的术语的并集。
上面聚合显示的结果为:
"aggregations" : {
"union_demo" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "Bob joined the company as a full time technology consultant in the year 2012 consultant",
"doc_count" : 1
},
{
"key" : "Jack joined the company as a part time management consultant in the year 2013 Management consultant",
"doc_count" : 1
},
{
"key" : "Tom is serving as the operations manager of the firm from the year 2011 Operations manager",
"doc_count" : 1
}
]
}
在上面我们把两个字段 position 及 about 组合成一个新的字段来做一个聚合。
总结
在本脚本教程中,我们已经了解了如何实现几种类型的聚合,这些聚合是本机 Elasticsearch 功能无法实现的。我们已经完成了直方图聚合中偏移值的操作,将值拆分到特定字段中的操作,以及如何在多个字段上进行术语聚合。 所有这些都是通过脚本完成的。