2

删除映射类型

在Elasticsearch 7.0.0或更高版本中创建的索引不再接受_default_映射,索引在6.x中创建将继续在Elasticsearch 6.x中运行,类型在api 7.0中是不受支持的,它会中断对索引创建、put映射、get映射、put模板、get模板和get字段映射API的更改。

什么是映射类型?

自从第一次发布Elasticsearch以来,每个文档都存储在一个索引中,并分配了一个映射类型,映射类型用于表示被索引的文档或实体的类型,例如twitter索引可能具有user类型和tweet类型。

每个映射类型都可以有自己的字段,因此user类型可以有full_name字段、user_name字段和email字段,而tweet类型可以有content字段、tweeted_at字段,和user类型一样,还有user_name字段。

每个文档都有一个包含类型名称的_type元字段,通过在URL中指定类型名称,可以将搜索限制为一个或多个类型:

GET twitter/user,tweet/_search
{
  "query": {
    "match": {
      "user_name": "kimchy"
    }
  }
}

_type字段与文档的_id相结合生成_uid字段,因此具有相同_id的不同类型的文档可以存在于一个索引中。

还使用映射类型在文档之间建立父子关系,因此类型为question的文档可以是类型为answer的文档的父文档。

为什么要删除映射类型?

最初,讨论了“索引”类似于SQL数据库中的“数据库”,以及“类型”等价于“表”。

这是一个错误的类比,导致了错误的假设,在SQL数据库中,表是相互独立的,一个表中的列与另一个表中具有相同名称的列没有关系,这与映射类型中的字段不同。

在Elasticsearch索引中,不同映射类型中具有相同名称的字段在内部由相同的Lucene字段支持,换句话说,使用上面的示例,user类型中的user_name字段存储在与tweet类型中的user_name字段完全相同的字段中,而且两个user_name字段在这两种类型中必须具有相同的映射(定义)。

例如,当你想要删除一个类型中的date字段和同一个索引中的另一个类型中的boolean字段时,这可能会导致失败。

最重要的是,存储在同一索引中具有很少或没有共同字段的不同实体会导致数据稀疏,并影响Lucene有效压缩文档的能力。

基于这些原因,决定将映射类型的概念从Elasticsearch中移除。

映射类型的替代方案

每种文档类型的索引

第一种选择是为每个文档类型都有一个索引,你可以将tweets存储为tweetsuser索引,而不是将tweetsusers存储在单个twitter索引中,索引之间是完全独立的,因此索引之间不存在字段类型的冲突。

这种方法有两个好处:

  • 数据更可能是密集的,因此受益于Lucene中使用的压缩技术。
  • 用于在全文搜索中得分的统计术语更可能准确,因为同一索引中的所有文档都表示一个实体。

每个索引都可以根据它将包含的文档数量适当调整大小:你可以为users使用较少的主碎片,而为tweet使用较多的主碎片。

自定义类型字段

当然,集群中可以存在多少主碎片是有限制的,因此你可能不希望为了一个只有几千个文档的集合而浪费整个碎片,在本例中,你可以实现自己的自定义类型字段,其工作方式与旧的_type类似。

让我们以上面的user/tweet为例,最初,工作流应该是这样的:

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"
    }
  }
}

你可以通过添加自定义类型字段来实现相同的功能,如下所示:

PUT twitter
{
  "mappings": {
    "_doc": {
      "properties": {
        "type": { "type": "keyword" }, 
        "name": { "type": "text" },
        "user_name": { "type": "keyword" },
        "email": { "type": "keyword" },
        "content": { "type": "text" },
        "tweeted_at": { "type": "date" }
      }
    }
  }
}

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" 
        }
      }
    }
  }
}
  • 显式type字段替代了隐式_type字段。

没有映射类型的父/子关系

以前,父—子关系表示为将一个映射类型表示为父,将一个或多个其他映射类型表示为子,没有类型,我们就不能再使用这种语法,除了表示文档之间关系的方式已更改为使用新的join字段外,父—子特性将继续像以前一样工作。

删除映射类型的时间表

对于用户来说,这是一个巨大的变化,所以已经尝试让它尽可能地不那么痛苦,更改将按如下方式进行:

Elasticsearch 5.6.0

  • 在索引上设置index.mapping.single_type: true将启用在6.0中强制执行的单类型/索引行为。
  • 在5.6中创建的索引中可以使用父—子join字段替换。

Elasticsearch 6.x

  • 在5.x中创建索引将继续在6.x中运行就像5.x。
  • 索引在6.x中创建只允许每个索引使用单一类型,类型可以使用任何名称,但只能有一个,首选的类型名称是_doc,因此索引API具有与7.0中相同的路径:PUT {index}/_doc/{id} and POST {index}/_doc
  • _type名称不能再与_id组合以形成_uid字段,_uid字段已成为_id字段的别名。
  • 新的索引不再支持旧式的父/子索引,而是应该使用join字段。
  • _default_映射类型已弃用。
  • 在6.8中,索引创建、索引模板和映射API支持查询字符串参数(include_type_name),该参数指示请求和响应是否应该包含类型名称。它默认为true,应该设置为一个显式值,以便准备升级到7.0,未设置include_type_name将导致一个弃用警告,没有显式类型的索引将使用虚拟类型名称_doc

Elasticsearch 7.x

  • 在请求中指定类型已弃用,例如,索引文档不再需要文档type,对于显式id,新的索引API是PUT {index}/_doc/{id},对于自动生成的id则是POST {index}/_doc,注意,在7.0中,_doc是路径的一个永久部分,它表示端点名称,而不是文档类型。
  • 索引创建、索引模板和映射API中的include_type_name参数默认为false,完全设置该参数将导致一个弃用警告。
  • 删除_default_映射类型。

Elasticsearch 8.x

  • 不再支持在请求中指定类型。
  • 删除include_type_name参数。

将多类型索引迁移到单类型

Reindex API可用于将多类型索引转换为单类型索引,下面的例子可以在Elasticsearch 5.6或Elasticsearch 6.x中使用,在6.x,不需要指定index.mapping.single_type作为默认值。

每种文档类型的索引

第一个示例将twitter索引拆分为tweets索引和users索引:

PUT users
{
  "settings": {
    "index.mapping.single_type": true
  },
  "mappings": {
    "_doc": {
      "properties": {
        "name": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "email": {
          "type": "keyword"
        }
      }
    }
  }
}

PUT tweets
{
  "settings": {
    "index.mapping.single_type": true
  },
  "mappings": {
    "_doc": {
      "properties": {
        "content": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "tweeted_at": {
          "type": "date"
        }
      }
    }
  }
}

POST _reindex
{
  "source": {
    "index": "twitter",
    "type": "user"
  },
  "dest": {
    "index": "users"
  }
}

POST _reindex
{
  "source": {
    "index": "twitter",
    "type": "tweet"
  },
  "dest": {
    "index": "tweets"
  }
}

自定义类型字段

下一个示例添加一个自定义类型字段,并将其设置为原始_type的值,它还将类型添加到_id中,以防有任何不同类型的文档具有冲突的id

PUT new_twitter
{
  "mappings": {
    "_doc": {
      "properties": {
        "type": {
          "type": "keyword"
        },
        "name": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "email": {
          "type": "keyword"
        },
        "content": {
          "type": "text"
        },
        "tweeted_at": {
          "type": "date"
        }
      }
    }
  }
}


POST _reindex
{
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter"
  },
  "script": {
    "source": """
      ctx._source.type = ctx._type;
      ctx._id = ctx._type + '-' + ctx._id;
      ctx._type = '_doc';
    """
  }
}

7.0中的无类型API

在Elasticsearch 7.0中,每个API都支持无类型请求,指定类型将产生一个弃用警告。

即使目标索引包含自定义类型,无类型API也可以工作,例如,如果索引具有自定义类型名称my_type,则可以使用无类型index调用向其添加文档,并使用无类型get调用加载文档。

索引API

索引创建、索引模板和映射API支持一个新的include_type_name URL参数,该参数指定请求和响应中的映射定义是否应该包含类型名称,版本6.8中的参数默认为true,以匹配在映射中使用类型名称的7.0之前的行为,它在7.0版本中默认为false,将在8.0版本中删除。

它应该在6.8中明确设置,以便准备升级到7.0,为了避免6.8中的弃用警告,可以将参数设置为truefalse,在7.0中,设置include_type_name将导致一个弃用警告。

查看一些与Elasticsearch交互的例子,这个选项设置为false

PUT index?include_type_name=false
{
  "mappings": {
    "properties": { 
      "foo": {
        "type": "keyword"
      }
    }
  }
}
  • 映射直接包含在mappings键下,没有类型名称。
PUT index/_mappings?include_type_name=false
{
  "properties": { 
    "bar": {
      "type": "text"
    }
  }
}
  • 映射直接包含在mappings键下,没有类型名称。
GET index/_mappings?include_type_name=false

上面的调用返回:

{
  "index": {
    "mappings": {
      "properties": { 
        "foo": {
          "type": "keyword"
        },
        "bar": {
          "type": "text"
        }
      }
    }
  }
}
  • 映射直接包含在mappings键下,没有类型名称。

文档API

在7.0中,必须使用{index}/_doc路径调用索引API,以便自动生成_id,使用显式id调用{index}/_doc/{id}

PUT index/_doc/1
{
  "foo": "baz"
}
{
  "_index": "index",
  "_id": "1",
  "_type": "_doc",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

类似地,getdelete API使用路径{index}/_doc/{id}

GET index/_doc/1
在7.0中,_doc表示端点名称,而不是文档类型,_doc组件是文档indexgetdelete API路径的永久部分,在8.0中不会被删除。

对于同时包含类型和端点名(如_update)的API路径,在7.0中端点将立即跟随索引名:

POST index/_update/1
{
    "doc" : {
        "foo" : "qux"
    }
}

GET /index/_source/1

类型也不应该再出现在请求体中,下面的bulk索引示例省略了URL和单个批量命令中的类型:

POST _bulk
{ "index" : { "_index" : "index", "_id" : "3" } }
{ "foo" : "baz" }
{ "index" : { "_index" : "index", "_id" : "4" } }
{ "foo" : "qux" }

搜索API

在调用诸如_search_msearch_explain之类的搜索API时,URL中不应该包含类型,此外,_type字段不应该用于查询、聚合或脚本。

响应中的类型

文档和搜索API将继续在响应中返回_type键,以避免中断响应解析,然而,键被认为是不赞成的,不应该再被引用,类型将在8.0中从响应中完全删除。

注意,当使用废弃的类型化API时,索引的映射类型将作为正常返回,但是无类型API将在响应中返回虚拟类型_doc,例如,下面的无类型get调用总是返回_doc作为类型,即使映射有一个像my_type这样的自定义类型名:

PUT index/my_type/1
{
  "foo": "baz"
}

GET index/_doc/1
{
    "_index" : "index",
    "_type" : "_doc",
    "_id" : "1",
    "_version" : 1,
    "_seq_no" : 0,
    "_primary_term" : 1,
    "found": true,
    "_source" : {
        "foo" : "baz"
    }
}

索引模版

建议通过将include_type_name设置为false来重新添加索引模板,使其无类型,在底层,无类型模板在创建索引时将使用虚拟类型_doc

如果将无类型模板用于类型化索引创建调用,或者将类型化模板用于无类型索引创建调用,则仍将应用模板,但索引创建调用将决定是否应该有类型。例如在下面的示例中,index-1-01将具有一个类型,尽管它匹配一个没有类型的模板,而index-2-01将具有无类型,尽管它匹配一个定义了类型的模板,index-1-01index-2-01都将从匹配的模板中继承foo字段。

PUT _template/template1
{
  "index_patterns":[ "index-1-*" ],
  "mappings": {
    "properties": {
      "foo": {
        "type": "keyword"
      }
    }
  }
}

PUT _template/template2?include_type_name=true
{
  "index_patterns":[ "index-2-*" ],
  "mappings": {
    "type": {
      "properties": {
        "foo": {
          "type": "keyword"
        }
      }
    }
  }
}

PUT index-1-01?include_type_name=true
{
  "mappings": {
    "type": {
      "properties": {
        "bar": {
          "type": "long"
        }
      }
    }
  }
}

PUT index-2-01
{
  "mappings": {
    "properties": {
      "bar": {
        "type": "long"
      }
    }
  }
}

在隐式索引创建的情况下,因为文档在索引中被索引,而索引还不存在,所以总是使用模板,这通常不是一个问题,因为无类型索引调用要处理有类型的索引。

混合版本的集群

在由6.8和7.0节点组成的集群中,应该在索引创建之类的索引API中指定参数include_type_name,这是因为参数在6.8和7.0之间有不同的默认值,所以相同的映射定义对两个节点版本都无效。

诸如bulkupdate之类的无类型文档API仅在7.0版本时可用,不能用于6.8节点,对于执行文档查找的查询的无类型版本,如terms,也是如此。



博弈
2.5k 声望1.5k 粉丝

态度决定一切