1. 借鉴
极客时间 阮一鸣老师的Elasticsearch核心技术与实战
官方文档 creating_an_index
官方文档 mapping
官方文档 multi-fields
2. 开始
创建索引
在前面,我们知道了什么是es中的索引,接下来我们就来创建一个索引
方式1:通过索引一篇文档创建一个新索引
- 比如:一开始并没有酒店的索引,我想创建一个有关酒店的索引
PUT /hotel/_doc/1
{
"name": "测试酒店1",
"grade": 2.5,
"address": "北京市"
}
- 上面这个语句创建了id为1包含三个属性(name,grade,address)的酒店
突然我想加为这篇文档添加/删除/修改一个属性怎么办呢?好的,多问几个为什么,我们会在后续讲,我会把连接粘贴到这里 // TODO
- 太草率了吧,这样就创建了索引了么。是的-_-,那如果我要将文档2添加到hotel索引中该如何呢?
PUT /hotel/_doc/2
{
"name": "测试酒店2",
"grade": 3,
"address": "上海市"
}
- 可以看到,当你索引
第一篇新文档
时会自动创建索引(es会自动根据数据格式识别它的类型),我们可以查看一下这篇索引的结构 - 在kibana中键入以下语句,查看索引的结构
GET /hotel/_mapping
- 可以看到kibana控制台返回以下信息
{
"hotel" : {
"mappings" : {
"properties" : {
"address" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"grade" : {
"type" : "float"
},
"name" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
-
额,那这些都是些什么呢,第二语言是英语的看官能猜出来个大概,我们标记一下
- 可能有疑问的就是fields属性了,我在文章开头贴上了官方文档[multi-fields],这里再简单解释一下,什么叫”为不同的目的以不同的方式索引相同的字段“。最常用的同一个字段指定不同的分词器,比如说都是name字段,我使用两个分词器,一个是简体中文分词器,另外一个是拼音分词器,那么我们就可以使用fields定义两个分词器,一个是用于简体中文分词器,另外一个是拼音分词器,类似于下面这种,(如果运行下面这个语句报错,先下载ik和pinyin分词器 // TODO es插件下载),搜索的时候指定同一个字段的不同属性进行查询,比如我们有以下索引:
PUT /hotel_multi_fields
{
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "ik_smart",
"fields": {
"pinyin": {
"type": "text",
"analyzer": "pinyin"
}
}
}
}
}
}
- 那我们根据两个属性来进行查询
# 添加文档
# googledjd
PUT /hotel_multi_fields/_doc/1
{
"name": "google大酒店"
}
PUT /hotel_multi_fields/_doc/2
{
"name": "微软大酒店"
}
# 查询
# 使用name属性查询
GET /hotel_multi_fields/_search
{
"query": {
"term": {
"name": {
"value": "google"
}
}
}
}
# 使用name.pinyin属性来查询
GET /hotel_multi_fields/_search
{
"query": {
"term": {
"name.pinyin": {
"value": "wrdjd"
}
}
}
}
全字段的解析会在后面的章节有详解。额,好像走远了。。。
- 当然这种方式有一定限制,但是读到这里的看官一般不会遇到,我会在后续章节详细叙述一些属性对于此种方式的限制。(// TODO)
方式2:通过指定mappings来创建一个新索引
- 方式1是基于es的动态索引类型识别,但是这种动态识别,有时候识别的并不是我们想要的结果。那我们就来举个栗子:我要索引hotel,name为文本类型,gradle为double类型,adress为文本类型,distance为double类型;但是我在创建索引文档的时候不小心类型输入错误了,输入了字符串类型,我们基于以上做测试。
# 先删除已有索引
DELETE /hotel
# 创建hotel索引,并索引id为1的文档[这种就是方式1,一气呵成对吧]
PUT /hotel/_doc/1
{
"name": "测试酒店1",
"grade": 2.5,
"address": "北京市",
"distance": "25.0"
}
# 我们来看看它的索引结构
GET /hotel/_mapping
- 返回结果如下
{
"hotel" : {
"mappings" : {
"properties" : {
"address" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"distance" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"grade" : {
"type" : "float"
},
"name" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
- 我们不小心设置错了类型它就将错就错,得到了我们不想要的结果,当然责任主要在我们。那接下来,我们精确的设置一下类型呢?
# 先删除已有索引
DELETE /hotel
# 指定索引结构
PUT /hotel
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"grade": {
"type": "double"
},
"address": {
"type": "text"
},
"distance": {
"type": "double"
}
}
}
}
# 再次插入相同的数据
PUT /hotel/_doc/1
{
"name": "测试酒店1",
"grade": 2.5,
"address": "北京市",
"distance": "25.0"
}
# 看看它的索引结构
GET /hotel/_mapping
{
"hotel" : {
"mappings" : {
"properties" : {
"address" : {
"type" : "text"
},
"distance" : {
"type" : "double"
},
"grade" : {
"type" : "double"
},
"name" : {
"type" : "text"
}
}
}
}
}
# 看下数据
GET /hotel/_search
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"name" : "测试酒店1",
"grade" : 2.5,
"address" : "北京市",
"distance" : "25.0"
}
}
]
}
}
- 可以看到,数据虽然是文本类型,但是索引本身并没有更改,如果我们这时新增一篇文档,把distance设置为非数字的文本呢?
PUT /hotel/_doc/2
{
"name": "测试酒店1",
"grade": 2.5,
"address": "北京市",
"distance": "啊哈哈"
}
- 结果会报错
{
"error": {
"root_cause": [
{
"type": "mapper_parsing_exception",
"reason": "failed to parse field [distance] of type [double] in document with id '2'. Preview of field's value: '啊哈哈'"
}
],
"type": "mapper_parsing_exception",
"reason": "failed to parse field [distance] of type [double] in document with id '2'. Preview of field's value: '啊哈哈'",
"caused_by": {
"type": "number_format_exception",
"reason": "For input string: \"啊哈哈\""
}
},
"status": 400
}
- 由此我们会猜测它内部会调用Double.valueOf()方法,如果报错就抛出来,如果是文本数字 的话,还能使用。
更多的时候,我们需要确定索引的结构,而非让es自动匹配,同时以便在我们设置错误的类型时es也能给我提示。
- 所以创建用此方式创建索引的最简模板就是
PUT /索引的名字
{
"mappings": {
"properties": {
"xxx1属性": {
"type": "该属性的类型"
},
"xxx2属性": {
"type": "该属性的类型"
}
}
}
}
比如我们创建一个person的索引
PUT /person
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"age": {
"type": "integer"
},
"sex": {
"type": "boolean"
}
}
}
}
除了以上简单的属性,我们还可参看官方文档有关mapping的更多属性(连接参看借鉴部分 [官方文档 mapping])