前面我们聊了 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 中有三种表现形式

  1. 可以格式化成日期类型的字符串,如"2020-07-26""2015/01/01 12:10:30"这样的
  2. 毫秒级时间戳用 long 类型表示
  3. 秒级时间戳用 integer 类型表示

在 Elasticsearch 内部,日期类型是以 long 类型的毫秒级时间戳存储的,时区使用的是0时区。

我们可以自定义时间格式,默认使用的是strict_date_optional_time||epoch_millis

strict_date_optional_time_nanos是通用的日期格式解析,至少要包含年份,如果要包含时间,则用T分隔,例如yyyy-MM-dd'T'HH:mm:ss.SSSSSSZyyyy-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_matchpath_unmatch 则是表示字段所在路径的是否匹配。

另外 dynamic template 还支持两种变量替换,分别是 {name}{dynamic_type}。其实 name 就是字段名,dynamic_type 就是检测出的字段类型。

总结

关于 Elasticsearch 的 mapping 我们就先聊这些,我认为 mapping 的配置是一个需要经验的事情,当你处理的 case 越来越多之后,就能比较轻松的知道如何更好的配置 mapping 了。此外,mapping 的许多字段和参数文中都没有涉及,对于我而言,大部分都是用到了现查文档,不过也还是建议大家看一看文档,起码遇到问题时能知道大概查找文档的一个方向。这样就会比身边人强不少。

Elasticsearch从入门到放弃:瞎说Mapping的更多相关文章

  1. Elasticsearch从入门到放弃:分词器初印象

    Elasticsearch 系列回来了,先给因为这个系列关注我的同学说声抱歉,拖了这么久才回来,这个系列虽然叫「Elasticsearch 从入门到放弃」,但只有三篇就放弃还是有点过分的,所以还是回来 ...

  2. Elasticsearch从入门到放弃:索引基本使用方法

    前文我们提到,Elasticsearch的数据都存储在索引中,也就是说,索引相当于是MySQL中的数据库.是最基础的概念.今天分享的也是关于索引的一些常用的操作. 创建索引 curl -X PUT & ...

  3. Elasticsearch从入门到放弃:文档CRUD要牢记

    在Elasticsearch中,文档(document)是所有可搜索数据的最小单位.它被序列化成JSON存储在Elasticsearch中.每个文档都会有一个唯一ID,这个ID你可以自己指定或者交给E ...

  4. Elasticsearch从入门到放弃:再聊搜索

    在前文中我们曾经聊过搜索文档的方法,Elasticsearch 一般适用于读多写少的场景,因此我们需要更多的关注读操作. Elasticsearch 提供的 Search API 可以分为 URI S ...

  5. Elasticsearch从入门到放弃:浅谈算分

    今天来聊一个 Elasticsearch 的另一个关键概念--相关性算分.在查询 API 的结果中,我们经常会看到 _score 这个字段,它就是用来表示相关性算分的字段,而相关性就是描述一个文档和查 ...

  6. elasticsearch入门到放弃之elasticsearch-head

    elasticsearch-head可理解为跟DBeaver一样是一个数据可视化工具,但是这个工具并没有理想中那么好用坑也是很多,我已经在我的github上fork了一份修改后的版本:https:// ...

  7. Go从入门到放弃

    Go语言介绍 为什么你应该学习Go语言? 开发环境准备 从零开始搭建Go语言开发环境 VS Code配置Go语言开发环境 Go语言基础 Go语言基础之变量和常量 Go语言基础之基本数据类型 Go语言基 ...

  8. CYQ.Data 从入门到放弃ORM系列:开篇:自动化框架编程思维

    前言: 随着CYQ.Data 开始回归免费使用之后,发现用户的情绪越来越激动,为了保持这持续的激动性,让我有了开源的念头. 同时,由于框架经过这5-6年来的不断演进,以前发的早期教程已经太落后了,包括 ...

  9. [精品书单] C#/.NET 学习之路——从入门到放弃

    C#/.NET 学习之路--从入门到放弃 此系列只包含 C#/CLR 学习,不包含应用框架(ASP.NET , WPF , WCF 等)及架构设计学习书籍和资料. C# 入门 <C# 本质论&g ...

随机推荐

  1. DVWA学习记录 PartⅥ

    Insecure CAPTCHA 1. 题目 Insecure CAPTCHA(全自动区分计算机和人类的图灵测试),意思是不安全的验证码. 指在进行验证的过程中,出现了逻辑漏洞,导致验证码没有发挥其应 ...

  2. 批量删除当前文件夹下面的.svn文件夹

    for /r . %%a in (.) do @if exist "%%a\.svn" rd /s /q "%%a\.svn 使用方法: 新建text文档,复制上面的文本 ...

  3. python os 模块的使用

    1.显示当前文件的绝对路径: os.path.abspath(__file__) 2.显示当前文件父目录的路径 os.path.dirname(os.path.abspath(__file__name ...

  4. 数据可视化之PowerQuery篇(二)这个方法帮你快速计算列

    https://zhuanlan.zhihu.com/p/81846862 PowerQuery中,对两列或者多列的计算一般通过添加自定义列来实现,以下表为例, 如果需要1月和2月数据的合计,可以添加 ...

  5. OSCP Learning Notes - WebApp Exploitation(5)

    Remote File Inclusion[RFI] Prepare: Download the DVWA from the following website and deploy it on yo ...

  6. Web Scraping using Python Scrapy_BS4 - Software

    Install the following software before web scraping. Visual Studio Code Python and Pip pip install vi ...

  7. Linux内核功能介绍及如何使用保护您的网页安全

    在本文中,我们快速浏览了Linux内核的许可流程,并向您展示了如何使用它们来保护您的网页或应用安全 传统上,Linux内核通过以下两类来区分其进程: 特权进程:这些进程使用户可以绕过所有内核权限检查. ...

  8. HDU - 1520 Anniversary party (树的最大独立集)

    Time limit :1000 ms :Memory limit :32768 kB: OS :Windows There is going to be a party to celebrate t ...

  9. C++语法小记---函数对象

    函数对象 用于替代函数指针 优势:函数对象内部可以保存状态,而不必使用全局变量或静态局部变量 关键:重载"()"操作符 #include<iostream> #incl ...

  10. 题解 SP1812 【LCS2 - Longest Common Substring II 】

    对于本题这样的多字符串的子串匹配问题,其实用广义后缀自动机就可以很好的解决,感觉会比普通的后缀自动机做法方便一些. 首先记录出每个节点被多少个字符串更新,也就是记录每个节点有多少个字符串能到达它,可以 ...