动态模板
动态模板允许您更好地控制 Elasticsearch 映射数据的方式,超越了默认的动态字段映射规则。您可以通过将 dynamic 参数设置为 true 或 runtime 来启用动态映射。然后,您可以使用动态模板根据匹配条件为动态添加的字段定义自定义映射。
match_mapping_type和unmatch_mapping_type对 Elasticsearch 检测到的数据类型进行操作。match和unmatch使用模式来匹配字段名称。path_match和path_unmatch对字段的完整点分路径进行操作。- 如果动态模板没有定义
match_mapping_type、match或path_match,它将不会匹配任何字段。您仍然可以在批量请求的dynamic_templates部分中按名称引用该模板。
在映射规范中使用 {{name}} 和 {{dynamic_type}} 模板变量作为占位符。
动态字段映射仅在字段包含具体值时添加。当字段包含 null 或空数组时,Elasticsearch 不会添加动态字段映射。如果在 dynamic_template 中使用了 null_value 选项,它仅在第一个具有该字段具体值的文档被索引后才会应用。
动态模板被指定为命名对象的数组。
"dynamic_templates": [
{
"my_template_name": {
... match conditions ...
"mapping": { ... }
}
},
...
]
- 模板名称可以是任何字符串值。
- 匹配条件可以包含以下任意项:
match_mapping_type、match、match_pattern、unmatch、path_match、path_unmatch。 - 匹配字段应使用的映射。
如果提供的映射包含无效的映射片段,则会返回验证错误。验证发生在索引时应用动态模板时,并且在大多数情况下,在更新动态模板时也会发生验证。提供无效的映射片段可能会导致在某些条件下更新或验证动态模板失败。
- 如果没有指定
match_mapping_type但该模板对于至少一种预定义映射类型有效,则该映射片段被视为有效。但是,如果匹配该模板的字段被索引为不同类型,则在索引时会返回验证错误。例如,配置一个没有match_mapping_type的动态模板被视为字符串类型是有效的,但如果匹配该动态模板的字段被索引为长整型(long),则在索引时会返回验证错误。建议将match_mapping_type配置为预期的 JSON 类型,或在映射片段中配置所需的type。 - 如果在映射片段中使用了
{{name}}占位符,则在更新动态模板时会跳过验证。这是因为此时字段名称未知。相反,验证会在索引时应用模板时进行。
模板按顺序处理——第一个匹配的模板胜出。当通过 更新映射 API 放入新的动态模板时,所有现有模板都会被覆盖。这允许在最初添加动态模板后对其进行重新排序或删除。
如果您希望 Elasticsearch 将特定类型的新字段动态映射为运行时字段,请在索引映射中设置 "dynamic":"runtime"。这些字段不会被索引,而是在查询时从 _source 加载。
或者,您可以使用默认的动态映射规则,然后创建动态模板将特定字段映射为运行时字段。您在索引映射中设置 "dynamic":"true",然后创建一个动态模板,将特定类型的新字段映射为运行时字段。
假设您的数据中每个字段都以 ip_ 开头。基于动态映射规则,Elasticsearch 会将通过 numeric 检测的任何 string 映射为 float 或 long。但是,您可以创建一个动态模板,将新字符串映射为 ip 类型的运行时字段。
以下请求定义了一个名为 strings_as_ip 的动态模板。当 Elasticsearch 检测到匹配 ip* 模式的新 string 字段时,它会将这些字段映射为 ip 类型的运行时字段。由于 ip 字段不会被动态映射,因此您可以将此模板与 "dynamic":"true" 或 "dynamic":"runtime" 一起使用。
PUT my-index-000001/
{
"mappings": {
"dynamic_templates": [
{
"strings_as_ip": {
"match_mapping_type": "string",
"match": "ip*",
"runtime": {
"type": "ip"
}
}
}
]
}
}
请参阅此示例,了解如何使用动态模板将 string 字段映射为索引字段或运行时字段。
match_mapping_type 参数根据 JSON 解析器检测到的数据类型匹配字段,而 unmatch_mapping_type 则根据数据类型排除字段。
由于 JSON 不区分 long 和 integer,也不区分 double 和 float,因此任何解析出的浮点数都被视为 double JSON 数据类型,而任何解析出的 integer 数字都被视为 long。
使用动态映射时,Elasticsearch 总是会选择较宽的数据类型。唯一的例外是 float,它比 double 需要的存储空间更少,并且对于大多数应用来说精度足够。运行时字段不支持 float,这就是为什么 "dynamic":"runtime" 使用 double 的原因。
Elasticsearch 自动检测以下数据类型
| Elasticsearch 数据类型 | ||
| JSON 数据类型 | "dynamic":"true" |
"dynamic":"runtime" |
null |
未添加字段 | 未添加字段 |
true 或 false |
boolean |
boolean |
double |
float |
double |
long |
long |
long |
object |
object |
未添加字段 |
数组 |
取决于数组中的第一个非 null 值 |
取决于数组中的第一个非 null 值 |
通过 日期检测 的 string |
date |
date |
通过 数值检测 的 string |
float 或 long |
double 或 long |
未通过 date 检测或 numeric 检测的 string |
带有 .keyword 子字段的 text |
keyword |
您可以为 match_mapping_type 或 unmatch_mapping_type 参数指定单个数据类型或数据类型列表。您还可以对 match_mapping_type 参数使用通配符 (*) 来匹配所有数据类型。
例如,如果我们想将所有整数类型的字段映射为 integer 而不是 long,并将所有 string 类型的字段同时映射为 text 和 keyword,我们可以使用以下模板
PUT my-index-000001
{
"mappings": {
"dynamic_templates": [
{
"numeric_counts": {
"match_mapping_type": ["long", "double"],
"match": "count",
"mapping": {
"type": "{dynamic_type}",
"index": false
}
}
},
{
"integers": {
"match_mapping_type": "long",
"mapping": {
"type": "integer"
}
}
},
{
"strings": {
"match_mapping_type": "string",
"mapping": {
"type": "text",
"fields": {
"raw": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
{
"non_objects_keyword": {
"match_mapping_type": "*",
"unmatch_mapping_type": "object",
"mapping": {
"type": "keyword"
}
}
}
]
}
}
PUT my-index-000001/_doc/1
{
"my_integer": 5,
"my_string": "Some string",
"my_boolean": "false",
"field": {"count": 4}
}
my_integer字段被映射为integer。my_string字段被映射为text,并带有keyword多字段。my_boolean字段被映射为keyword。field.count字段被映射为long。
match 参数使用一个或多个模式来匹配字段名称,而 unmatch 使用一个或多个模式来排除被 match 匹配的字段。
match_pattern 参数调整 match 参数的行为,以支持在字段名称上匹配完整的 Java 正则表达式,而不是简单的通配符。例如
"match_pattern": "regex",
"match": "^profit_\d+$"
以下示例匹配所有名称以 long_ 开头的 string 字段(以 _text 结尾的字段除外),并将它们映射为 long 字段
PUT my-index-000001
{
"mappings": {
"dynamic_templates": [
{
"longs_as_strings": {
"match_mapping_type": "string",
"match": "long_*",
"unmatch": "*_text",
"mapping": {
"type": "long"
}
}
}
]
}
}
PUT my-index-000001/_doc/1
{
"long_num": "5",
"long_text": "foo"
}
long_num字段被映射为long。long_text字段使用默认的string映射。
您可以为 match 或 unmatch 字段指定一个使用 JSON 数组的模式列表。
下一个示例匹配所有名称以 ip_ 开头或以 _ip 结尾的字段(以 one 开头或以 two 结尾的字段除外),并将它们映射为 ip 字段
PUT my-index-000001
{
"mappings": {
"dynamic_templates": [
{
"ip_fields": {
"match": ["ip_*", "*_ip"],
"unmatch": ["one*", "*two"],
"mapping": {
"type": "ip"
}
}
}
]
}
}
PUT my-index-000001/_doc/1
{
"one_ip": "will not match",
"ip_two": "will not match",
"three_ip": "12.12.12.12",
"ip_four": "13.13.13.13"
}
one_ip字段未匹配,因此使用text的默认映射。ip_two字段未匹配,因此使用text的默认映射。three_ip字段被映射为ip类型。ip_four字段被映射为ip类型。
path_match 和 path_unmatch 参数的工作方式与 match 和 unmatch 相同,但它们对字段的完整点分路径(例如 some_object.*.some_field)进行操作,而不仅仅是最终名称。
此示例将 name 对象中任何字段的值复制到顶层 full_name 字段,但 middle 字段除外
PUT my-index-000001
{
"mappings": {
"dynamic_templates": [
{
"full_name": {
"path_match": "name.*",
"path_unmatch": "*.middle",
"mapping": {
"type": "text",
"copy_to": "full_name"
}
}
}
]
}
}
PUT my-index-000001/_doc/1
{
"name": {
"first": "John",
"middle": "Winston",
"last": "Lennon"
}
}
以下示例对 path_match 和 path_unmatch 都使用了模式数组。
name 对象或 user.name 对象中任何字段的值都会被复制到顶层 full_name 字段,但 middle 和 midinitial 字段除外
PUT my-index-000001
{
"mappings": {
"dynamic_templates": [
{
"full_name": {
"path_match": ["name.*", "user.name.*"],
"path_unmatch": ["*.middle", "*.midinitial"],
"mapping": {
"type": "text",
"copy_to": "full_name"
}
}
}
]
}
}
PUT my-index-000001/_doc/1
{
"name": {
"first": "John",
"middle": "Winston",
"last": "Lennon"
}
}
PUT my-index-000001/_doc/2
{
"user": {
"name": {
"first": "Jane",
"midinitial": "M",
"last": "Salazar"
}
}
}
path_match 和 path_unmatch 参数不仅匹配叶子字段,还匹配对象路径。例如,索引以下文档会导致错误,因为 path_match 设置也匹配了对象字段 name.title,它不能被映射为 text
PUT my-index-000001/_doc/2
{
"name": {
"first": "Paul",
"last": "McCartney",
"title": {
"value": "Sir",
"category": "order of chivalry"
}
}
}
{{name}} 和 {{dynamic_type}} 占位符会在 mapping 中被替换为字段名称和检测到的动态类型。以下示例将所有字符串字段设置为使用与字段同名的 analyzer,并为所有非字符串字段禁用 doc_values
PUT my-index-000001
{
"mappings": {
"dynamic_templates": [
{
"named_analyzers": {
"match_mapping_type": "string",
"match": "*",
"mapping": {
"type": "text",
"analyzer": "{name}"
}
}
},
{
"no_doc_values": {
"match_mapping_type":"*",
"mapping": {
"type": "{dynamic_type}",
"doc_values": false
}
}
}
]
}
}
PUT my-index-000001/_doc/1
{
"english": "Some English text",
"count": 5
}
english字段被映射为带有english分析器的string字段。count字段被映射为禁用了doc_values的long字段。
以下是一些可能有用的动态模板示例
当您设置 "dynamic":"true" 时,Elasticsearch 会将字符串字段映射为带有 keyword 子字段的 text 字段。如果您只索引结构化内容且对全文搜索不感兴趣,您可以让 Elasticsearch 仅将您的字段映射为 keyword 字段。但是,您必须搜索索引时的完全相同的值才能搜索这些字段。
PUT my-index-000001
{
"mappings": {
"dynamic_templates": [
{
"strings_as_keywords": {
"match_mapping_type": "string",
"mapping": {
"type": "keyword"
}
}
}
]
}
}
与前面的示例相反,如果您只关心字符串字段的全文搜索,并且不打算进行聚合、排序或精确搜索,您可以指示 Elasticsearch 将字符串映射为 text
PUT my-index-000001
{
"mappings": {
"dynamic_templates": [
{
"strings_as_text": {
"match_mapping_type": "string",
"mapping": {
"type": "text"
}
}
}
]
}
}
或者,您可以在映射的运行时部分创建一个动态模板,将您的字符串字段映射为 keyword 字段。当 Elasticsearch 检测到 string 类型的新字段时,这些字段将被创建为 keyword 类型的运行时字段。
虽然您的 string 字段不会被索引,但它们的值存储在 _source 中,可用于搜索请求、聚合、过滤和排序。
例如,以下请求创建了一个动态模板,将 string 字段映射为 keyword 类型的运行时字段。虽然 runtime 定义为空,但新的 string 字段将基于 Elasticsearch 用于将字段类型添加到映射的动态映射规则被映射为 keyword 运行时字段。任何未通过日期检测或数值检测的 string 都会自动映射为 keyword
PUT my-index-000001
{
"mappings": {
"dynamic_templates": [
{
"strings_as_keywords": {
"match_mapping_type": "string",
"runtime": {}
}
}
]
}
}
索引一个简单的文档
PUT my-index-000001/_doc/1
{
"english": "Some English text",
"count": 5
}
当您查看映射时,您会看到 english 字段是一个 keyword 类型的运行时字段
GET my-index-000001/_mapping
{
"my-index-000001" : {
"mappings" : {
"dynamic_templates" : [
{
"strings_as_keywords" : {
"match_mapping_type" : "string",
"runtime" : { }
}
}
],
"runtime" : {
"english" : {
"type" : "keyword"
}
},
"properties" : {
"count" : {
"type" : "long"
}
}
}
}
}
Norms 是索引时的评分因子。如果您不关心评分(例如,如果您从不按分数对文档进行排序),您可以禁用这些评分因子在索引中的存储,从而节省一些空间。
PUT my-index-000001
{
"mappings": {
"dynamic_templates": [
{
"strings_as_keywords": {
"match_mapping_type": "string",
"mapping": {
"type": "text",
"norms": false,
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
]
}
}
此模板中出现子 keyword 字段是为了与动态映射的默认规则保持一致。当然,如果您不需要它们,因为您不需要对该字段执行精确搜索或聚合,您可以按照上一节所述将其删除。
在使用 Elasticsearch 进行时间序列分析时,通常会有许多数字字段,您经常会对这些字段进行聚合,但从不进行过滤。在这种情况下,您可以禁用这些字段上的索引以节省磁盘空间,并可能获得一些索引速度的提升
PUT my-index-000001
{
"mappings": {
"dynamic_templates": [
{
"unindexed_longs": {
"match_mapping_type": "long",
"mapping": {
"type": "long",
"index": false
}
}
},
{
"unindexed_doubles": {
"match_mapping_type": "double",
"mapping": {
"type": "float",
"index": false
}
}
}
]
}
}
- 与默认的动态映射规则一样,double 被映射为 float,这通常足够精确,但需要的磁盘空间只有前者的一半。