一、Mapping概述
为了能够把日期字段处理成日期,把数字字段处理成数字,把字符串字段处理成Full-text或者精确的字符串值。
ES需要知道每个字段里面都包含了什么类型。这些类型和字段的信息存储(包含)在映射(mapping)中。
索引中每个文档都有一个类型(type)。每个类型都拥有自己的映射(mapping)或者模式定义(schema definition)。
一个映射定义了字段类型,每个字段的数据类型,以及字段被Elasticsearch处理的方式。映射还用于设置关联到类型上的元数据。
例如,可以使用映射来定义:
- 字符串字段是否作为全文本搜索字段
- 哪些字段包含数字,日期或地理信息
- 文档中所有字段的值是否应该被索引到_all字段
- 日期值的格式
- 自定义规则来控制动态添加的字段的映射
映射类型与type:即一个索引中有多个type,从逻辑上对文档进行划分、
每个索引有一个或多个映射类型,类型是对Document划分的逻辑组,索引中每个文档都有一个类型(type),每个类型拥有自己的映射或者模式定义(schema definition) 。每个映射类型包括:
- 关联到类型上的元数据,比如:_index, _type, _id, and _source
- 字段或属性的定义,比如:字段类型,每个字段的数据类型,以及字段被ES处理的方式。
数据类型大致分类:
- 简单类型,比如:string, date, long, double, boolean 或 ip等
- 嵌套对象
- 特殊类型,比如:geo_point, geo_shape, 或completion等
二、SAMPLE
即使没有在es中显示指定映射,ES可以自动猜测字段类型,进行自动映射。如果不符要求,运行期再手动修改也一样。
{ "mappings": { "user": { "_all": { "enabled": false }, "properties": { "title": { "type": "string" }, "name": { "type": "string" }, "age": { "type": "integer" } } }, "blogpost": { "properties": { "title": { "type": "string" }, "body": { "type": "string" }, "user_id": { "type": "string", "index": "not_analyzed" }, "created": { "type": "date", "format": "strict_date_optional_time||epoch_millis" } } } }
以上是官网提供的SAMPLE,提供了2个type,user和blogpost
如何查看某个具体type的映射->
curl -XGET ‘http://localhost:9200/mytest/_mapping/product?pretty‘
我们将映射的最高一层称为根对象,此处的根对象有properties和_all设置
通常根对象可能包含以下内容:
- 一个 properties 节点,列出了文档中可能包含的每个字段的映射
- 多个元数据字段,每一个都以下划线开头,例如 _type, _id 和 _source
- 设置项,控制如何动态处理新的字段,如analyzer, dynamic_date_formats 和dynamic_templates
- 其他设置,可以同时应用在根对象和其他 object 类型的字段上,如enabled, dynamic 等
properties属性:
1. type: 字段的数据类型,例如 string 和 date,形如:
{ "字段1": { "type": "integer" } }
2. index: index参数控制字符串以何种方式被索引,它包含以下三个值当中的一个:
(1)analyzed: 分词 索引。换言之,以全文形式索引此字段。
(2)not_analyzed: 索引 不分词,使之可以被搜索,但是索引内容和指定值一样。不分析此字段。
(3)no:不索引这个字段。这个字段不能为搜索到。
其中string类型字段默认值是analyzed。如果我们想映射字段为确切值,需要设置它为not_analyzed:
"字段1": { "type": "string", "index": "not_analyzed" }
注意:除了String外的其他简单类型(long、double、date等等)也接受index参数,但相应的值只能是no和not_analyzed,它们的值不能被分析
3. analyzer: 确定在索引和或搜索时全文字段使用的 分析器,形如:
对于analyzed类型的字符串字段,使用analyzer参数来指定哪一种分析器将在搜索和索引的时候
使用。默认的,ES使用standard分析器,但是你可以通过指定一个内建的分析器来更改它,例如
whitespace、simple或english。
{ "字段1": { "type": "string", "analyzer": "english" } }
4. fielddata: 设置对字段数据装载内存的处理方式,对同一个索引下相同的名字的设置必须是一样的
(1) fielddata.format: 设置是否装载字段数据到内存,只有装载到内存的数据才能执行排序,聚合等等操作
"properties": { "text": { "type": "string", "fielddata": { "format": "disabled" } } }
(2) fielddata.loading: 装载方式,支持 lazy, eager, eager_global_ordinals三种
(3) fielddata.filter: 装载时对数据进行过滤,支持频率,支持正则表达式等等
"properties": { "tag": { "type": "string", "fielddata": { "filter": { "frequency": { "min": 0.001, "max": 0.1, "min_segment_size": 500 } } } } }
三、常见字段类型简介
以下内容主要是介绍在设计mapping映射关系时可以选择的字段类型以及相关参数。
字段数据类型大致上可以分为以下几类,几乎是够用了。。
1:核心数据类型,包括:
- String类型:string
- Numeric类型:long, integer, short, byte, double, float
- Date类型:date
- Boolean类型:boolean
- Binary类型:binary
2:复杂数据类型,包括:
- Array类型:Array,ES并没有专用的Array,直接使用[]来表示数组
- Object类型:单一的 JSON 对象
- Nested类型:嵌套的多个 JSON 对象数组
3:地理数据类型,包括:
- Geo-point类型:geo_point的纬度/经度点
- Geo-Shape类型:复杂的地理形状,比如多边形
4:特殊数据类型,包括:
- IPv4类型:IPv4地址
- Completion类型:提供自动完成建议
- Token count类型:计算字符串中token的数量
- mapper-murmur3类型:murmur3用来计算索引时的hash值,并把它们存储在索引里面
- Attachment类型:映射附件的格式,比如:Ms Office,ePub等
下面逐个类型进行详细说明
(1). Binary类型
这个类型可以看做是可以以Base64编码的字符串,这个类型缺省是只存储,但是不可查询。接受的参数如下:
1:doc_values:是否被存储在磁盘,以参加后续的排序、聚合等,缺省true
2:store:是否存储到_source,并能从_source检索,缺省false
关于store的说明:如果设置为false,当_source=false时,无法获取,演示如下:
//1. 创建索引mytest,或者先删除,再创建 //2. 定义mytest/t2的mapping映射 curl -XPUT ‘http://localhost:9200/mytest/_mapping/t2?pretty‘ -d ‘ { "_source": { "enabled": false }, "properties": { "content": { "type": "string", "store": "true" }, "name": { "type": "string", "store": "false" } } } ‘ //3. 插入数据 curl -XPUT ‘http://localhost:9200/mytest/t2/1‘ -d ‘ { "content": "hello", "type": "world" } ‘ //4. 通过fields查询,返回结果只有content curl -XGET ‘http://localhost:9200/mytest/t2/1?pretty&fields=content,type‘
(2) Boolean类型
认为是false的值有很多,比如:false, "false", "off", "no", "0", "" (empty string), 0, 0.0
接受的参数:
1:doc_values 、 store
2:boost:权重,缺省1.0
3:index:这个字段是否可以被查询,接受not_analyzed (缺省) 和no
4:null_value:设置为null时候的值,缺省是null
(3) 日期类型
符合缺省或者指定日期格式的值会被当作日期类型,缺省是
"strict_date_optional_time||epoch_millis" ,支持很多格式,通常我们会明确指定自己的格式。
接受的参数:
1: boost、 doc_values、 store、 index、 null_value
2:format:日期格式,比如:yyyy-MM-dd HH:mm:ss
3:ignore_malformed:数据不满足格式的时候,true表示忽略,false抛出一个异常,并拒绝整个文档
4:include_in_all:是否把字段值包含在_all中,如果index设置为no或者父对象的字段的这个参数设置的是
false,缺省是false;其它情况缺省是true
5:precision_step:设置索引的term的数量,以加快范围查询,缺省16
(4) String类型
熟悉的字符串,支持以下参数:
1: boost、doc_values、include_in_all、store、index、null_value
2:analyzer:用来分析字符串字段值的分析器,包括索引和查询(如果没有设置查询索引的分析器)两个阶段,缺省使用index的分析器或者standard分析器
3:fielddata:字段值是否能放置到内存中,用于排序、聚合等,接受disabled或paged_bytes(缺省)
4:fields:Multi-fields允许这同一个字符串值,以多种方式索引,以满足不同的目的。
5:ignore_above:不要索引或分析任何比这个值长的字符串。缺省为0(disabled)。
6:index_options:为了查询或高亮显示,应该在index中存储什么信息。缺省对analyzed字段是positions,对not_analyzed是docs
7:norms:计算查询得分时,是否要考虑字段长度,缺省对analyzed字段是
{ “enabled”: true,“loading”: “lazy” } ,对not_analyzed是{ "enabled": false },后面会详述查询得分
8:position_increment_gap:距离查询时,最大允许查询的距离,默认是100
9:search_analyzer:查询时使用的分析器,缺省是analyzer设置的分析器
10:search_quote_analyzer:搜索中碰到短语使用的分析器,缺省是search_analyzer设置的分析器
11:similarity:设置计算相似度的算法,缺省TF/IDF
12:term_vector:是否为一个analyzed字段保存term词计数的值,缺省是no
(5) Token Count类型
这个类型实质上是integer,功能是分析字符串,然后记录字符串分析后的token数量,例如:
PUT my_index { "mappings": { "my_type": { "properties": { "name": { "type": "string", "fields": { "length": { "type": "token_count", "analyzer": "standard" } } } } } } }
接受的参数:
1:analyzer、boost、doc_values、include_in_all、store、index、null_value
2:precision_step:控制索引的额外term的数量,以加快范围查询,缺省32
(6) 多值字段-数组
比如可以索引一个标签数组来代替单一字符串:
{ "tag": [ "search", "nosql" ]}
对于数组不需要特殊的映射。任何一个字段可以包含零个、一个或多个值,同样对于全文字段将被分析并产生多个词。
这意味着数组中所有值必须为同一类型,不能把日期和字符串混合。如果创建一个新字段,这个字段索引了一个数组,ES将使用第一个值的类型来确定这个新字段的类型。
(7) 空字段
Lucene没法存放null值,所以一个null值的字段被认为是空字段。这四个字段将被识别为空字段而不被索引:
"empty_string": "",
"null_value": null,
"empty_array": [],
"array_with_null_value": [ null ]
(8) 内部对象
ES会动态的检测新对象的字段,并映射它们为object类型,然后将每个字段加到properties字段
下,例如:
curl -XPUT http://localhost:9200/mytest2?pretty -d ‘ { "mappings": { "blog": { "properties": { "createTime": { "type": "date", "format": "yyyy-MM-dd HH:mm:ss" }, "name": { "type": "object", "properties": { "full": { "type": "string" }, "first": { "type": "string" }, "last": { "type": "string" } } }, "title": { "type": "string" } } } } } ‘
说明:内部对象是怎样被索引的
Lucene并不了解内部对象。 一个 Lucene 文件包含一个键-值对应的扁平表单。 为了让 ES可以有效的索引内部对象,将文件转换为以下格式:注意都是数组...
{ "tweet": [ elasticsearch, flexible, very ], "user.id": [ @johnsmith ], "user.gender": [ male ], "user.age": [ 26 ], "user.name.full": [ john, smith ], "user.name.first": [ john ], "user.name.last": [ smith ] }
内部栏位可被归类至name,例如"first"。 为了区别两个拥有相同名字的栏位,我们可以使用完整路径,例如"user.name.first" 或甚至类型名称加上路径:"tweet.user.name.first"。
注意: 在以上扁平化文件中,并没有栏位叫作user也没有栏位叫作user.name。Lucene 只索引阶层或简单的值,而不会索引复杂的文档结构
(9) 内部对象数组
一个包含内部对象的数组如何索引,假如有个数组如下所示:
{ "followers": [ { "age": 35, "name": "Mary White" }, { "age": 26, "name": "Alex Jones" }, { "age": 19, "name": "Lisa Smith" } ] }
此文件会如我们以上所说的被扁平化,但其结果会像如此:
{ "followers.age": [ 19, 26, 35 ], "followers.name": [ alex, jones, lisa, smith, mary, white ] }
但是这种合并数组的方式会带来问题,{age: 35}与{name: Mary White}之间的关联会消失,因每个多值的栏位会变成一个值集合,而非有序的阵列。 这让我们可以知道:是否有26岁的追随者?
但我们无法取得准确的资料如:
是否有26岁的追随者且名字叫Alex Jones?
可以通过关联内部对象(Nested Object)解决此类问题,称之为嵌套对象。
四、元数据类型
ES支持的元数据类型分成如下几类:
1:Identity meta-fields:_index、_type、_id、_uid(_type和_id的组合值)
2:Document source meta-fields:_source、_size(_source的bytes)
3:Indexing meta-fields:_all、_field_names(文档中值非null的字段名称)、_timestamp(文档关联
的时间戳,手动设置或自动生成)、_ttl(文档的存活时间)
4:Routing meta-fields:_parent(设置文档的parent-child关系)、_routing
5:Other meta-fielde:_meta(具体应用使用的特殊的元数据)
(1) _source
默认情况下,ES用JSON字符串来表示源数据,即没有经过分词等等处理的数据,并保存在_source字段中。
可以在mapping中定义_source的处理方式: false表示禁用
{ "mappings": { "my_type": { "_source": { "enabled": false } } } }
(2) _all
泛指所有的字段,也可以是所有的类型,索引等,泛指所有的
_all指字段时可以看做是一个特殊的字段,所有被include_in_all选项控制的字段都包含在内。但是要注意_all会被当作用一个字符串对象。哪怕字段中原本的mapping类型是date,在_all中依然会当做一个字符串,而date和string的索引方式是不同的。
同样可以在mapping中禁用:
{ "my_type": { "_all": { "enabled": false }
可以在type,字段多个层次去控制是否需要被包含在_all中
PUT /my_index/my_type/_mapping { "my_type": { "include_in_all": false, "properties": { "title": { "type": "string", "include_in_all": true }, ... } } }
_all字段仅仅是一个经过分词的string字段。它使用默认的分析器来分析它的值,而不管这值本来所在字段指定的分析器。
PUT /my_index/my_type/_mapping { "my_type": { "_all": { "analyzer": "whitespace" } } }
(3) _id
即唯一id,注意一个唯一的文档由index type id唯一决定,其中type仅仅只是一个逻辑上的概念
最好是所有type中不同文档的字段名都不相同
五、动态映射
ES支持自动的动态映射,我们很少去修改ES默认的设置,仅仅是某些特殊需求下才会修改动态映射的默认行为
如何禁止自动映射?
生产环境中,可能出于更加规范的目的强制要求必须手动建立映射去禁止自动映射,可以在配置文件中配置。也可以使用api
PUT /_settings { "index.mapper.dynamic":false}
_default_: 为没有 手动指定mapping的type指定默认mapping内容
PUT my_index { "mappings": { "_default_": { "_all": { "enabled": false } }, "user": {}, "blogpost": { "_all": { "enabled": true } } } }
_default的意思就表示是类型的默认映射关系,如果没有指定,就用default,比如上面的us.
动态映射,不重要,仅仅演示
缺省的,ES会为没有设置映射的字段进行动态映射,可以通过 dynamic 设置来控制动态映射,它接受下面几个选项:
(1)true:自动添加字段(默认)
(2)false:忽略字段
(3)strict:当遇到未知字段时抛出异常
dynamic 设置可以用在根对象或任何 object 对象上。你可以将 dynamic 默认设置为 strict,
而在特定内部对象上启用它,例如:
PUT /my_index { "mappings": { "my_type": { "dynamic": "strict", "properties": { "title": { "type": "string" }, "name": { "type": "object", "dynamic": true } } } } }
六、总结
ES通过映射来控制索引的行为,并逻辑上对文档进行划分,拥有相同映射的称为一个type。
通常,建议在以下情况中使用自定义字段映射:
(1) 区分full-text和exact value,即是否需要分词
(2) 使用特定的分词器,比如中文需要使用ik
(3) 优化部分匹配字段
(4) 指定自定义的日期格式
...
当然ES也支持指定完映射之后修改,但是请注意,已经存在的字段不要随意修改,因为当前这个字段可能已经建立了索引分词等等。修改必然伴随着代价。
可以新增字段和设置其类型,对已有数据修改,可能导致错误并且不能正确的被搜索到。