ES中的动态映射和动态模板
为了方便演示和切换 ES 地址,在
~/.bashrc
中添加如下变量和脚本:ES=localhost:9200 escurl () { curl -H 'Content-Type: application/json' "$@"; } 复制代码
Elasticsearch 具有非常强大的动态性和灵活性,例如当向一个不存在的索引添加文档时,会自动创建该索引,例如:
$ escurl -XPUT $ES/my_article/doc/1?pretty -d '
{
"title": "标题",
"createdAt": "2020-02-02T02:02:02.020Z",
"wordCount": 100,
"extra": {
"deleted": false,
"score": 8.5
}
}'
复制代码
如果 my_article 索引不存在,则会自动创建 my_article 索引,并向其中添加数据,其数据结构为:
{
"mappings": {
"doc": {
"properties": {
"createdAt": { "type": "date" },
"wordCount": { "type": "long" },
"extra": {
"properties": {
"score": { "type": "float" },
"deleted": { "type": "boolean" }
}
},
"title": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
}
}
}
}
}
复制代码
可以发现 ES 自动做了如下字段类型映射
- title 字段映射成 text 类型
- createdAt 字段映射成 date 类型
- wordCount 字段映射成 long 类型
- extra.score 字段映射成 float 类型
- extra.deleted 字段映射成 boolean 类型
猜测非常准确,而且后续增加新字段还可以动态猜测并更新 mapping。
动态更新映射
如果继续添加一个新文档,里面多出几个字段:
$ escurl -XPUT $ES/my_article/doc/2?pretty -d '
{
"title": "新文章",
"createdAt": "2020-02-02T02:02:02.020Z",
"wordCount": 100,
"likes": 0,
"author": "作者",
"extra": {
"deleted": false,
"score": 8.5,
"remark": "备注"
}
}'
复制代码
发现映射被自动更新了:
{
"mappings": {
"doc": {
"properties": {
"createdAt": { "type": "date" },
"wordCount": { "type": "long" },
"author": {
"type": "text",
"fields": { "keyword": { "ignore_above": 256, "type": "keyword" } }
},
"extra": {
"properties": {
"score": { "type": "float" },
"deleted": { "type": "boolean" },
"remark": {
"type": "text",
"fields": { "keyword": { "ignore_above": 256, "type": "keyword" } }
}
}
},
"title": {
"type": "text",
"fields": { "keyword": { "ignore_above": 256, "type": "keyword" } }
},
"likes": { "type": "long" }
}
}
}
}
复制代码
也就是说,ES 会根据该字段的值猜测其数据类型,并动态添加到类型映射里面。
手动干预动态映射
动态映射虽然灵活,但有时候又想明确数据结构,因为不是所有字段都需要被存储,此时可以配置索引的 dynamic 选项,有三个可选值:
- true:动态添加新的字段(默认值)
- false:忽略新的字段
- strict:如果遇到新字段抛出异常
$ escurl -XPUT $ES/my_article?pretty -d '
{
"mappings": {
"doc": {
"dynamic": "false",
"properties": {
"title": { "type": "keyword"},
"wordCount": { "type": "long" },
"createdAt": { "type": "date" },
"extra": {
"type": "object",
"dynamic": true
}
}
}
}
}'
复制代码
上面的索引的意思是:如果遇到新字段,会自动忽略,而内部对象 extra 遇到新字段就会动态创建新字段。注意,如果索引已经存在就会出错,必须删除重建。这个时候,如果再添加如下数据:
$ escurl -XPUT $ES/your_article/doc/2?pretty -d '
{
"title": "新文章",
"createdAt": "2020-02-02T02:02:02.020Z",
"wordCount": 100,
"likes": 0,
"author": "作者",
"extra": {
"deleted": false,
"score": 8.5,
"remark": "备注"
}
}'
复制代码
likes 和 author 字段就会被忽略,而 extra.remark 字段则被添加进去了。
用动态模板约束动态映射
动态映射的自动推断功能很强大,但有时候并不完全符合业务需求,例如我希望所有 string 类型都映射成 keyword 而不是 text,所有 number 都映射成 double 而不是 long,这个时候就需要动态模板(dynamic_templates),可以完全控制新生成字段的映射类型。例如:
$ escurl -XPUT $ES/my_article?pretty -d '
{
"settings": {
"index": {
"number_of_shards": 1,
"number_of_replicas": 0
}
},
"mappings": {
"doc": {
"dynamic_templates": [
{
"string_fields": {
"match": "*",
"match_mapping_type": "string",
"mapping": {
"type": "keyword",
"ignore_above": 256
}
}
},
{
"number_fields": {
"match": "*",
"match_mapping_type": "long",
"mapping": {
"type": "double"
}
}
}
]
}
}
}'
复制代码
dynamic_templates 是一个数组,也就是说可以添加多个模板,ES 会按照顺序来检测,启用第一个匹配的模板。
上面的做法是给当前索引指定动态模板,其实也可以反过来,先创建动态模板,让模板指定匹配的索引。
$ escurl -XPUT $ES/_template/my-template?pretty -d '
{
"index_patterns":[ "my_*" ],
"mappings": {
"doc": {
"dynamic_templates": [
{
"string_fields": {
"match": "*",
"match_mapping_type": "string",
"mapping": {
"type": "keyword",
"ignore_above": 256
}
}
},
{
"number_fields": {
"match": "*",
"match_mapping_type": "long",
"mapping": {
"type": "double"
}
}
}
]
}
}
}'
复制代码
这个时候再创建 my-xxx
索引的时候,动态字段映射会根据 my-template 里面的规则进行映射。由于动态模板非常实用,下面记录其增删改查的语法:
查看模板
escurl -XGET $ES/_template?pretty # 查看所有模板
escurl -XGET $ES/_template/tpl_1?pretty # 查看指定模板 tpl_1
escurl -XGET $ES/_template/tpl_1,tpl_2?pretty # 批量查看模板 tpl_1 和 tpl_2
复制代码
返回结果是一个对象,key 是模板名称,value 是模板定义。
检查模板是否存在
$ escurl --head $ES/_template/tpl_1
复制代码
存在则返回:
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 488
复制代码
否则返回:
HTTP/1.1 404 Not Found
content-type: application/json; charset=UTF-8
content-length: 2
复制代码
创建模板
$ escurl -XPUT $ES/_template/my-prefix-template
{
"order": 0,
"index_patterns": [
"prefix-*"
],
"settings": {
"index": {
"number_of_shards": "5",
"number_of_replicas": "0"
}
},
"mappings": {
"doc": {
"dynamic_templates": [
{
"string_fields": {
"match": "*",
"match_mapping_type": "string",
"mapping": {
"type": "keyword",
"ignore_above": 256
}
}
}
],
"properties": {
"discount": {
"type": "double"
},
"pay": {
"type": "double"
}
}
}
}
}
复制代码
删除模板
$ escurl -XDELETE $ES/_template/template_1
复制代码