移除映射类型(mapping type)
es在8.X之前的逻辑存储模型是模拟关系型数据库的三层结构:index(库)-type(表)-document(record).这种结构在7.x时不再推荐使用,8.x的时候会把type移除变成最终的两级结构:index(库)-document(record).由于层级的调整,在7.x中影响范围包括:index creation, put mapping, get mapping, put template, get template and get field mappings APIs.
什么是映射类型(mapping type)
es中每个文档存储在一个索引(index)中,同时分配到一个映射类型(mapping type)中.映射类型通常表示文档类型和索引实体.如一个twitter索引可能有user类型和tweet类型.每个映射类型有自己的字段,所以user类型可能有full_name,user_name,email字段,tweet类型可能有content,tweeted_at,user_name字段.文档通过_type元数据字段来标识类型名称.查询时也通过类型字段来限制查询范围:
GET twitter/user,tweet/_search
{
"query": {
"match": {
"user_name": "kimchy"
}
}
}
映射类型的缺点
关系数据库中,每个表是独立的,表间的字段即使同名也互相没有干扰.但是es中的映射类型不同.
- es中同一个索引下的同名字段会被Lucene存储在一起,这也要求它们的映射类型必须一致.如果不一致则会导致数据删除失败.
- 同一个索引中存储不同的实体,往往相同字段很少,由于es的数据存储是以索引为维度的,这就造成实际存储的数据比较分散,不利于文档压缩.
这也看出来映射类型的存在很鸡肋,因此es决定7.x时移除映射类型的概念.这也是基于查询和存储的效率最佳选择.
推荐的映射类型方式
- 按文档类型划分索引
实际存储时,不再通过映射类型区分文档类型,而是通过索引来区分.如上面的twitter索引示例,我们把user存到user索引中,把tweet存到tweet索引中,查询,存储完全独立.
优点:
- 数据更密集,有利于lucene压缩
- 同一索引中文档数据表示独立的实体,使得全文检索条目排名分析更精确.
- 自定义类型字段(custome type field)
由于集群中主分片数量是有限的,你可能不想浪费整个分片存储一些很少数量的文档数据.这种情况下,你可以通过自定义类型字段实现类似之前映射类型的功能.
用上面的user/tweet举例:
7.x之前的版本存储流程如下:
PUT twitter
{
"mappings": {
"user": {
"properties": {
"name": { "type": "text" },
"user_name": { "type": "keyword" },
"email": { "type": "keyword" }
}
},
"tweet": {
"properties": {
"content": { "type": "text" },
"user_name": { "type": "keyword" },
"tweeted_at": { "type": "date" }
}
}
}
}
PUT twitter/user/kimchy
{
"name": "Shay Banon",
"user_name": "kimchy",
"email": "shay@kimchy.com"
}
PUT twitter/tweet/1
{
"user_name": "kimchy",
"tweeted_at": "2017-10-24T09:00:00Z",
"content": "Types are going away"
}
GET twitter/tweet/_search
{
"query": {
"match": {
"user_name": "kimchy"
}
}
}
7.x之后的版本通过添加自定义类型(type)字段存储方式:
PUT twitter?include_type_name=true
{
"mappings": {
"_doc": {
"properties": {
"type": { "type": "keyword" },
"name": { "type": "text" },
"user_name": { "type": "keyword" },
"email": { "type": "keyword" },
"content": { "type": "text" },
"tweeted_at": { "type": "date" }
}
}
}
}
7.x之后明确指定类型(type)字段,而7.x之前是隐式指定的_type字段
PUT twitter/_doc/user-kimchy
{
"type": "user",
"name": "Shay Banon",
"user_name": "kimchy",
"email": "shay@kimchy.com"
}
PUT twitter/_doc/tweet-1
{
"type": "tweet",
"user_name": "kimchy",
"tweeted_at": "2017-10-24T09:00:00Z",
"content": "Types are going away"
}
GET twitter/_search
{
"query": {
"bool": {
"must": {
"match": {
"user_name": "kimchy"
}
},
"filter": {
"match": {
"type": "tweet"
}
}
}
}
}
- 父子结构(Parent/Child)不再提供映射类型
7.x前,parent-child关系,定义映射关系时可以指定parent和children类型.7.x后不使用这个语法.parent-child特性运行不做改变,但是实际关系表示改为通过join字段实现.
7.0无类型接口(typeless apis)
7.x作为过渡版本,它在弱化类型的实际影响.正常api使用中如果你明确指定了类型(type),会得到一个不推荐警告:
#! Deprecation: [types removal] Specifying types in search requests is deprecated.
- 索引APIs
索引创建,索引模板和映射apis支持新的url参数include_type_name.它指明在请求和响应映射定义中是否包含类型名称.这个参数默认值在6.8中是true,以匹配7.0前的映射类型匹配问题.7.0后的默认值为false,8.0中将移除.
PUT /my-index-000001?include_type_name=false
{
"mappings": {
"properties": {
"foo": {
"type": "keyword"
}
}
}
}
新增索引映射时不进行类型映射.
- 文档APIs
7.x中,索引APIs必须通过{index}/_doc路径调用,自动生成_id或指明{index}/_doc/{id}中的id.
PUT /my-index-000001/_doc/1
{
"foo": "baz"
}
实际存储数据
{
"_index": "my-index-000001",
"_id": "1",
"_type": "_doc",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
获取和删除APIs也类似:{index}/_doc/{id}
GET /my-index-000001/_doc/1
- 查询APIs
当调用像_search, _msearch, 或_explain查询API时,url中不应该包含类型.此外,_type字段不用于查询,聚合和脚本中.
- 响应中的类型
文档和查询APIs的响应将继续返回_type关键字,避免破坏现在的响应解析.然而,这个关键字不再推荐也不再被引用.在8.0中将被完全删除.
- 索引模板
推荐在重新添加索引无类型模板时将include_type_name设置为false.后台实现中,无类型模板在创建指令中模仿类型_doc进行创建.
实际官方文档中还展示旧版到新版的数据迁移方式,以及进行映射类型版本支持的计划.点击参考资料即可获取.