文章目录
- 1. Mapping介绍
- 2. 元字段
- 2.1 Identity 元字段
- 2.2 文档源元字段
- 2.3 索引元字段
- 2.4 路由元字段
- 2.5 其他元字段
- 3. 字段的数据类型
- 3.1 核心数据类型:
- 3.2 复杂数据类型
- 3.3 Geo 数据类型
- 3.4 专用数据类型
- 3.5 多字段类型multi-fields
- 3. Dynamic Mapping
- 4. 类型的自动识别
- 5. 能否更改 Mapping 的字段类型
- 5.1 新增字段
- 5.2 已有字段
- 5.3 控制 Dynamic Mappings
- 6. 如何显示定义一个 Mapping
- 7. 控制当前字段是否被索引
- 7.1 Index Options
- 7.2 null_value
- 7.3 copy_to
- 7.4 数组类型
- 8. 动态映射的弊端
- 8.1 字段匹配不准确
- 8.2 字段匹配相对准确,但不是用户期望的。
- 8.3 占据多余的存储空间。
- 8.4 Mapping可能错误泛滥。
1. Mapping介绍
Mapping 类似数据库的 schema 的定义,作用如下
- 定义索引中的字段名称
- 定义字段的数据类型,例如字符串,数字,布尔…
- 字段,倒排索引的相关配置,(Analyzed or Not Analyzed,Analyzer)
Mapping 会把 JSON 文档映射称 Lucene 所需的扁平格式
 一个 Mapping 属于一个索引的 Type
- 每个文档都属于一个 Type
- 一个 Type 有一个 Mapping 定义
- 7.0 开始,不需要在 Mapping 定义指定 type 信息
2. 元字段
各种元字段,它们都以一个下划线开头,例如 _type 、 _id 和 _source。
2.1 Identity 元字段
• _index:表示它所属的文档的索引。
 • uid:type和_id的组合键。
 • _id:表示文档的ID。
2.2 文档源元字段
• _source:表示代表文档正文的原始JSON对象
 • size:它表示source字段的大小(以字节为单位)
2.3 索引元字段
• _field_names:表示给定文档中包含非空值的所有字段。
 • _timestamp:与每个文档相关联的手动或自动生成的时间戳。
 • _ttl:表示应该保持活动状态的时间,之后该时间将被删除。
2.4 路由元字段
• _parent:当必须创建父子关系时,使用此方法。
 • _routing:一个专有值,有助于将给定文档路由到指定的分片。
2.5 其他元字段
• _meta:表示应用程序特定的元数据。
3. 字段的数据类型
3.1 核心数据类型:
核心数据类型是大多数系统都可用并支持的基本数据类型,例如
 • 整型 integer,
 • 双精度浮点型 double,
 • 长整型 long,
 • 短整型 short,
 • 字节类型 byte,
 • 单精度浮点型 float,
 • 字符串类型 string(text 和 keyword),
 • 布尔类型 Boolean,
 • 日期类型 date,
 • 二进制类型 binary。
3.2 复杂数据类型
复杂数据类型是核心数据类型的组合,如:
 • 数组类型 arrays,
 • JSON 对象类型:Object,
 • 嵌套数据类型: nested。
 数组类型需要多啰嗦几句。
 第一: 任何类型都可以包含一个或者多个元素,当数据包含多个元素的时候,它就是数组类型。
 第二:数据类型要求一个组内的数据类型一致。
 实战举例如下:
PUT my_index_010/_doc/1
{
"class_tags": [
"新闻",
"论坛",
"博客",
"电子报"
],
"info_array": [
{
"name": "Mary",
"age": 12
},
{
"name": "John",
"age": 10
}
],
"size_tags": [
0,
50,
100
]
}
如上示例,定义三种数组类型:
 • class_tags:媒体分类数组,数组元素是字符串类型
 • info_array:个人信息数组,数组元素是object类型
 • size_tags: 大小规模数组,数组元素是整型
3.3 Geo 数据类型
地理数据类型是用于保存详细信息(例如地点的地理位置)的数据类型,例如:
 • geo_point用于标识纬度和经度。
3.4 专用数据类型
专用数据类型是那些具有本质上唯一的详细信息的数据类型,例如:
 • IP地址,自动完成建议以及从字符串中计数令牌。
 • completion, 补全建议导航搜索功能。
3.5 多字段类型multi-fields
上个例子,说明问题(考题中一种举例)。
PUT my_index_011
{
"mappings": {
"properties": {
"cont": {
"type": "text",
"analyzer": "english",
"fields": {
"keyword": {
"type": "keyword"
},
"stand":{
"type":"text",
"analyzer":"standard"
}
}
}
}
}
}
上述demo定义了类型cont,使用english分词器,基于英文关键词全文检索。
 同时为cont定义了两个扩展类型:
 • 其一:keyword,用于精准匹配。
 • 其二:standard,用于全文检索。
 公司项目中实战,我们往往对需要全文检索的字段设置:text类型,并且指定:ik_max_word等中文分词器。
 除此之外,如果这个字段还需要整个字段聚合或者排序等操作,我们往往还会设置其为keyword类型。
 举例如下:
PUT my_index_012
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
以上,在你的公司里面是不是也很常见?
 multi-fields是上面所有知识点的综合,一个知识点覆盖了Mapping 类型部分的绝大部分认知,所以是考试的重中之重。
3. Dynamic Mapping
在写入文档的时候,如果索引不存在,会自动创建索引
- Dynamic Mapping 的机制,使得我们无需手动定义 Mappings。Elasticsearch
 会自动根据文档信息,推算出字段的类型
- 但是会有时候推算不对。例如地理位置信息
- 当类型如果设置不对时,会导致一些功能无法正常运行,例如 Range 查询 
4. 类型的自动识别

//写入文档
    PUT mapping_test/_doc/1
    {
      "firstName":"Lee",
      "lastName":"Crazy",
      "loginDate":"2019-10-22T21:08:48"
    }
    //查看Mapping 文件
    GET mapping_test/_mapping
//放回结果
    {
      "mapping_test" : {
        "mappings" : {
          "properties" : {
            "firstName" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "lastName" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "loginDate" : {
              "type" : "date"
            }
          }
        }
      }
    }
//dynamic mapping 推断字符的类型
PUT mapping_test/_doc/1
{
  "uid":"123",
  "isVip": false,
  "isAdmin":"true",
  "age": 18,
  "heigh" : 180
}
GET mapping_test/_mapping
//返回结果
{
  "mapping_test" : {
    "mappings" : {
      "properties" : {
        "age" : {
          "type" : "long"
        },
        "heigh" : {
          "type" : "long"
        },
        "isAdmin" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "isVip" : {
          "type" : "boolean"
        },
        "uid" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}
5. 能否更改 Mapping 的字段类型
两种情况
5.1 新增字段
- Dynamic 设置为 true 时,一定有新增字段的文档写入,Mapping 也同时被更新
- Dynamic 设为 false,Mapping 不会被更新,自增字段的数据无法被索引,但是信息会出现在_source 中
- Dynamic 设置成 Strict 文档写入失败
5.2 已有字段
- 一旦已经有数据写入,就不在支持修改字段定义
- Luene 实现的倒排索引,一旦生成后,就不允许修改
- 如果希望改变字段类型,必须 Reindex API,重建索引
原因
- 如果修改了字段的数据类型,会导致已被索引的属于无法被搜索
- 但是如果是增加新的字段,就不会有这样的影响
5.3 控制 Dynamic Mappings
| TRUE | FALSE | strict | |
| 文档可索引 | YES | YES | NO | 
| 字段可索引 | YES | NO | NO | 
| Mapping 被更新 | YES | NO | NO | 
- 当 dynamic 被设置成 false 时候,存在新增字段的数据写入,该数据可以被索引,当时新增字段被废弃
- 当设置成 Strict 模式的时候,数据写入直接出错
//默认Mapping支持dynamic,写入的文档加入新的字段
  
  PUT dynamic_mapping_test/_doc/1
  {
    "newField":"someValue"
  }
  //能被搜索到
  POST dynamic_mapping_test/_search
  {
    "query": {
      "match": {
        "newField": "someValue"
      }
    }
  }
  ------------------------------------------------
  //修改为dynamic false
  PUT dynamic_mapping_test/_mapping
  {
    "dynamic":false
  }
  //新增anotherField 成功
  PUT dynamic_mapping_test/_doc/10
  {
    "anotherField":"someValue"
  }
  //重新去查询,但是anotherField 未被搜索到
  POST dynamic_mapping_test/_search
  {
    "query": {
      "match": {
        "anotherField": "someValue"
      }
    }
  }
  //查看mapping,无anotherField
  GET dynamic_mapping_test/_mapping
----------------------------------------------  
  //修改为dynamic strict
  PUT dynamic_mapping_test/_mapping
  {
    "dynamic": "strict"
  }
  //新增newField 报错
  PUT dynamic_mapping_test/_doc/12
  {
    "lastField":"value"
  }
6. 如何显示定义一个 Mapping
PUT movies
{
"mappings" : {
// define your mappings here
}
}
自定义 Mapping 的一些建议:
 1.可以参考 API 手册,纯手写
 2.为了减少输入的工作量,减少出错率,依照以下步骤:
- 创建一个临时的 index,写入一些样本数据
- 通过访问 Mapping API 获得该临时文件的动态 Mapping 定义
- 修改后用,使用该配置创建的索引
- 删除临时索引
7. 控制当前字段是否被索引
index - 控制当前字段是否被索引。默认为 true。如果设置成 false,该字段不可被搜索
DELETE users
//定义mappings
PUT users
{
"mappings" : {
"properties" : {
"firstName" : {
"type" : "text"
},
"lastName" : {
"type" : "text"
},
"mobile" : {
"type" : "text",
"index": false
}
}
}
}
//写入数据
PUT users/_doc/1
{
"firstname":"Ruan",
"lastname":"Yiming",
"mobile":12345678
}
//查询数据
POST /users/_search
{
"query":{
"match": {
"mobile": "12345678"
}
}
}
返回内容为400,"reason": "Cannot search on field [mobile] since it is not indexed."
7.1 Index Options
四种不同级别的 Index Options 配置,可以控制倒排索引记录的内容
- docs - 记录 doc id
- freqs - 记录 doc id 和 term frequencies
- positions - 记录 doc id /term frequencies /term position
- offsets - doc id / term frequencies / term posistion / character offects
Text 类型默认记录 postions,其他默认为 docs
 记录内容越多,占用存储空间越大
PUT users
{
"mappings" : {
"properties" : {
"firstName" : {
"type" : "text"
},
"lastName" : {
"type" : "text"
},
"mobile" : {
"type" : "text",
"index": false
},
"bio": {
"type" : "text",
"index_options": "offsets"
}
}
}
}
7.2 null_value
- 需要对 NULL 值实现搜索
- 只有 Keyword 类型支持设定 Null_Value
DELETE users
PUT users
{
"mappings" : {
"properties" : {
"firstName" : {
"type" : "text"
},
"lastName" : {
"type" : "text"
},
"mobile" : {
"type" : "keyword", //这个如果是text 无法设置为空
"null_value": "NULL"
}
}
}
}
//推送带有空值得数据
PUT users/_doc/2
{
"firstName":"Li",
"lastName": "Sunke",
"mobile": null
}
//查询mobile为null的数据
GET users/_search?q=mobile:NULL
或者
GET users/_search
{
"query":{
"match": {
"mobile": "NULL"
}
}
}
//搜索结果
"_source" : {
"firstName" : "Li",
"lastName" : "Sunke",
"mobile" : null
}
7.3 copy_to
- _all 在 7 中已经被 copy_to 所替代
- 满足一些特定的搜索需求
- copy_to 将字段的数值拷贝到目标字段,实现类似 _all 的作用
- copy_to 的目标字段不出现在_source 中
DELETE users
//设定带copy_to的mapping
PUT users
{
"mappings": {
"properties": {
"firstName":{
"type": "text",
"copy_to": "fullName"
},
"lastName":{
"type": "text",
"copy_to": "fullName"
}
}
}
}
//写入数据
PUT users/_doc/1
{
"firstName":"Ruan",
"lastName": "Yiming"
}
//get查询
GET users/_search?q=fullName:(Ruan Yiming)
//返回一个值
//post查询
POST users/_search
{
"query": {
"match": {
"fullName": {
"query": "Ruan Yiming",
"operator": "and"
}
}
}
}
//返回一个值
7.4 数组类型
Elasticsearch 中不提供专门的数组类型。但是任何字段,都可以包含多个相同类型的数值.
DELETE users
PUT users/_doc/1
{
"name":"onebird",
"interests":"reading"
}
PUT users/_doc/1
{
"name":"twobirds",
"interests":["reading","music"]
}
GET users/_mapping
//部分代码
"interests" : {
"type" : "text", //类型还是text,不是数组
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
8. 动态映射的弊端
8.1 字段匹配不准确
如:date类型匹配为keyword类型。
 举例:
DELETE my_index_014
PUT my_index_014/_doc/1
{
"create_date": "2020-12-26 12:00:00"
}
GET my_index_014/_mapping
返回结果可知:create_date 是 text和keyword类型。不是我们期望的date类型。
 是有解决方案的,如下,但也需要提前设置匹配规则。
DELETE my_index_014
PUT my_index_014
{
"mappings": {
"dynamic_date_formats": ["yyyy-MM-dd HH:mm:ss"]
}
}
8.2 字段匹配相对准确,但不是用户期望的。
举例: 用户期望text类型支持ik分词,但默认的是standard标准分词器。
 当然也会有解决方案,借助动态 template解决。
8.3 占据多余的存储空间。
举例:string类型匹配为:text和keyword两种类型。意味着两次索引。
 但实际用户极有可能只期望排序和聚合的keyword类型。
 或者极有可能只需要存储text类型,如:网页正文内容只需要全文检索,不需要排序和聚合操作。
8.4 Mapping可能错误泛滥。
不小心写错的查询语句,由于使用了put操作,很可能写入Mapping中也经常见。
 基于此,实际工程开发实战中,建议:使用静态Mapping,提前定义好字段。
参考资料:
 极客时间:Elasticsearch核心技术与实战 










