Elasticsearch 7.0 废弃mapping type原因及替代方案
1、删除映射类型-Type
在Elasticsearch 7.0.0 或更高版本中创建的索引不再接受默认映射。
在 6.x 中创建的索引将继续像以前在 Elasticsearch 6.x 中一样运行。类型在 7.0 的 API 中被弃用,对索引创建、放置映射、获取映射、放置模板、获取模板和获取字段映射 API 进行了重大更改。
注意:ES7废弃,但还在用,ES8才真正的去掉了type。
2、什么是映射类型?
从 Elasticsearch 的第一个版本开始,每个文档都存储在一个索引中并分配一个映射类型。映射类型用于表示被索引的文档或实体的类型,例如,twitter 索引可能具有用户类型和推文类型。
每个映射类型都可以有自己的字段,因此用户类型可能有一个 full_name 字段、一个 user_name 字段和一个 email 字段,而 tweet 类型可以有一个 content 字段、一个 tweeted_at 字段,并且与用户类型一样,还有一个 user_name场地。
每个文档都有一个包含类型名称的 _type 元字段,通过在 URL 中指定类型名称,可以将搜索限制为一种或多种类型:
GET twitter/user,tweet/_search
{
"query": {
"match": {
"user_name": "kimchy"
}
}
}
_type 字段与文档的 _id 组合生成 _uid 字段,因此具有相同 _id 的不同类型的文档可以存在于单个索引中。
映射类型还用于建立文档之间的父子关系,因此问题类型的文档可以是答案类型的文档的父级。
3、为什么要删除映射类型?
“type类型”不是“table表”
我们在学习Elasticsearch时,会把ES数据架构与关系数据库Mysql对比,如下:
| MySQL | ElasticSearch |
|---|---|
| Database | Index |
| Row | Document |
| Column | Field |
| Schema | Mapping |
- 关系型数据库中的数据库(DataBase),等价于ES中的索引(Index)
一个数据库下面有N张表(Table),等价于1个索引 Index下面有N多类型(Type)- 一个数据库表(Table)下的数据由多行(ROW)多列(column,属性)组成,等价于1个Type由多个文档(Document)和多Field组成。
- 在一个关系型数据库里面,schema定义了表、每个表的字段,还有表和字段之间的关系。 与之对应的,在ES中:Mapping定义索引下的Type的字段处理规则,即索引如何建立、索引类型、是否保存原始索引JSON文档、是否压缩原始JSON文档、是否需要分词处理、如何进行分词处理等。
- 在数据库中的增insert、删delete、改update、查search操作等价于ES中的增PUT、删Delete、改POST、查GET.
最初,我们谈到“索引”类似于 SQL 数据库中的“数据库”,“类型”相当于“表”。
这是一个糟糕的类比,导致了错误的假设。在 SQL 数据库中,表是相互独立的。一个表中的列与另一个表中的同名列无关。对于映射类型中的字段,情况并非如此。
在 Elasticsearch 索引中,不同映射类型中具有相同名称的字段在内部由相同的 Lucene 字段支持。 换句话说,使用上面的示例,用户类型中的 user_name 字段与 tweet 类型中的 user_name 字段存储在完全相同的字段中,并且两个 user_name 字段在两种类型中必须具有相同的映射(定义)。
例如,当您希望 delete 成为同一索引中一种类型的日期字段和另一种类型的布尔字段时,这可能会导致挫败感.
最重要的是,在同一索引中存储具有很少或没有共同字段的不同实体会导致数据稀疏并干扰 Lucene 有效压缩文档的能力。
由于这些原因,我们决定从 Elasticsearch 中删除映射类型的概念。
实战场景举例
下面两种场景,大家都用过:
同一个index下,不同的type,命名名称一样的字段名。
同一个数据库下,不同的表,命名名称一样的字段名。
-
在关系型数据库中,不同的表中,包含相同的字段名是很常见的,而且它们可以做到互不干扰。
-
在ElasticSearch中,不同的type,如果包含相同的字段名,它们是一样的,es会认为是一个字段,模糊掉不同type的概念。
所以在es里边,type这个概念没必要存在,所以es7就废弃了。
同志们,可以试一下,在同一个index中,不同的type,创建一个同名的字段,但是类型不要弄成一样的,看能否成功创建。
答案是不可以,它会提示你,不可以将这个字段的类型更改为这个类型。
illegla_argument_exception
mapper [create_time_] cannot be changed from type [date] to [keyword]
所以,结论就是,es确实把不同type中的同名字段,当成了一个字段。
在设计索引库的时候,同名问题一定要注意:
- 最简单的方法就是一个index,一个type,想要其他类型,另外创建index,
- 当然你可以用别的字段名。
4、映射类型的替代方案
(1)每个文档类型的索引
**第一种选择是为每个文档类型设置一个索引。**您可以将推文和用户存储在推文索引中,将用户存储在用户索引中,而不是将推文和用户存储在单个推特索引中。索引彼此完全独立,因此索引之间不会存在字段类型冲突。
这种方法有两个好处:
- 数据更可能是密集的,因此受益于 Lucene 中使用的压缩技术。
- 在全文搜索中用于评分的术语统计信息更可能准确,因为同一索引中的所有文档都表示单个实体。
每个索引都可以根据它包含的文档数量适当调整大小:您可以为用户使用较少数量的主分片,为推文使用更多数量的主分片。
(2)自定义类型字段
当然,集群中可以存在多少主分片是有限制的,因此您可能不希望将整个分片浪费在只有几千个文档的集合上。在这种情况下,您可以实现自己的自定义类型字段,其工作方式与旧 _type 类似。
让我们以上面的用户/推文为例。最初,一个index多个type 的 工作流程看起来像这样:
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"
}
}
}
自定义类型字段的解决方案,您可以通过添加自定义类型字段来实现相同的目的,如下所示:
自定义类型字段 : "type": { "type": "keyword" }
显式类型字段代替了隐式 _type 字段。
PUT twitter
{
"mappings": {
"_doc": {
"properties": {
"type": { "type": "keyword" }, # 1
"name": { "type": "text" },
"user_name": { "type": "keyword" },
"email": { "type": "keyword" },
"content": { "type": "text" },
"tweeted_at": { "type": "date" }
}
}
}
}
PUT twitter/_doc/user-kimchy
{
"type": "user", # 2
"name": "Shay Banon",
"user_name": "kimchy",
"email": "shay@kimchy.com"
}
PUT twitter/_doc/tweet-1
{
"type": "tweet", # 3
"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" # 4
}
}
}
}
}
(3)没有映射类型的父/子
以前,父子关系通过将一种映射类型作为父映射类型,将一种或多种其他映射类型作为子映射类型来表示。没有类型,我们就不能再使用这种语法了。父子功能将继续像以前一样发挥作用,只是表示文档之间关系的方式已更改为使用新的连接字段。
参考文档: