映射定义了文档和它所包含的属性被存储类型和索引方式等等
举例来说,我们用映射来定义以下信息:
_all
元数据中。类比于关系数据库,索引(index)相当于数据库,类型(type)相当于数据表,映射(Mapping)相当于数据表的表结构。ElasticSearch中的映射(Mapping)用来定义一个文档,可以定义所包含的字段以及字段的类型、分词器及属性等等。
在关系型数据库中,我们使用数据库必须优先创建数据库,创建并定义数据表结构,我们才可以逐行添加修改数据。而在Elasticsearch中我们不需要事先定义映射(Mapping),文档写入Elasticsearch时,Elasticsearch会根据文档字段自动识别类型创建映射,依据Elasticsearch是否自动创建映射,我们可以把映射分为动态映射和静态映射。
字段的数据类型在加入索引之前不需要事先定义,Elasticsearch会根据文档字段自动识别类型创建映射。不仅首次加入数据到索引时Elasticsearch会自动创建映射,新的字段首次加入文档顶级对象或者内部对象Elasticsearch也会自动创建映射。
新建索引tweet
PUT tweet
创建结果
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "tweet"
}
此时我们查看一下索引的映射
GET tweet/_mapping
映射如下
{
"tweet": {
"mappings": {}
}
}
此时,我们插入一条tweet信息
PUT tweet/tweet/1
{
"tweet": "However did I manage before Elasticsearch?",
"date": "2014-09-14",
"name": "Mary Jones",
"user_id": 1
}
插入结果如下:
{
"_index": "tweet",
"_type": "tweet",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1
}
此时我们查看一下索引的映射
GET tweet/_mapping
映射如下
{
"tweet": {
"mappings": {
"tweet": {
"properties": {
"date": {
"type": "date"
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"tweet": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"user_id": {
"type": "long"
}
}
}
}
}
}
从上面的映射结果我们可以看出来,tweet 和 name映射为text类型,发布日期date自动映射为date,而用户IDuser_id也自动识别为了long型。
此时,我们再为我们的tweet追加一条评论。
PUT tweet/tweet/1
{
"tweet": "However did I manage before Elasticsearch?",
"date": "2014-09-14",
"name": "Mary Jones",
"user_id": 1,
"comments": [
{
"content": "very good",
"date": "2014-09-14",
"user_id": 1
}
]
}
追加结果如下
{
"_index": "tweet",
"_type": "tweet",
"_id": "1",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1
}
此时我们再查看一下索引的映射
GET tweet/_mapping
映射如下
{
"tweet": {
"mappings": {
"tweet": {
"properties": {
"comments": {
"properties": {
"content": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"date": {
"type": "date"
},
"user_id": {
"type": "long"
}
}
},
"date": {
"type": "date"
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"tweet": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"user_id": {
"type": "long"
}
}
}
}
}
}
此时,我们新加的comments.content也自动映射为text类型,发布日期date自动映射为date,而用户IDuser_id也自动识别为了long型。
由此可见ElasticSearch的动态映射十分强大。
由于Elasticsearch动态映射的存在,我们在创建索引后直接将文档数据写入Elasticsearch而不需要优先创建映射。在实际项目中,如果在导入数据前不能确定包含哪些字段或者不方便确定字段类型,可以使用动态映射。当向Elasticsearch写入一个文档时,文档中包含当前映射没有的字段,Elasticsearch会通过动态映射来推断该字段类型修改映射。
ElasticSearch动态映射规则如下:
JSON数据 | 自动推测的类型 |
---|---|
null | 没有字段被添加 |
true或false | boolean型 |
小数 | float型 |
数字 | long型 |
日期 | date或text |
字符串 | text |
数组 | 由数组第一个非空值决定 |
JSON对象 | object类型 |
相对于Elasticsearch的自动映射,我们可以在ElasticSearch中事先为索引定义好映射,包含文档的各个字段及其类型等,并且考虑到动态映射的自动类型推测功能并不是100%正确的,这就需要静态映射机制。静态映射与关系数据库中创建表语句类型思想一致,我们需要事先指定字段类型。
相对于动态映射,静态映射可以添加更加详细字段类型、更精准的配置信息等。
一级分类 | 二级分类 | 具体类型 |
---|---|---|
核心类型 | 字符串类型 | string,text,keyword |
整数类型 | integer,long,short,byte | |
浮点类型 | double,float,half_float,scaled_float | |
逻辑类型 | boolean | |
日期类型 | date | |
范围类型 | range | |
二进制类型 | binary | |
复合类型 | 数组类型 | array |
对象类型 | object | |
嵌套类型 | nested | |
地理类型 | 地理坐标类型 | geo_point |
地理地图 | geo_shape | |
特殊类型 | IP类型 | ip |
范围类型 | completion | |
令牌计数类型 | token_count | |
附件类型 | attachment | |
抽取类型 | percolator |
类型 | 取值范围 |
---|---|
byte | -128~127 |
short | -32768~32767 |
integer | -231~231-1 |
long | -263~263-1 |
在满足需求的情况下,尽可能选择范围小的数据类型。比如,某个字段的取值最大值不会超过100,那么选择byte类型即可。迄今为止吉尼斯记录的人类的年龄的最大值为134岁,对于年龄字段,short足矣。字段的长度越短,索引和搜索的效率越高。
类型 | 取值范围 |
---|---|
doule | 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。
其中scaled_float,比如价格只需要精确到分,price为57.34的字段缩放因子为100,存起来就是5734
优先考虑使用带缩放因子的scaled_float浮点类型。
逻辑类型(布尔类型)可以接受true/false/”true”/”false”值
我们人类使用的计时系统是相当复杂的:秒是基本单位, 60秒为1分钟, 60分钟为1小时, 24小时是一天……如果计算机也使用相同的方式来计时, 那显然就要用多个变量来分别存放年月日时分秒, 不停的进行进位运算, 而且还要处理偶尔的闰年和闰秒以及协调不同的时区. 基于”追求简单”的设计理念, UNIX在内部采用了一种最简单的计时方式:
计算从UNIX诞生的UTC时间1970年1月1日0时0分0秒起, 流逝的秒数
UTC时间1970年1月1日0时0分0秒就是UNIX时间0, UTC时间1970年1月2日0时0分0秒就是UNIX时间86400.
这个计时系统被所有的UNIX和类UNIX系统继承了下来, 而且影响了许多非UNIX系统.
日期类型表示格式可以是以下几种:
ElasticSearch 内部会将日期数据转换为UTC,并存储为milliseconds-since-the-epoch的long型整数。
二进制字段是指用base64来表示索引中存储的二进制数据,可用来存储二进制形式的数据,例如图像。默认情况下,该类型的字段只存储不索引。二进制类型只支持index_name属性。
在ElasticSearch中,没有专门的数组(Array)数据类型,但是,在默认情况下,任意一个字段都可以包含0或多个值,这意味着每个字段默认都是数组类型,只不过,数组类型的各个元素值的数据类型必须相同。在ElasticSearch中,数组是开箱即用的(out of box),不需要进行任何配置,就可以直接使用。(我们自定义数据类型映射时,一般可以指定类型为text类型(自动映射一般会映射首元素类型),涉及到elasticsearch的检索原理)。
在同一个数组中,数组元素的数据类型是相同的,ElasticSearch不支持元素为多个数据类型: [ 10, "some string" ]
,常用的数组类型是:
[ "one", "two" ]
productId:[ 1, 2 ]
"user":[
{ "name": "Mary", "age": 12 },
{ "name": "John", "age": 10 }
]
ElasticSearch内部把对象数组展开为
{"user.name": ["Mary", "John"],"user.age": [12,10]}
JSON天生具有层级关系,文档会包含嵌套的对象。
自定义映射
PUT test
{
"mappings":{
"test": {
"properties": {
"name": {"type": "keyword"},
"age": {"type": "integer"},
"birthday": {"type": "date"},
"interests":{"type": "text"},
"friends":{"type": "object"}
}
}
}
}
映射定义成功
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "test"
}
查看映射
{
"test": {
"mappings": {
"test": {
"properties": {
"age": {
"type": "integer"
},
"birthday": {
"type": "date"
},
"friends": {
"type": "object"
},
"interests": {
"type": "text"
},
"name": {
"type": "keyword"
}
}
}
}
}
}
插入数据
PUT test/test/1
{
"name":"test",
"age":28,
"birthday":"1986-10-18",
"interests":["reading","sing"],
"friends":{
"name":"jack"
}
}
数据插入成功
{
"_index": "test",
"_type": "test",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
再次查看映射
{
"test": {
"mappings": {
"test": {
"properties": {
"age": {
"type": "integer"
},
"birthday": {
"type": "date"
},
"friends": {
"properties": {
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"interests": {
"type": "text"
},
"name": {
"type": "keyword"
}
}
}
}
}
}
我们新加的字段也自动映射了。