我们知道在动态 mapping 启动后,一个索引的字段可能会随着导入文档字段数的增加而自动增加,在有些情况下会发生 “映射爆炸”,也就是说字段数超过我们容忍的范围,而且另外一个坏处是,随着字段的增加,导入的速度会变慢,这是因为更多的字段需要被分词。更多被分词的字段意味着更多的磁盘空间。这种情况在很多的情况下是不允许的。在这种情况下,我们可以动态创建 runtime fields。这个方法的好处是:我们可以针对喜欢的字段在 mapping 中进行定义,而对于那么不那么重要的字段可以建立 runtime fields,虽然他们被搜索的速度会有所影响。为了平衡搜索效果和灵活性,你通常会搜索并过滤索引字段,例如时间戳。运行查询时,Elasticsearch 首先自动使用这些索引字段,从而缩短了响应时间。然后,你可以使用 runtime fields 来限制 Elasticsearch 计算其值所需的字段数。将索引字段与 runtime fields 一起使用可为你索引的数据以及如何定义其他字段的查询提供灵活性。
在今天的练习中,我们将通过一个例子来展示如何动态地创建 runtime fields。
例子
如果 dynamic 参数设置为 runtime 的情况下启用了 dynamic field mapping,则新字段将作为 runtime fields 自动添加到索引映射中。我们使用如下的命令来创建一个 index template:
PUT _index_template/my_dynamic_index
{
"index_patterns": [
"my_dynamic_index-*"
],
"template": {
"mappings": {
"dynamic": "runtime",
"properties": {
"timestamp": {
"type": "date",
"format": "yyyy-MM-dd"
},
"response_code": {
"type": "integer"
}
}
}
}
}
在上面我们定义了一个叫做 my_dynamic_index-* 的 index pattern 的索引模板。但凡以 my_dynamic_index- 为开头的任何索引将具有的 mapping。在上面我们定义了两个字段: timestamp 及 response_code。同时我们也启动了dynamic 为 runtime。这意味着任何文档的字段名称不是 timestamp 和 response_code 的都将被定义为 runtime fields。它们在文档导入时,不会被分词,但是在搜索时,会自动被生成 runtime fields 而使用。
接下来,我们来创建一个如下的索引 my_dynamic_index-1:
POST my_dynamic_index-1/_bulk
{"index":{}}
{"timestamp": "2021-01-01", "response_code": 200, "new_data": "data-1", "another_data": "another1"}
{"index":{}}
{"timestamp": "2021-01-01", "response_code": 200, "new_data": "data-1", "another_data": "another2"}
{"index":{}}
{"timestamp": "2021-01-01", "response_code": 200, "new_data": "data-2", "another_data": "another3"}
{"index":{}}
{"timestamp": "2021-01-01", "response_code": 200, "new_data": "data-2", "another_data": "another4"}
在上面我们看到 timestamp 及 response_codeo 这两个字段,但是我们也看到 new_data 以及 another_data 两个字段。由于这两个字段没有在 mapping 中被定义,而 dynamic 也被设置为 runtime,那么这两个字段将被设置为 runtime fields。执行完上面的命令后,我们可以通过如下的命令来查看这个索引的 mapping:
GET my_dynamic_index-1/_mapping
上面的命令显示:
{
"my_dynamic_index-1" : {
"mappings" : {
"dynamic" : "runtime",
"runtime" : {
"another_data" : {
"type" : "keyword"
},
"new_data" : {
"type" : "keyword"
}
},
"properties" : {
"response_code" : {
"type" : "integer"
},
"timestamp" : {
"type" : "date",
"format" : "yyyy-MM-dd"
}
}
}
}
}
从上面我们可以看出来 new_data 及 another_data 都被设置为 runtime fields,而且他们的类型为 keyword。
我们可以通过如下的方式来进行查询:
GET my_dynamic_index-1/_search
{
"query": {
"match": {
"new_data": "data-2"
}
}
}
上面命令的响应为:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "my_dynamic_index-1",
"_type" : "_doc",
"_id" : "xVhmmXcBQeGXF-ccp1Km",
"_score" : 1.0,
"_source" : {
"timestamp" : "2021-01-01",
"response_code" : 200,
"new_data" : "data-2",
"another_data" : "another3"
}
},
{
"_index" : "my_dynamic_index-1",
"_type" : "_doc",
"_id" : "xlhmmXcBQeGXF-ccp1Km",
"_score" : 1.0,
"_source" : {
"timestamp" : "2021-01-01",
"response_code" : 200,
"new_data" : "data-2",
"another_data" : "another4"
}
}
]
}
}
从上面的结果中,我看可以看到有两个文档被搜索到。表明上看起来和我们正常的搜索没有什么不同,就好像一个正常的字段一样,但是必须指出的是针对大量的文档来说它的效率没有在 mapping 中定义的那些字段那么高。如果在以后的使用中,我们发现 new_data 这个字段需要在导入时被分词,从而提高效率,你可以重新修改 index template 为:
PUT _index_template/my_dynamic_index
{
"index_patterns": [
"my_dynamic_index-*"
],
"template": {
"mappings": {
"dynamic": "runtime",
"properties": {
"timestamp": {
"type": "date",
"format": "yyyy-MM-dd"
},
"response_code": {
"type": "integer"
},
"new_data": {
"type": "keyword"
}
}
}
}
}