1.Mapping的概念

对于关系型数据库,在创建表时,会对表字段进行声明,来描述字段的名称,类型,限制大小,别名等
而在es中,同样需要对一个type中的文档进行字段声明,包括字段类型,所否分词,是否索引,分词规则等多种属性,mapping就是提供这个作用的。
es中提供了动态mapping以及静态mapping两种方式来声明一个类型中文档的字段。接下来会对mapping做一个详细的讲解

2.Mapping属性

mapping的属性,可以理解为一个type中,每个字段所具备的属性。(PS:全量属性具体参考:链接
共包含以下常用属性:

属性名 描述
type 字段类型,常用的有 text、integer 等等。
index 默认true,字段是否建立倒排索引,false情况下不能被搜索,但支持聚合分析
enable 默认true,字段是否建立倒排索引以及doc value,false情况下不能被搜索以及聚合,但是节约了内存空间
store 默认false,字段是否额外存储,如果需要查询获取的字段只是文档中的小数据,这些字段可以store,减少IO。而且这个存储是独于 _source 的存储的。
doc_values 默认true,优化字段排序聚合脚本访问,耗用磁盘空间
fields 多字段特性。让一个字段拥有多个子字段类型,使得一个字段能够被多个不同的索引方式进行索引。
norms 默认为true,是否支持评分,如果字段只用来过滤和聚合分析,而不需要被评分,那么可以设置为false;type 为 text 时,默认为 true;而 type 为 keyword 时,默认为 false。
analyzer 指定索引和搜索时的分析器,如果同时指定 search_analyzer 则搜索时会优先使用 search_analyzer。
search_analyzer 指定搜索时的分析器,搜索时的优先级最高。
fielddata 默认false,针对text类型排序、聚合、脚本访问优化,尽量避免,操作昂贵
index_options 用于设置倒排(索引)列表包含的信息,这些信息用于搜索(Search)和高亮显示:
- docs:只索引文档编号(Doc Number);
- freqs:索引文档编号和词频率(term frequency);
- positions:索引文档编号,词频率和词位置(序号);
- offsets:索引文档编号,词频率,词偏移量(开始和结束位置)和词位置(序号)。

默认情况下,被分词的字符串(analyzed string)字段使用 positions,其他字段默认使用 docs。
此外,需要注意的是 index_option 是 elasticsearch 特有的设置属性;临近搜索和短语查询时,index_option 必须设置为 offsets,同时高亮也可使用 postings highlighter。
记录内容越多,占用存储空间越大。 | | term_vector | 索引选项控制词向量相关信息:
- no:默认值,表示不存储词向量相关信息;
- yes:只存储词向量信息;
- with_positions:存储词项和词项位置;
- with_offsets:存储词项和字符偏移位置;
- with_positions_offsets:存储词项、词项位置、字符偏移位置。
term_vector 是 lucene 层面的索引设置。 | | similarity | 指定文档相似度算法(也可以叫评分模型):
- BM25:es 5 之后的默认设置。
| | dynamic | 默认值为true,允许动态地向文档类型中加入新的字段。推荐设置为false,禁止向文档中添加字段,这样,文档类型的所有字段必须在索引映射的properties属性中显式定义,在properties字段中未定义的字段都将会ElasticSearch忽略。
dynamic设置为ture:默认值,新增加的字段被添加到索引映射中;
dynamic设置为false:新增加的字段会被忽略;
dynamic设置为strict:当向文档中新增字段时,ElasticSearch引擎抛出异常; |

3.字段类型(type)详解

核心基础数据类型简介

一级分类 二级分类 具体类型
核心类型 字符串类型 text、keyword
数字类型 byte、short、integer、long、double、float、half_float、scaled_float
日期类型 date
布尔类型 boolean
二进制类型 binary
范围类型 range

字符串类型

text

会做分词处理。类型为 text的字段可以通过全文检索搜索到。如果一个字段是要被全文搜索的,比如邮件内容、产品描述、新闻内容,应该使用 text 类型。设置 text 类型以后,字段内容会被分析,在生成倒排索引以前,字符串会被分词器分成一个一个词条(term)。text 类型的字段不用于排序,且很少用于聚合(Terms Aggregation 除外)。

keyword

不会做分词处理。类型为 keyword 的字段只能通过精确值搜索到。类型适用于索引结构化的字段,比如 email 地址、主机名、状态码和标签,通常用于过滤(比如,查找已发布博客中 status 属性为 published 的文章)、排序、聚合。
PS:Elasticsearch 5.X 之后的字段类型不再支持 string,由 text 或 keyword 取代。如果仍使用 string,会给出警告。

数字类型

数据类型共包括以下字段

类型 取值范围
byte -128 至 127
short -32768 至 32767
integer -2^31 至 2^31-1
long -2^63 至 2^63-1
double 64 位双精度 IEEE 754 浮点类型
float 32 位单精度 IEEE 754 浮点类型
half_float 16 位半精度 IEEE 754 浮点类型
scaled_float 缩放类型的浮点数

注意点

对于 float、half_float 和 scaled_float,-0.0 和 +0.0 是不同的值,使用 term 查询查找 -0.0 不会匹配 +0.0,同样 range 查询中上边界是 -0.0,也不会匹配 +0.0,下边界是 +0.0,也不会匹配 -0.0。
对于数字类型的字段,在满足需求的情况下,要尽可能选择范围小的数据类型。比如,某个字段的取值最大值不会超过 100,那么选择 byte 类型即可。迄今为止,吉尼斯世界记录的人类的年龄的最大值为 134 岁,对于年龄字段,short 类型足矣。字段的长度越短,索引和搜索的效率越高。
处理浮点数时,优先考虑使用 scaled_float 类型。scaled_float 是通过缩放因子把浮点数变成 long 类型,比如价格只需要精确到分,price 字段的取值为 57.34,设置放大因子为 100,存储起来就是 5734。所有的 API 都会把 price 的取值当作浮点数,事实上 Elasticsearch 底层存储的是整数类型,因为压缩整数比压缩浮点数更加节省存储空间。

创建示例
  1. {
  2. "mappings": {
  3. "my_type": {
  4. "properties": {
  5. "number_of bytes": {
  6. "type": "integer"
  7. },
  8. "time_in_seconds": {
  9. "type": "float"
  10. },
  11. "price": {
  12. "type": "scaled_float",
  13. "scaling_factor": 100
  14. }
  15. }
  16. }
  17. }
  18. }

日期类型

JSON 中没有日期类型,所以在 Elasticsearch 中的日期可以是以下几种形式:

  • 格式化日期的字符串,如 “2015-01-01” 或 “2015/01/01 12:10:30”。
  • 毫秒时间戳(epoch 指的是一个特定的时间:1970-01-01 00:00:00 UTC)。
  • 秒级时间戳。

Elasticsearch 内部会把日期转换为 UTC(世界标准时间),并将其存储为毫秒时间戳,这样做的原因是和字符串相比,数值在存储和处理时更快。

布尔类型

如果一个字段是布尔类型,可接受的值为 true、false。Elasticsearch 5.4 版本以前,可以接受被解释为 true 或 false 的字符串和数字,5.4 版本以后只接受 true、false、”true”、”false”。

二进制(binary)类型

binary 类型数据格式为base64 编码的字符串,默认不额外存储,也不可搜索。

Range类型

range 类型的使用场景包括网页中的时间选择表单、年龄范围选择表单等,range 类型支持的类型和取值范围如下表:

类型 范围
integer_range -2^31 至 2^31-1
long_range -2^63 至 2^63-1
float_range 32-bit IEEE 754
double_range 64-bit IEEE 754
date_range 64 位整数,毫秒计时

复合数据类型简介

一级分类 二级分类 具体类型
复合数据类型 数组类型 array
对象类型 object
嵌套类型 nested

数组类型

Elasticsearch 没有专用的数组类型,默认情况下任何字段都可以包含0个或者多个值,但是一个数组中的值必须是同一种类型。例如:

  1. 整型数组:[1,3]
  2. 嵌套数组:[1,[2,3]],等价于 [1,2,3]
  3. 对象数组:[{“city”:”amsterdam”,”country”:”nederland”},{“city”:”brussels”,”country”:”belgium”}]

动态添加数据时,数组的第一个值的类型决定整个数组的类型。混合数组类型是不支持的,比如:[1,”abc”]。数组可以包含 null 值,空数组 [ ] 会被当作 missing field 对待。
在文档中使用 array 类型不需要提前做任何配置,默认支持。

对象类型

对象类型很好理解,即JSON对象嵌套对象,需要注意的是,es中会对嵌套的JSON对象做扁平化处理
例如,数据存储参数

  1. {
  2. "name":"ftc",
  3. "friend":[
  4. "name":"zyl"
  5. ]
  6. }

数据存储格式

  1. {
  2. "name":"ftc",
  3. "friend.name":"zyl"
  4. }

嵌套类型

嵌套类型是一种特殊的对象类型,上文已描述,es本身会对对象类型字段做扁平化处理,那么当存储的对象类型为对象数组时,会出现关联关系失效的情况
示例,存储数据参数

  1. {
  2. "name":"ftc",
  3. "friends":[
  4. {
  5. "name":"zyl",
  6. "age":2
  7. },
  8. {
  9. "name":"skx",
  10. "age":18
  11. }
  12. ]
  13. }

es做扁平化处理之后的数据结构

  1. {
  2. "name":"ftc",
  3. "friends.name":["zyl","skx"],
  4. "friend.age":[2,18]
  5. }

可以看出,数据直接的关联性没有了
如果需要索引对象数组并避免上述问题的产生,应该使用 nested 对象类型而不是 object 类型,nested 对象类型可以保持数组中每个对象的独立性。Nested 类型将数组中每个对象作为独立隐藏文档来索引,这意味着每个嵌套对象都可以独立被搜索
为了防止过度定义嵌套字段的数量,每个索引可以定义的嵌套字段被限制在 50 个。

地理数据类型简介

Elasticsearch 的地理相关类型有地理坐标类型地理图形类型

一级分类 二级分类 具体类型
地理类型 地理坐标类型 geo_point
地理图形类型 geo_shape

地理坐标(geo_point)类型

geo point 类型用于存储地理位置信息的经纬度,可用于以下几种场景:

  1. 查找一定范围内的地理位置。
  2. 通过地理位置或者相对中心点的距离来聚合文档。
  3. 把距离因素整合到文档的评分中。
  4. 通过距离对文档排序。

    存储格式
  5. 经纬度JSON格式:{“lat”:41.12,”lon”:-71.34}

  6. 经纬度字符串格式:”41.12,-71.34”
  7. 地理坐标hash值:”u1269qu5dcgp”
  8. 经纬度数组形式:[41.12,-71.34]

    地理图形(geo_shape)类型

    存储格式
    geo_shape类型可以存储一块区域,比如矩形、三角形或者其他多边形。地理图形类型默认的存储格式为geoJson。geojson示例格式如下:
    1. {
    2. "type":"Point",
    3. "coordinates":[
    4. 100,
    5. 0
    6. ]
    7. }
    类型说明
    | Elasticsearch 类型 | 说明 | | —- | —- | | point | 一个单独的经纬度坐标点 | | linestring | 任意的线条,由两到多个点组成 | | polygon | 由 N+1 个点组成的封闭 N 边形 | | multipoint | 一组不连续但有可能相关联的点 | | multilinestring | 多条不关联的线 | | multipolygon | 多个不关联的多边形 | | geometrycollection | 几何对象的集合 | | envelop | 由左上角坐标或右下角坐标确定的封闭矩形 | | circle | 由圆心和半径确定的圆,默认单位为米 |

特殊类型

一级分类 二级分类 具体类型
特殊类型 IP 类型 ip
范围类型 completion
令牌计数类型 token_count
附件类型 attachment
过滤类型 percolator

IP类型数据

ip 类型的字段用于存储 IPv4 或者 IPv6 的地址。如”192.168.1.1”或”192.168.0.0/16”

token_count 类型

token_count 用于统计text分词后的词条个数,本质上是一个整数型字段。举个例子,映射中指定 name 为 text 类型,增加name.length 字段用于统计分词后词项的长度,类型为 token_count,分词器为标准分词器,命令如下:

  1. {
  2. "mappings": {
  3. "my_type": {
  4. "properties": {
  5. "name": {
  6. "type": "text",
  7. "fields": {
  8. "length": {
  9. "type": "token_count",
  10. "analyzer": "standard"
  11. }
  12. }
  13. }
  14. }
  15. }
  16. }
  17. }

4.动态映射

什么是动态映射

动态映射是一种偷懒的方式,可直接创建索引并写入文档,文档中字段的类型是 Elasticsearch 自动识别的,不需要在创建索引的时候设置字段的类型。
在实际项目中,如果遇到的业务在导入数据之前不确定有哪些字段,也不清楚字段的类型是什么,使用动态映射非常合适。当 Elasticsearch 在文档中碰到一个以前没见过的字段时,它会利用动态映射来决定该字段的类型,并自动把该字段添加到映射中
PS:在不确定索引类型字段时,可以先创建临时索引,然后存储数据,查询映射。再删除临时索引。这样就得到数据的基本映射了,可以在其上做精细化修改

动态映射规则

JSON 格式的数据 自动推测的字段类型
null 没有字段被添加
true or false boolean 类型
浮点类型数字 float 类型
数字 long 类型
JSON 对象 object 类型
数组 由数组中第一个非空值决定
string 有可能是 date 类型(若开启日期检测)、double 或 long 类型、text 类型、keyword 类型

使用动态 mapping 要结合实际业务需求来综合考虑,如果将 Elasticsearch 当作主要的数据存储使用,并且希望出现未知字段时抛出异常来提醒注意这一问题,那么开启动态 mapping 并不适用。在 mapping 中可以通过 dynamic 设置来控制是否自动新增字段,接受以下参数:

  • true:默认值为 true,自动添加字段。
  • false:忽略新的字段。
  • strict:严格模式,发现新的字段抛出异常。

    如何控制动态映射规则

    日期检测

    es中当字符串的格式为:yyyy/MM/dd HH:mm:ss Z||yyyy/MM/dd Z 时,es动态模板会自动认为该字符串为日志类型
    可以通过:date_detection来关闭日期模板匹配
    可以通过:dynamic_date_formats来指定日期字符匹配模板
    例如:
    1. PUT my_index
    2. {
    3. "mappings": {
    4. "dynamic_date_formats": ["MM/dd/yyyy"]
    5. }
    6. }

    数字检测

    动态映射有时会将数字映射为字符串类型,可以通过开启数字检测来解决该问题
    例如:
    1. PUT my_index
    2. {
    3. "mappings": {
    4. "numeric_detection": true
    5. }
    6. }

    动态映射模板

    当然,也可以通过定义动态映射模板,来格式化动态映射的规则
    动态映射模板包含以下关键字:
  1. match_mapping_type:用来匹配当前映射的字段类型。可以理解为,当字段类型为match_mapping_type时
  2. match、unmatch 、match_pattern:用来匹配当前映射字段的名称。可以理解为,当字段名称匹配match、match_pattern时,当字段名称不匹配unmatch时
  3. path_match、path_unmatch:用来匹配当前映射字段的路径,通常用来匹配嵌套字段。可以理解为,当字段路径为path_match时,当字段路径不匹配path_unmatch时
  4. {name}, {dynamic_type} :用来进行占位符匹配,具体怎么用,都可以,但是最终映射的值为name或动态类型
  5. 当然也可以直接强制指定类型和属性

    动态映射模板示例

    match_mapping_type

    dynamic_templates是动态模板名称,什么字段都可以。integers与strings时动态模板项名称,什么字段都可以
    1. PUT my_index
    2. {
    3. "mappings": {
    4. "dynamic_templates": [
    5. {
    6. "integers": {
    7. "match_mapping_type": "long",
    8. "mapping": {
    9. "type": "integer"
    10. }
    11. }
    12. },
    13. {
    14. "strings": {
    15. "match_mapping_type": "string",
    16. "mapping": {
    17. "type": "text",
    18. "fields": {
    19. "raw": {
    20. "type": "keyword",
    21. "ignore_above": 256
    22. }
    23. }
    24. }
    25. }
    26. }
    27. ]
    28. }
    29. }

    match、unmatch

    1. PUT my_index
    2. {
    3. "mappings": {
    4. "dynamic_templates": [
    5. {
    6. "longs_as_strings": {
    7. "match": "long_*",
    8. "unmatch": "*_text",
    9. "mapping": {
    10. "type": "long"
    11. }
    12. }
    13. }
    14. ]
    15. }
    16. }

    match_pattern

    1. {
    2. "mappings": {
    3. "dynamic_templates": [
    4. {
    5. "longs_as_strings": {
    6. "match_pattern": "regex",
    7. "match": "^profit_\d+$",
    8. "unmatch": "*_text",
    9. "mapping": {
    10. "type": "long"
    11. }
    12. }
    13. }
    14. ]
    15. }
    16. }

    path_match、path_unmatch

    ```json { “mappings”: { “dynamic_templates”: [ {
    1. "full_name": {
    2. "path_match": "name.*",
    3. "path_unmatch": "*.middle",
    4. "mapping": {
    5. "type": "text",
    6. "copy_to": "full_name"
    7. }
    8. }
    } ] } }
  1. <a name="ZuVLB"></a>
  2. #### {name}、{dynamic_type}
  3. ```json
  4. {
  5. "mappings": {
  6. "dynamic_templates": [
  7. {
  8. "named_analyzers": {
  9. "match_mapping_type": "string",
  10. "match": "*",
  11. "mapping": {
  12. "type": "text",
  13. "analyzer": "{name}"
  14. }
  15. }
  16. },
  17. {
  18. "no_doc_values": {
  19. "match_mapping_type":"*",
  20. "mapping": {
  21. "type": "{dynamic_type}",
  22. "doc_values": false
  23. }
  24. }
  25. }
  26. ]
  27. }
  28. }

直接指定类型

  1. {
  2. "mappings": {
  3. "dynamic_templates": [
  4. {
  5. "strings_as_keywords": {
  6. "match_mapping_type": "string",
  7. "mapping": {
  8. "type": "keyword"
  9. }
  10. }
  11. }
  12. ]
  13. }
  14. }

5.索引模板

概念

索引模板的作用是提前创建好模板,后续索引的创建会根据匹配规则,自动生成该索引的配置

注意点

  1. 模板只在索引创建时起作用,修改模板不会影响已创建的索引。
  2. 可以设置多个索引模板,这些设置会被 merge 在一起。
  3. 可以设置 order 的数值,控制 merge 的过程。

当一个索引被创建时,如果符合多个模板,那么模板之间merge符合以下规则:

  1. order 值高的模板会覆盖 order 值低的模板。
  2. 相同字段会覆盖
  3. 不同字段会叠加

    索引模板格式

    ```json

{ “order”: 0, // 模板优先级 “template”: “sample_info*”, // 模板匹配的名称方式,以sample_info开头的名称都会被匹配 “settings”: {…}, // 索引设置 “mappings”: {…}, // 索引中各字段的映射定义 “aliases”: {…} // 索引的别名 }

  1. <a name="bybST"></a>
  2. #### template
  3. 模板名称的匹配方式,如 "template": "tete*" 所表示的含义是,当新建索引时,所有以 tete 开头的索引都会自动匹配到该索引模板,利用该模板进行相应的设置和字段添加等。
  4. <a name="LQYO3"></a>
  5. #### settings
  6. 索引模板中的 setting 部分一般定义的是索引的主分片、拷贝分片、刷新时间、自定义分析器等。常见的 setting 部分结构如下
  7. ```json
  8. "settings": {
  9. "index": {
  10. "analysis": {...}, // 自定义的分词器
  11. "number_of_shards": "32", // 主分片的个数
  12. "number_of_replicas": "1", // 备分片个数
  13. "refresh_interval": "5s" // 刷新时间
  14. }
  15. }

通过刷新时间可以看到,es中的数据落盘要匹配对应的刷新时间,这也是为什么es时近实时查询的原因
自定义分词器格式如下

  1. "settings": {
  2. "index": {
  3. "analysis": {
  4. "char_filter": { ... }, // 用户自定义字符过滤器
  5. "tokenizer": { ... }, // 用户自定义分词器
  6. "filter": { ... }, // 用户自定义标记过滤器
  7. "analyzer": { ... } // 用户自定义分析器
  8. },
  9. ...
  10. }
  11. }

具体参考:链接