在之前的文章 “Elasticsearch:使用 Runtime fields 对索引字段进行阴影处理以修复错误 – 7.11 发布”,我展示了如何使用 runtime field 来 shadow 一个已有的在 mapping 中的字段,比如 duration。在今天的练习中,我将展示如何创建一个崭新的字段并进行数据的统计。在这里请注意的是:新增加的 runtime field 并不在 source 中添加,而只是在查询时生成的,也即 schema on read。
在接下来的练习中,它包含创建 runtime field 的演示,其中从包含日期的时间戳字段中计算星期几。 然后使用索引字段和新创建的 runtime field 在 Kibana Lens 中创建可视化文件。 Runtime field 是在 Elasticsearch 中读取时为 schema 的实现提供的名称。
展示
我们先来创建一个 index mapping:
#Create the index mapping
PUT date_to_day
{
"mappings": {
"properties": {
"timestamp": {
"type": "date",
"format": "yyyy-MM-dd"
},
"response_code": {
"type": "integer"
}
}
}
}
在上面,我们展示了两个字段: timestamp 以及 response_code。由于有一个时间戳,我们可以从这个时间戳中导出时间所在的 day of week,也就是星期几。这个对于我们想对一周的每一天统计非常有用。
我们通过如下的 bulk API 来导入数据:
#Load a few documents to work with
POST date_to_day/_bulk
{"index":{}}
{"response_code": 200, "timestamp": "2021-01-01"}
{"index":{}}
{"response_code": 300, "timestamp": "2021-01-03"}
{"index":{}}
{"response_code": 200, "timestamp": "2021-01-04"}
{"index":{}}
{"response_code": 400, "timestamp": "2021-01-01"}
{"index":{}}
{"response_code": 300, "timestamp": "2021-01-05"}
{"index":{}}
{"response_code": 200, "timestamp": "2020-12-21"}
{"index":{}}
{"response_code": 200, "timestamp": "2021-01-02"}
{"index":{}}
{"response_code": 200, "timestamp": "2021-01-08"}
{"index":{}}
{"response_code": 300, "timestamp": "2021-01-09"}
{"index":{}}
{"response_code": 400, "timestamp": "2021-01-09"}
由于我们想对一周内的每一天来进行统计。一种办法是重新建立一个新的 mapping。在这个新的 mapping 里包含这个 day of week 的定义。并在数据导入之前我们对数据进行处理。在实际的使用中,面对大量的已有数据,这样的处理可能非常费力。我们可以使用 runtime field 来完成想要的功能。
在搜索请求时使用
由于 runtime field 是动态生成的,它需要计算机来进行处理。在很多的时候,我们并不想修改 mapping 来完成。我们只想针对一些搜索来进行生成这个 runtime field,或者只是作为在修改 mapping 前的一个练习来验证 runtime field 的正确性。我们使用如下的命令来生产这类的 runtime field:
#Create an ephemeral runtime field for day of week and aggregate on it
GET date_to_day/_search
{
"runtime_mappings": {
"day_of_week": {
"type": "keyword",
"script": {
"source": """emit(doc['timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.SHORT, Locale.ROOT))"""
}
}
},
"size": 0,
"aggs": {
"terms": {
"terms": {
"field": "day_of_week"
}
}
}
}
在上面的命令中,我们可以仔细阅读这个部分:
"runtime_mappings": {
"day_of_week": {
"type": "keyword",
"script": {
"source": """emit(doc['timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.SHORT, Locale.ROOT))"""
}
}
},
这个不是是使用 script 来生成一个叫做 day_of_week 的 runtime 字段。而这个字段只存在于这个搜索中。在执行完这个搜索后,这字段将自动消失。这个 day_of_week 字段是根据 timestamp 导引出来的,是之前的 mapping 中完全没有的字段。
上面命令的执行结果为:
{
"took" : 16,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 10,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"terms" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "Fri",
"doc_count" : 3
},
{
"key" : "Sat",
"doc_count" : 3
},
{
"key" : "Mon",
"doc_tgcodecount" : 2
},
{
"key" : "Sun",
"doc_count" : 1
},
{
"key" : "Tue",
"doc_count" : 1
}
]
}
}
}
在上面显示,我们对一周内的每一天进行了统计。
在 index mapping 中使用
当然在很多的情况下,我们希望这个字段一直存在于索引的 mapping 中。这样做的好处是,我们可以在 Kibana 中的可视化中直接使用被定义的 runtime fields。我们可以通过如下的方法来定义:
#Add the runtime field to the index mapping
PUT date_to_day/_mapping
{
"runtime": {
"day_of_week": {
"type": "keyword",
"script": {
"source": """emit(doc['timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.SHORT, Locale.ROOT))"""
}
tgcode }
}
}
我们可以通过如下的命令来查看 date_to_day 索引的 mapping:
{
"date_to_day" : {
"mappings" : {
"runtime" : {
"day_of_week" : {
"type" : "keyword",
"script" : {
"source" : "emit(doc['timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.SHORT, Locale.ROOT))",
"lang" : "painless"
}
}
},
"properties" : {
"response_code" : {
"type" : "integer"
},
"timestamp" : {
"type" : "date",
"format" : "yyyy-MM-dd"
}
}
}
}
}
我们到 Kibana 的 index pattern 中去创建一个索引模式并查看它的字段定义:
从上面我们可以看出来一个新增加的 day_of_week 的字段。
我们可以在 Kibana 中直接使用这个字段并进行可视化:
当然,也许你怀疑是不是索引的 source 是否已经包含新生成的 day_of_week 字段,我们可以通过如下的命令来查看:
GET date_to_day/_search
上面的命令显示:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 10,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "date_to_day",
"_type" : "_doc",
"_id" : "_iyDlXcBjSpwk8PH7vNz",
"_score" : 1.0,
"_source" : {
"response_code" : 200,
"timestamp" : "2021-01-01"
}
},
{
"_index" : "date_to_day",
"_type" : "_doc",
"_id" : "_yyDlXcBjSpwk8PH7vNz",
"_score" : 1.0,
"_source" : {
"response_code" : 300,
"timestamp" : "2021-01-03"
}
},
{
"_index" : "date_to_day",
"_type" : "_doc",
"_id" : "ACyDlXcBjSpwk8PH7vRz",
"_score" : 1.0,
"_source" : {
"response_code" : 200,
"timestamp" : "2021-01-04"
}
},
...
显然,我们的 source 并没有任何tgcode的改变。 day_of_week 只是 schema on read。