Elasticsearch从入门到放弃:瞎说Mapping
前面我们聊了 Elasticsearch 的索引、搜索和分词器,今天再来聊另一个基础内容—— Mapping。
Mapping 在 Elasticsearch 中的地位相当于关系型数据库中的 schema,它可以用来定义索引中字段的名字、定义字段的数据类型,还可以用来做一些字段的配置。从 Elasticsearch 7.0开始,Mapping 中不在乎需要定义 type 信息了,具体原因可以看官方的解释。
字段的数据类型
我们刚刚提到 Mapping 中可以定义字段的数据类型,这可能是 Mapping 最常用的功能了,所以我们先来看看 Elasticsearch 都支持哪些数据类型。
-
简单类型:text、keyword、date、long、double、boolean、ip -
复杂类型:对象类型、嵌套类型 -
特殊类型:用于描述地理位置的 geo_point、geo_shape
Elasticsearch 支持的数据类型远不止这些,由于篇幅原因,这里就不一一列举了。我找几个工作中常见的来介绍一下。
首先就是字符串了,Elasticsearch 中的字符串有 text 和 keyword 两种。其中 text 类型的字符串是可以被全文检索的,它会被分词器作用,
PUT my_index
{
"mappings": {
"properties": {
"full_name": {
"type": "text"
}
}
}
}
在设置字段类型为 text 时,还可以利用一些参数对这个字段进行更进一步的定制。
index
:标记这个字段是否能被搜索,默认是 true
search_analyzer
:被搜索时所使用的分词器,默认使用 setting 中设置的分词器
fielddata
:字段是否允许在内存中进行排序、聚合,默认是 false
meta
:关于字段的一些元数据
像一些id、邮箱、域名这样的字段,我们就需要使用 keyword 类型了。因为 keyword 类型可以支持排序、聚合,并且只能支持精确查询。
有些同学可能会把 ID 设置为数字类型,这也是没问题的,数字类型和 keyword 各有各的好处,使用数字类型可以进行范围查找,而使用 keyword 类型则有更高的查询效率。具体用哪种还要看使用场景。
日期类型在 Elasticsearch 中有三种表现形式
-
可以格式化成日期类型的字符串,如 "2020-07-26"
和"2015/01/01 12:10:30"
这样的 -
毫秒级时间戳用 long 类型表示 -
秒级时间戳用 integer 类型表示
在 Elasticsearch 内部,日期类型是以 long 类型的毫秒级时间戳存储的,时区使用的是0时区。
我们可以自定义时间格式,默认使用的是strict_date_optional_time||epoch_millis
「strict_date_optional_time_nanos」是通用的日期格式解析,至少要包含年份,如果要包含时间,则用T
分隔,例如yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ
或 yyyy-MM-dd
。
如果想要同时支持多种日期格式,可以使用format
字段
PUT my_index
{
"mappings": {
"properties": {
"date": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
}
Mapping参数
刚才我们提到配置 Mapping 的日期格式的参数format
,Mapping 还提供了很多其他的参数。
-
analyzer -
boost -
coerce -
copy_to -
doc_values -
dynamic -
eager_global_ordinals -
enabled -
fielddata -
fields -
format -
ignore_above -
ignore_malformed -
index_options -
index_phrases -
index_prefixes -
index -
meta -
normalizer -
norms -
null_value -
position_increment_gap -
properties -
search_analyzer -
similarity -
store -
term_vector
我们来介绍几个常用的字段。
fields
首先是fields
,它可以使同一个字段通过不同的方式实现不同的目的。
例如,我们可以对一个字符串字段设置为text
类型,用于全文检索,同时可以利用fields
设置为keyword
类型,用于排序和聚合。
PUT my-index-000001
{
"mappings": {
"properties": {
"city": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
}
}
}
}
查询时我们就可以使用city
进行全文检索,使用city.raw
进行排序和聚合。
GET my-index-000001/_search
{
"query": {
"match": {
"city": "york"
}
},
"sort": {
"city.raw": "asc"
},
"aggs": {
"Cities": {
"terms": {
"field": "city.raw"
}
}
}
}
enabled
有些时候,我们只想把某个字段作为数据存储来使用,并不需要用来做搜索,这时,我们就可以将这个字段禁用掉,字段被禁用以后,它所保存的值也不受 mapping 指定的类型控制。
PUT my-index-000001
{
"mappings": {
"properties": {
"user_id": {
"type": "keyword"
},
"last_updated": {
"type": "date"
},
"session_data": {
"type": "object",
"enabled": false
}
}
}
}
上面的例子中,我们禁用掉了 session_data
这个字段,这时,你既可以往 session_data
字段中存储 JSON 格式的数据,也可以存储非 JSON 格式的数据。
除了针对于单个字段的禁用以外,我们还可以直接禁用掉整个 mapping。我们来重新创建一个index
PUT my-index-000002
{
"mappings": {
"enabled": false
}
}
这时,文档所有的字段都不会被索引,只是用来存储。
需要注意的是,无论是具体字段中还是整个 mapping 的 enabled 属性都不可以被修改,因为一旦设置为 false,Elasticsearch 就不会对字段进行索引了,也不会校验数据的合法性,如果产生了脏数据以后再设置为 true,就会造成程序错误。
null_value
null 在 Elasticsearch 中是不可以被索引或搜索的,这里我们所说的 null 并不是狭义上某种语言的 null,而是所有的空值。例如所有值都是 null 的数组,总之,这里的定义就是没有值。
对于有需要搜索空值的业务怎么办呢?Elasticsearch 为我们提供了 null_value
这个参数,它可以指定一个值,搜索时使用这个值来替代空值。
举个栗子
PUT my-index-000001
{
"mappings": {
"properties": {
"status_code": {
"type": "keyword",
"null_value": "NULL"
}
}
}
}
我们给 status_code
字段设置了 null_value
为 "NULL"
。这里需要注意, null_value
的类型必须与要查找的数据类型相同,如果在这个例子中 status_code
的类型是long,那么就不能把null_value
设置为 "NULL"
。
dynamic
对于新增加的字段:
-
dynamic 设置为 true 时,一旦有新增字段的文档写入,Mapping 也会被更新 -
dynamic 设置为 false 时,Mapping 不会被更新,新增字段无法被索引,但信息会出现在 _source
中 -
dynamic 设置为 strict 时,文档写入失败
对于已有的字段,一旦已经有数据写入,就不再支持修改字段定义
Dynamic Mapping
我们在创建索引时,可以不用手动写 Mappings, Elasticsearch 会帮我们自动识别出字段的类型。我们称之为 Dynamic Mapping。不过有时推算的可能不是很准确。
Elasticsearch 自动识别类型是基于 JSON 的。数据类型的对应关系如下(表格来自 elastic 官网)
「JSON data type」 | 「Elasticsearch data type」 |
---|---|
null | No field is added. |
true or false | boolean field |
floating point number | float field |
integer | long field |
object | object field |
array | Depends on the first non-null value in the array. |
string | Either a date field (if the value passes date detection), a double or long field (if the value passes numeric detection) or a text field, with a keyword sub-field. |
Elasticsearch 支持的字段映射的数据类型在这个文档中,除了这些,其他的类型映射都需要显示的指定了。
关于日期类型,默认是可以映射的,但是 Elasticsearch 只能识别几种格式的日期yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis
。如果关掉了 date_detection
开关,那么就只能识别为字符串了。
PUT my-index-000001
{
"mappings": {
"date_detection": false
}
}
当然,你也可以根据需要自己指定要识别的日期格式,只需要使用 dynamic_date_formats
参数即可。
PUT my-index-000001
{
"mappings": {
"dynamic_date_formats": ["MM/dd/yyyy"]
}
}
Elasticsearch 还提供了一种把字符串型的数字识别为数字的能力,它是由 numeric_detection
开关控制的。
PUT my-index-000005
{
"mappings": {
"numeric_detection": true
}
}
PUT my-index-000005/_doc/1
{
"my_float": "1.0",
"my_integer": "1"
}
在这个例子中,my_float
会被识别为 float 类型,而 my_integer
会被识别为 long 类型。
Dynamic template
dynamic template 允许我们自定义 mapping ,并应用到具体索引上。dynamic template 的定义一般是这样的
"dynamic_templates": [
{
"my_template_name": {
... match conditions ...
"mapping": { ... }
}
},
...
]
my_template_name
可以是任意字符串。
match conditions
包括match_mapping_type
, match
, match_pattern
, unmatch
, path_match
, path_unmatch
这几种。
mapping
就是指匹配到的字段应该使用怎样的 mapping。下面我们介绍几种 match conditions
match_mapping_type
我们先来看一个简单的例子
PUT my-index-000001
{
"mappings": {
"dynamic_templates": [
{
"integers": {
"match_mapping_type": "long",
"mapping": {
"type": "integer"
}
}
},
{
"strings": {
"match_mapping_type": "string",
"mapping": {
"type": "text",
"fields": {
"raw": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
]
}
}
这里我们有两个模版,其一是使用 integer
类型来代替 long
类型,其二是将字符串类型映射为 keyword
。
match 和 unmatch
这两个比较简单,match 是指匹配到模式的字段, unmatch 是表示不匹配的字段。
PUT my-index-000001
{
"mappings": {
"dynamic_templates": [
{
"longs_as_strings": {
"match_mapping_type": "string",
"match": "long_*",
"unmatch": "*_text",
"mapping": {
"type": "long"
}
}
}
]
}
}
在这个例子中,我们需要的是 long_
开头的字符串,不需要 _text
结尾的字符串字段。
除了以上三种之外,其他的就是 match_pattern
用来进行正则匹配,path_match
和 path_unmatch
则是表示字段所在路径的是否匹配。
另外 dynamic template 还支持两种变量替换,分别是 {name}
和 {dynamic_type}
。其实 name 就是字段名,dynamic_type 就是检测出的字段类型。
总结
关于 Elasticsearch 的 mapping 我们就先聊这些,我认为 mapping 的配置是一个需要经验的事情,当你处理的 case 越来越多之后,就能比较轻松的知道如何更好的配置 mapping 了。此外,mapping 的许多字段和参数文中都没有涉及,对于我而言,大部分都是用到了现查文档,不过也还是建议大家看一看文档,起码遇到问题时能知道大概查找文档的一个方向。这样就会比身边人强不少。
本文分享自微信公众号 - 代码洁癖患者(Jackeyzhe2018)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
同时搞定Android和iOS的Dart语言(2):变量与常量
1. 定义和使用变量 尽管Dart是静态语言,但仍然拥有动态特性。所以定义变量也有如下2种方式。 使用数据类型定义变量 使用var定义变量 在Dart语言中,数据类型放在变量前面,格式如下: 数据类型 变量名; 在Dart语言中,每条语句后面必须跟分号(;),所以在定义变量时后面也要加分号。如下面的代码定义了一个整数类型的变量和一个字符串类型的变量。 int num; // 整数类型的变量String s; // 字符串类型的变量 在定义变量时可以同时为变量赋值,也称为初始化,所以可以用下面的代码为num和s赋值。 int num = 20;String s = "hello world"; 如果在定义变量时指定了数据类型,那么就意味着变量的数据类型不可以改变,例如,上面代码中已经将num变量定义为int类型,那么num就永远是int类型,不可以为该变量赋其他类型的值。 由于Dart语言中一切皆为对象,所以如果在定义变量时没有为其初始化,那么变量的默认值就是null。 另外一种定义变量的方法是使用var关键字,代码如下: var num = 20; // num被自动识别为int类型...
- 下一篇
HashCode为什么使用31作为乘数?
持续坚持原创输出,点击蓝字关注我吧 作者:小傅哥博客:https://bugstack.cn ❝ 沉淀、分享、成长,让自己和他人都能有所收获!😜 ❞ 目录 一、前言 二、面试题 三、资源下载 四、源码讲解 1. 固定乘积31在这用到了 2. 来自stackoverflow的回答 3. Hash值碰撞概率统计 4. Hash值散列分布 五、总结 一、前言 在面经手册的前两篇介绍了「《面试官都问我啥》」和「《认知自己的技术栈盲区》」,这两篇内容主要为了说明面试过程的考查范围,包括个人的自我介绍、技术栈积累、项目经验等,以及在技术栈盲区篇章中介绍了一个整套技术栈在系统架构用的应用,以此全方面的扫描自己有哪些盲区还需要补充。而接下来的章节会以各个系列的技术栈中遇到的面试题作为切入点,讲解技术要点,了解技术原理,包括;数据结构、数据算法、技术栈、框架等进行逐步展开学习。 在进入数据结构章节讲解之前可以先了解下,数据结构都有哪些,基本可以包括;数组(Array)、栈(Stack)、队列(Queue)、链表(LinkList)、树(Tree)、散列表(Hash)、堆(Heap)、图(Graph)。...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS8编译安装MySQL8.0.19
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Mario游戏-低调大师作品
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS6,7,8上安装Nginx,支持https2.0的开启