ElasticSearch 入门教程 - 基础篇之三
大纲
版本说明
软件 | 版本 | 描述 |
---|---|---|
ElasticSearch | 7.4.2 | ElasticSearch 的版本 |
Curl | 7.29.0 | Curl 的版本 |
Mapping
Mapping 简介
Mapping 是用来定义文档(Document),以及它所包含的字段(Field)是如何存储和检索的,这类似创建 MySQL 数据库表时指定表的字段类型,主要作用如下:
- 定义 Index 下的字段名
- 定义字段类型,比如数值型、浮点型、布尔型等
- 定义倒排索引相关的设置,比如是否索引、记录 Position 等
一句话简单概括就是,Mapping 决定了 ES 在建立倒排索引和进行检索时对文档采取的相关策略,如数字类型、日期类型、文本类型等等。
值得一提的是,检索时用到的分析策略,要和建立索引时的分析策略相同,否则将导致数据分析不准确。ES 对不同的类型有不同的存储和检索策略,例如:
Exact Value - 精确匹配
:对 Exact Value(如date
),在索引的分词阶段,会将整个 Value 作为一个关键词建立到倒排索引中。Full Text - 全文检索
:对于 Full Text 型的数据类型(如text
),在索引时会经过各类处理,包括分词、Normalization(时态转换、单复数的转换、同义词转换、大小写转换、缩写)等,才会建立到索引数据中,更深入的话就涉及到 NPL 自然语义处理。
Mapping Type
每个索引都拥有唯一的 Mapping Type,用来决定文档将如何被索引。Mapping Type 由下面两部分组成:
Meta fields
:元字段,用于自定义如何处理文档的相关元数据。元字段包括文档的_index
、_type
、_id
和_source
等字段。Fields or properties
:映射类型,包含与文档相关的字段或属性的列表。
特别注意
从 ES 6.0.0 及以上的版本开始,Mapping Type 已经被官方移除。
常用字段类型
提示
ES 完整的字段类型说明请查看 官方文档。
核心字段类型
- 布尔类型:boolean
- 二进制类型:binary
- 日期类型:date、date_nanos
- 字符串型:text、keyword(精确匹配,不会进行分词)
- 数字类型:long、integer、short、byte、double、float、half_float、scaled_float
- 范围类型:integer_range、float_range、long_range、double_range、date_range
复合字段类型
- 数组类型:array(支持不针对特定的类型)
- 对象类型:object(用于单个 JSON 对象)
- 嵌套类型:nested(用于 JSON 对象数组)
- 地理位置类型:geo_point(用于描述地理坐标,如经纬度)、geo_shape(用于描述复杂形状,如地理图形)
专用字段类型
- IP 类型:ip(记录 IPV4 和 IPV6 地址)
- 哈希类型:murmur3(记录字符串的 Hash 值)
- 补全类型:completion(提供自动补全的提示)
- 令牌计数类型:token_count(用于统计字符串中的词条数量)
- 抽取类型:percolator(接受特定领域查询语言 - Query DSL 的查询)
- 附件类型:attachment(支持存储附件,如 Microsoft Office、Open Document、ePub、HTML 等格式的内容)
多字段的特性
通常用于为不同的目的用不同的方法索引同一个字段,这样可以更好地满足各种搜索需求。例如一个字符串类型的字段可以设置为 text
来支持全文检索,与此同时也可以让这个字段拥有 keyword
类型来做排序和聚合。大多数的字段类型,都是通过 fields
参数来支持多字段的特性。
数据类型自动猜测
以下的 JSON 数据类型,ES 会自动猜测其字段类型。对于其他 ES 不会自动猜测的数据类型,则需要手动通过 Mapping 来指定其字段类型。
JSON Type | JSON Value | ES Type |
---|---|---|
布尔型 | true、false | boolean |
整数 | 123 | long |
浮点数 | 123.45 | double |
日期 | 2020-09-15 | date |
字符串 | foo | string |
Mapping 操作
版本兼容说明
ES 6 及以下版本拥有
type
的概念ES 7 及以上版本移除了
type
的概念- 关系型数据库中两个数据库表是互相独立的,即使它们里面有相同名称的列也不影响使用,但在 ES 中不是这样的。ES 是基于 Lucene 开发的搜索引擎,而 ES 中不同
type
下名称相同的filed
最终在 Lucene 中的处理方式是一样的。 - 比如两个不同
type
下的两个user_name
,在 ES 同一个索引下其实被认为是同一个filed
,必须在两个不同的type
中定义相同的filed
映射。否则,不同type
中的相同字段名称就会在处理中出现冲突的情况,导致 Lucene 处理效率下降。ES 去掉type
的支持,就是为了提高处理数据的效率。
- 关系型数据库中两个数据库表是互相独立的,即使它们里面有相同名称的列也不影响使用,但在 ES 中不是这样的。ES 是基于 Lucene 开发的搜索引擎,而 ES 中不同
在 ES 7.x 版本里,URL 中的
type
参数为可选项,也就是索引一个文档时不再要求提供文档类型。而在 ES 8.x 版本里,不再支持 URL 中的type
参数。ES 不同版本的兼容方案如下:- 1)将索引从多类型迁移到单类型,即每种类型文档都建一个独立的索引。
- 2)将已存在的索引下的类型数据,全部迁移到指定位置。
查询 Mapping
- 查询
bank
索引下的映射
1 | curl -X GET http://127.0.0.1:9200/bank/_mapping |
- 返回的 JSON 结果
1 | { |
创建 Mapping
- 创建索引并指定映射,这里的 URL 不需要带
_mapping
1 | PUT /user |
- 若希望新增数据,可以使用以下方式,其中的
_doc
是类型(固定不变的)
1 | POST /user/_doc/1 |
新增 Mapping
- 添加新的字段映射
1 | PUT /user/_mapping |
属性 | 可选 | 说明 |
---|---|---|
index | 是 | 默认 true,如果为 false,表示该字段不会被索引,也就是在检索结果里面会出现,但字段本身并不能当做检索条件。 |
doc_values | 是 | 默认 true,如果为 false,表示该字段不可以做排序、聚合以及脚本操作,这样更节省磁盘空间。还可以通过设定 doc_values 为 true,index 为 false 来让字段不能被搜索,但可以用于排序、聚合以及脚本操作。 |
更新 Mapping
对于已经存在的映射字段,ES 不支持更新,这是因为 Lucence 实现的倒排索引生成后不允许修改。映射字段的更新,必须先创建新的索引,然后进行数据迁移。
特别注意
- 由于从 ES 7 及以上版本开始,
Type
的概念已经被官方移除,因此 ES 6 及以下版本在迁移数据时,需要指定Type
,而其他版本则不需要指定Type
。
- 先创建新的索引,并指定新的映射
1 | PUT /new_user |
- 将旧索引下的数据进行迁移,以下是 ES 7 及以上版本的写法,其中的
source
用于指定旧索引,dest
用于指定新索引
1 | POST /_reindex |
- 将旧索引下的数据进行迁移,以下是 ES 6 及以下版本的写法,必须指定
type
,其中的source
用于指定旧索引,dest
用于指定新索引
1 | POST /_reindex |
分词
一个 Tokenizer(分词器)接收一个字符流,将之分割为独立的 Tokens(词元,通常是独立的单词),然后输出 Tokens 流。例如,Whitespace Tokenizer 遇到空白字符时分割文本。它会将文本 Quick brown fox!
分割为 [Quick, brown, fox!]。 该 Tokenizer(分词器)还负责记录各个 Term(词条)的顺序或 Position 位置(用于 Phrase 短语和 Word Proximity 词近邻查询),以及 Term(词条)所代表的原始 Word(单词)的 Start(起始)和 End(结束)的 Character Offsets(字符偏移量,用于高亮显示搜索的内容)。Elasticsearch 提供了很多内置的分词器,可以用来构建 Custom Analyzers(自定义分词器)。
IK 分词器简介
IKAnalyzer 是一个开源的,基于 Java 语言开发的轻量级的中文分词工具包。从 2006 年 12 月推出 1.0 版开始,IKAnalyzer 已经推出了 3 个大版本。最初,它是以开源项目 Lucene 为应用主体的,结合词典分词和文法分析算法的中文分词组件。新版本的 IKAnalyzer 3.0 则发展为 面向 Java 的公用分词组件,独立于 Lucene 项目,同时提供了对 Lucene 的默认优化实现。
安装 IK 分词器
可以从 GitHub 仓库 下载 IK 分词器的安装包,然后按照以下步骤安装即可。值得一提的是,IK 分词器的版本号必须与 ES 的版本号一致。
- 安装 IK 分词器
1 | # 进入 ES 的 plugins 目录 |
- 查看 IK 分词器是否安装成功
1 | # 进入 ES 的 bin 目录 |
测试 IK 分词器
观察下述的结果,能够看出来使用不同的分词器,分词结果有明显的区别,所以以后定义一个索引不能再使用默认的 Mapping 了,要手动建立 Mapping,因为要选择合适的分词器。
提示
- IK 分词器,提供了两种颗粒度拆分(ik_smart,ik_max_word)
- ik_smart(最粗粒度拆分),分割的粒度较小
- ik_max_word(最细粒度划分),分割的力度较大
- 使用默认的分词器,无法对中文进行正确分词
1 | POST /_analyze |
1 | { |
- 使用
ik_smart
分词器,可以对中文进行正确分词
1 | POST /_analyze |
1 | { |
- 使用
ik_max_word
分词器,可以对中文进行正确分词
1 | POST /_analyze |
1 | { |
自定义词库
IK 分词器自身不能识别最新的网络流行语,但可以通过自定义词库来解决。
- 第一步:更改 IK 分词器的配置文件
IKAnalyzer.cfg.xml
,配置文件在 ES 的plugins
目录下,例如/usr/local/elasticsearch/plugins/ik/config/IKAnalyzer.cfg.xml
1 |
|
提示
remote_ext_dict
参数用于指定远程扩展字典所在的网络位置(URL)。值得一提的是,这里可以将远程扩展字典通过文件的方式(一行一个分词)存放在 Nginx 服务器,也可以自行开发 HTTP 接口并返回扩展字典的内容。- 远程扩展字典默认支持热更新,要求 Http 请求需要返回两个头部(header),一个是 Last-Modified,一个是 ETag,这两者都是字符串类型,只要有一个发生变化,IK 插件就会去自动抓取新的字典进而更新词库。
- 更改完 IK 分词器的配置文件后,必须重启 ES 服务器,否则远程扩展字典无法生效。
第二步:重启 ES 服务器
第三步:上述配置完成之后,ES 只会对新增的数据用新词分词,历史数据是不会重新分词的。如果希望历史数据重新分词,需要执行以下的 HTTP 请求:
1 | curl -X POST my_index/_update_by_query?conflicts=proceed |