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 底层存储的是整数类型,因为压缩整数比压缩浮点数更加节省存储空间。
创建示例
{
"mappings": {
"my_type": {
"properties": {
"number_of bytes": {
"type": "integer"
},
"time_in_seconds": {
"type": "float"
},
"price": {
"type": "scaled_float",
"scaling_factor": 100
}
}
}
}
}
日期类型
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,3]
- 嵌套数组:[1,[2,3]],等价于 [1,2,3]
- 对象数组:[{“city”:”amsterdam”,”country”:”nederland”},{“city”:”brussels”,”country”:”belgium”}]
动态添加数据时,数组的第一个值的类型决定整个数组的类型。混合数组类型是不支持的,比如:[1,”abc”]。数组可以包含 null 值,空数组 [ ] 会被当作 missing field 对待。
在文档中使用 array 类型不需要提前做任何配置,默认支持。
对象类型
对象类型很好理解,即JSON对象嵌套对象,需要注意的是,es中会对嵌套的JSON对象做扁平化处理
例如,数据存储参数
{
"name":"ftc",
"friend":[
"name":"zyl"
]
}
数据存储格式
{
"name":"ftc",
"friend.name":"zyl"
}
嵌套类型
嵌套类型是一种特殊的对象类型,上文已描述,es本身会对对象类型字段做扁平化处理,那么当存储的对象类型为对象数组时,会出现关联关系失效的情况
示例,存储数据参数
{
"name":"ftc",
"friends":[
{
"name":"zyl",
"age":2
},
{
"name":"skx",
"age":18
}
]
}
es做扁平化处理之后的数据结构
{
"name":"ftc",
"friends.name":["zyl","skx"],
"friend.age":[2,18]
}
可以看出,数据直接的关联性没有了
如果需要索引对象数组并避免上述问题的产生,应该使用 nested 对象类型而不是 object 类型,nested 对象类型可以保持数组中每个对象的独立性。Nested 类型将数组中每个对象作为独立隐藏文档来索引,这意味着每个嵌套对象都可以独立被搜索
为了防止过度定义嵌套字段的数量,每个索引可以定义的嵌套字段被限制在 50 个。
地理数据类型简介
Elasticsearch 的地理相关类型有地理坐标类型和地理图形类型。
一级分类 | 二级分类 | 具体类型 |
---|---|---|
地理类型 | 地理坐标类型 | geo_point |
地理图形类型 | geo_shape |
地理坐标(geo_point)类型
geo point 类型用于存储地理位置信息的经纬度,可用于以下几种场景:
- 查找一定范围内的地理位置。
- 通过地理位置或者相对中心点的距离来聚合文档。
- 把距离因素整合到文档的评分中。
-
存储格式
经纬度JSON格式:{“lat”:41.12,”lon”:-71.34}
- 经纬度字符串格式:”41.12,-71.34”
- 地理坐标hash值:”u1269qu5dcgp”
- 经纬度数组形式:[41.12,-71.34]
地理图形(geo_shape)类型
存储格式
geo_shape类型可以存储一块区域,比如矩形、三角形或者其他多边形。地理图形类型默认的存储格式为geoJson。geojson示例格式如下:{
"type":"Point",
"coordinates":[
100,
0
]
}
类型说明
| 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,分词器为标准分词器,命令如下:
{
"mappings": {
"my_type": {
"properties": {
"name": {
"type": "text",
"fields": {
"length": {
"type": "token_count",
"analyzer": "standard"
}
}
}
}
}
}
}
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来指定日期字符匹配模板
例如:PUT my_index
{
"mappings": {
"dynamic_date_formats": ["MM/dd/yyyy"]
}
}
数字检测
动态映射有时会将数字映射为字符串类型,可以通过开启数字检测来解决该问题
例如:PUT my_index
{
"mappings": {
"numeric_detection": true
}
}
动态映射模板
当然,也可以通过定义动态映射模板,来格式化动态映射的规则
动态映射模板包含以下关键字:
- match_mapping_type:用来匹配当前映射的字段类型。可以理解为,当字段类型为match_mapping_type时
- match、unmatch 、match_pattern:用来匹配当前映射字段的名称。可以理解为,当字段名称匹配match、match_pattern时,当字段名称不匹配unmatch时
- path_match、path_unmatch:用来匹配当前映射字段的路径,通常用来匹配嵌套字段。可以理解为,当字段路径为path_match时,当字段路径不匹配path_unmatch时
- {name}, {dynamic_type} :用来进行占位符匹配,具体怎么用,都可以,但是最终映射的值为name或动态类型
- 当然也可以直接强制指定类型和属性
动态映射模板示例
match_mapping_type
dynamic_templates是动态模板名称,什么字段都可以。integers与strings时动态模板项名称,什么字段都可以PUT my_index
{
"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
}
}
}
}
}
]
}
}
match、unmatch
PUT my_index
{
"mappings": {
"dynamic_templates": [
{
"longs_as_strings": {
"match": "long_*",
"unmatch": "*_text",
"mapping": {
"type": "long"
}
}
}
]
}
}
match_pattern
{
"mappings": {
"dynamic_templates": [
{
"longs_as_strings": {
"match_pattern": "regex",
"match": "^profit_\d+$",
"unmatch": "*_text",
"mapping": {
"type": "long"
}
}
}
]
}
}
path_match、path_unmatch
```json { “mappings”: { “dynamic_templates”: [ {
} ] } }"full_name": {
"path_match": "name.*",
"path_unmatch": "*.middle",
"mapping": {
"type": "text",
"copy_to": "full_name"
}
}
<a name="ZuVLB"></a>
#### {name}、{dynamic_type}
```json
{
"mappings": {
"dynamic_templates": [
{
"named_analyzers": {
"match_mapping_type": "string",
"match": "*",
"mapping": {
"type": "text",
"analyzer": "{name}"
}
}
},
{
"no_doc_values": {
"match_mapping_type":"*",
"mapping": {
"type": "{dynamic_type}",
"doc_values": false
}
}
}
]
}
}
直接指定类型
{
"mappings": {
"dynamic_templates": [
{
"strings_as_keywords": {
"match_mapping_type": "string",
"mapping": {
"type": "keyword"
}
}
}
]
}
}
5.索引模板
概念
索引模板的作用是提前创建好模板,后续索引的创建会根据匹配规则,自动生成该索引的配置
注意点
- 模板只在索引创建时起作用,修改模板不会影响已创建的索引。
- 可以设置多个索引模板,这些设置会被 merge 在一起。
- 可以设置 order 的数值,控制 merge 的过程。
当一个索引被创建时,如果符合多个模板,那么模板之间merge符合以下规则:
{ “order”: 0, // 模板优先级 “template”: “sample_info*”, // 模板匹配的名称方式,以sample_info开头的名称都会被匹配 “settings”: {…}, // 索引设置 “mappings”: {…}, // 索引中各字段的映射定义 “aliases”: {…} // 索引的别名 }
<a name="bybST"></a>
#### template
模板名称的匹配方式,如 "template": "tete*" 所表示的含义是,当新建索引时,所有以 tete 开头的索引都会自动匹配到该索引模板,利用该模板进行相应的设置和字段添加等。
<a name="LQYO3"></a>
#### settings
索引模板中的 setting 部分一般定义的是索引的主分片、拷贝分片、刷新时间、自定义分析器等。常见的 setting 部分结构如下
```json
"settings": {
"index": {
"analysis": {...}, // 自定义的分词器
"number_of_shards": "32", // 主分片的个数
"number_of_replicas": "1", // 备分片个数
"refresh_interval": "5s" // 刷新时间
}
}
通过刷新时间可以看到,es中的数据落盘要匹配对应的刷新时间,这也是为什么es时近实时查询的原因
自定义分词器格式如下
"settings": {
"index": {
"analysis": {
"char_filter": { ... }, // 用户自定义字符过滤器
"tokenizer": { ... }, // 用户自定义分词器
"filter": { ... }, // 用户自定义标记过滤器
"analyzer": { ... } // 用户自定义分析器
},
...
}
}
具体参考:链接