Elasticsearch 中数据类型 text 与 keyword 的区别
随着ElasticSearch 5.X 系列的到来, 同时也迎来了该版本的重大特性之一: 移除了string
类型. 这个变动的根本原因是string
类型会给我们带来很多困惑: 因为ElasticSearch对字符串拥有两种完全不同的搜索方式. 你可以按照整个文本进行匹配, 即关键词搜索(keyword search), 也可以按单个字符匹配, 即全文搜索(full-text search). 对ElasticSearch稍有了解的人都知道, 前者的字符串被称为not-analyzed
字符, 而后者被称作analyzed
字符串.
事实上, 同一种类型用于应对两种不同的使用场景是会让人崩溃的, 因为有些选项只对其一的场景设置有效.例如position_increment_gap
对not-analyzed
字符就不会起作用, 而像ignore_above
对于analyzed
字符串就很难区分它到底是对整个字符串的值有效还是对单独的每个分词有效(在这种场景, ignore_above确实只对整个字符串值有效, 而对单个分词的限制可以使用limit
设置).
为了避免上述尴尬, string
字段被拆分成两种新的数据类型: text
用于全文搜索的, 而keyword用于关键词搜索.
新的默认类型
做了这个类型分解之后, 我们对string字段的默认dynamic mappings 也做了改变. 在以前刚接接触ElasticSearch时, 如果需要对某个字段的所有取值做聚合, 你不得不对这些数据重做索引. 假如你正在处理的文档中包含一个city
字段. 对这个字段做聚合的话会分别给出new
和york
的总数, 而非我们通常期望的New York
的总数.让人沮丧的是为了达到我们希望的结果, 我们必须对这个字段重新进行索引.
为了不让事情变得这么糟糕, ElasticSearch决定从Logstash中借取思路: 字符串将默认被同时映射成text
和keyword
类型. 例如对下面的文档进行索引后:
{ "foo": "bar" }
ElasticSearch将会为你创建下面的动态映射(dynamic mappings):
{ "foo": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } } }
当然, 基于这个映射你即可以在foo
字段上进行全文搜索, 也可以通过foo.keyword
字段实现关键词搜索及数据聚合.
禁用这个特性也很方便: 你只需要在定义mapping时显式声明字符串字段的类型或者使用一个动态模板(dynamic template)
来匹配你所有的字符串字段即可. 例如通过下面的dynamic template
就可以恢复到在ElasticSearch 2.x中使用的dynamic template的效果:
{ "match_mapping_type": "string", "mapping": { "type": "text" } }
如何迁移到新版本
通常, 迁移工作是非常容易的. 以前映射到analyzed
的字符串的字段:
{ "foo": { "type": "string", "index": "analyzed" } }
如今只要映射为text
即可:
{ "foo": { "type": "text", "index": true } }
以前被定义为not_analyzed
的字符串字段:
{ "foo": { "type": "string", "index": "not_analyzed" } }
也只需要被定义为keyword
即可:
{ "foo": { "type": "keyword", "index": true } }
如上所述, string
字段被重新定义为text
和keyword
字段. 对于上面的index
属性, 因为在新的定义中我们不需要三种状态(在以前的string
定义中可以是analyzed
, not_analyzed
和no
), 所以只简单的定义成了boolean值, 以告知ElasticSearch是否可在该字段上进行搜索.
向后兼容
因为大的版本升级本身就充满挑战, 因此官方尽力不让在在升级ElasticSearch过程中更新你的mapping字义. 首先, string
字段可以继续在已定义的2.x版本的索引中继续使用, 而当创建新的索引时, ElasticSearch会做些处理以自动把string
映射成等价的text
或keyword
. 如果在你已有的索引模板(index template)中定义有string
字段, 这一点将非常有用, 因为这些模板无须改动即可使用到ElasticSearch 5.x中. 话说回来, 你还是需要着手做这些模板做些升级, 因为在ElasticSearch 6.0中我们可能会移除这个向后兼容的逻辑.