ElasticSearch
ElasticSearch
使用kibana操作ElasticSearch
参考网址:
1.简介
索引库(Indices): indices是index的复数,代表许多的索引, 可以想象成数据库一个database
类型(type): 类型是模拟mysql中的table概念,一个索引库下可以有不同类型的索引,比如商品索引,订单索引,其数据格式不同。不过这会导致 索引库混乱,因此未来版本中会移除这个概念 可以想象成数据库中的一个表,一个数据库中可以有很多表,一个索引库中就会有很多类型
**文档(document) **: 存入索引库原始的数据。比如每一条商品信息,就是一个文档 想成每一个表中一行一行的数据
字段(field):文档中的属性
映射配置(mappings):字段的数据类型、属性、是否索引、是否存储等特性
2.创建索引
2.1语法
POST 新增
GET 查询
DELETE 删除
PUT 修改
2.2查询
查看某一个特定索引库
GET 索引库名
查看所有索引库
GET *
删除索引
DELETE 索引库名
案例:查询索引名为text_demo 的全部数据
查询数据:GET /text_demo/_search
查询字段属性:GET text_demo
获取索引text_demo 中 id为2对应的文本信息:GET /text_demo/_doc/2
2.3创建映射字段
2.3.1添加一个索引
"mappings": {"properties":{ 字段 }}
PUT test_demo
{
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"update_date": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis||yyyy||yyyy-MM"
}
}
}
}
案例,添加不同 字段类型的
PUT text_demo
{
"mappings": {
"properties": {
"images": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"price": {
"type": "float"
},
"saleable": {
"type": "boolean"
},
"stock": {
"type": "long"
},
"update_date": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis||yyyy||yyyy-MM"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
2.3.2 修改,添加字段,添加nested
PUT /索引库名/_mapping/类型名称
{
"properties": {
"字段名": {
"type": "类型",
"index": true,
"store": true,
"analyzer": "分词器"
}
}
}
类型名称:就是前面将的type的概念,类似于数据库中的不同表 字段名:任意填写 ,可以指定许多属性,例如:
type:类型,可以是text、long、short、date、integer、object等
index:是否索引,默认为true
store:是否存储,默认为false (会自动生成一个_source备份)
analyzer:分词器,这里的ik_max_word即使用ik分词器
案例:索引 text_demo 已经添加 现在需要 添加字段:images,price,saleable,stock,title
和添加索引区别在于 "properties": {}. 不用 "mapping" 注意 PUT / text_demo /**_mapping **/
PUT / text_demo / _mapping /
{
"properties": {
"images": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"price": {
"type": "float"
},
"saleable": {
"type": "boolean"
},
"stock": {
"type": "long"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
tower索引添加一个子项yz_patrol_station 类型是nested ,包含2个字段 id和name
PUT /tower/_mapping
{
"properties": {
"yz_patrol_station": {
"type":"nested",
"properties":{
"id":{
"type": "keyword"
},
"name":{
"type": "keyword"
}
}
}
}
}
在索引 nested 对象下追加 字段
PUT test_demo/_mapping
{
"properties": {
"test_nested": {
"type": "nested",// 这个也要,否则无法执行
"properties": {
"id": {
"type": "keyword"
},
"name": {
"type": "keyword"
}
}
}
}
}
2.4 说明:
String类型,又分两种:
text:可分词,不可参与聚合 keyword:不可分词,数据会作为完整字段进行匹配,可以参与聚合
Numerical:数值类型,分两类 基本数据类型:long、interger、short、byte、double、float、half_float 浮点数的高精度类型:scaled_float 需要指定一个精度因子,比如10或100。elasticsearch会把真实值乘以这个因子后存储,取出时再还原。
Date:日期类型
elasticsearch可以对日期格式化为字符串存储,但是建议我们存储为毫秒值,存储为long,节省空间。
index index影响字段的索引情况。 true:字段会被索引,则可以用来进行搜索。默认值就是true false:字段不会被索引,不能用来搜索 index的默认值就是true,也就是说你不进行任何配置,所有字段都会被索引。 但是有些字段是我们不希望被索引的,比如商品的图片信息,就需要手动设置index为false。
2.5创建数据(添加和修改都是一样的操作,如果id存在 则修改,否则添加)
2.5.1 随机生成新id
POST /索引库名/类型名
{
"key":"value"
}
2.5.2 添加数据是,指定id
POST /索引库名/类型/id值
{
...
}
案例 添加一条id为 id_12121212的 线路信息,索引为 line
PUT /line/_doc/id_12121212
{
"dl_line_length":"1111",
"line_name":"测试003",
"line_length":"20",
"jk_line_length":18,
"start_location":18,
"voltage_grade":17,
"ydxj_job_opfile" : [
{
"Filepath" : "上报照片图片url1",
"id" : "1",
"type" : "2"
},
{
"Filepath" : "上报照片图片url2",
"id" : "2",
"type" : "2"
}
]
}
复制索引,针对 索引添加错误,比如需要删除字段,则会用到数据迁移,场景描述:将tower索引的数据备份到tower2中
POST _reindex
{
"source": {
"index": "tower"
},
"dest": {
"index": "tower2"
}
}
2.5.3 更新部分字段
更新好像不能单个字段去更新,会导致其他数据被覆盖
场景:索引 tower 下id为3的文档,更新字段 pylon_material 为 GT材质
POST /tower/_update/3
{
"doc" : {
"pylon_material" : "GT材质"
}
}
2.6 删除 DELETE /索引库名/类型名/id值 DELETE line/_doc/id_13 删除 索引库中 索引为line id为id_13 的 文档
3.查询
基本查询
这里的query代表一个查询对象,里面可以有不同的查询属性 查询类型: 例如:match_all, match,term , range 等等 案例:查询text_demo索引下title为 超米手机15 的 数据,但是需要注意的是,如果title 字段类型为 text那么相当于用模糊匹配的形式(分词,部分字段匹配上就 会查询到)
GET /text_demo/_search
{
"query":{
"match":{
"title":"超米手机15"
}
}
}
3.1.1 查询所有(match_all)
hits:搜索结果的文档对象数组,每个元素是一条搜索到的文档信息
index:索引库
type:文档类型
id:文档id
score:文档得分
source:文档的源数据
3.1.2. 匹配查询(match)
场景:查询text_demo索引下title为 超米手机15 的 数据 1.这种查询 针对。查询字段,必须是keyword,如果是text则会进行分词查询,每个分词之间用or连接,所以查询到结构 可能不是我们想要的
GET /text_demo/_search
{
"query":{
"match":{
"title":"超米手机15"
}
}
}
2.正对上面的 可以添加 "operator":"and",意思是每个分词之间用and关联
GET /text_demo/_search
{
"query":{
"match":{
"title":{
"query":"超米手机5",
"operator":"and"
}
}
}
}
3.match 查询支持 minimum_should_match 最小匹配参数
GET /text_demo/_search
{
"query":{
"match":{
"title":{
"query":"超米手机5",
"minimum_should_match": "100%"
}
}
}
}
3.1.3多字段查询(multi_match)
查询字段 title和 subTitle 中 是否包含 关键字“小米”(或者) ,同样也存在分词的情况,所以可以添加operator
GET /text_demo/_search
{
"query":{
"multi_match": {
"query": "小米",
"fields": [ "title", "subTitle" ],
//"operator":"and"
}
}
}
3.1.4 词条匹配(term) term 查询被用于精确值 匹配,这些精确值可能是数字、时间、布尔或者那些未分词的字符串(keyword) 目前没有体现和match用法的区别
GET /heima/_search
{
"query":{
"term":{
"price":2699.00
}
}
}
3.1.5 多词条精确匹配(terms) terms 查询和 term 查询一样,但它允许你指定多值进行匹配。如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件:
GET /heima/_search
{
"query":{
"terms":{
"price":[2699.00,2899.00,3899.00]
}
}
}
3.2.结果过滤 默认情况下,elasticsearch在搜索的结果中,会把文档中保存在_source的所有字段都返回。如果我们只想获取其中的部分字段,我们可以添加_source的过滤 3.2.1 直接指定该字段
GET /heima/_search
{
"_source": ["title","price"],//返回我们需要的
"query": {
"term": {
"price": 2699
}
}
}
3.2.2 指定includes和excludes 我们也可以通过: includes:来指定想要显示的字段 excludes:来指定不想要显示的字段 GET /heima/_search { "_source": { "includes":["title","price"] }, "query": { "term": { "price": 2699 } } }
与下面的结果将是一样的:
GET /heima/_search
{
"_source": {
"excludes": ["images"]
},
"query": {
"term": {
"price": 2699
}
}
}
3.3高级查询 3.3.1 布尔组合(bool)(多条件查询,相当于 and)
GET text_demo/_search
{
"query": {
"bool": {
"must": [
{"match": {
"title": "小米电视"
}}
],
"must_not": [
{
"match": {
"title": "电视"
}
}
]
}
}
}
}
}
案例:tower下嵌套 驿站yz_patrol_station 查询满足条件的 驿站 记录
GET /tower/_search
{
"query": {
"bool": {
"must": [{
"match": {
"line_id": "a"
}
},
{
"nested": {
"path": "yz_patrol_station",
"query": {
"bool": {
"must": [{
"match": {
"yz_patrol_station.del_flag": "0"
}
}]
}
},
"inner_hits": {}// 通过这个返回 满足条件的数据,如果不用这个,返回的结果 不满足的子项也返回
}
}
]
}
}
}
3.3.2范围查询(range) range 查询找出那些落在指定区间内的数字或者时间 range允许以下操作符 操作符 说明 gt : 大于 gte :大于等于 lt :小于 lte :小于等于 案例:查找价格在 3000到 9909 的数据
GET heima/goods/_search
{
"query": {
"range": {
"price": {
"gte": 3000,
"lte": 9909
}
}
}
}
3.3.3. 模糊查询(fuzzy) fuzzy 查询是 term 查询的模糊等价。它允许用户搜索词条与实际词条的拼写出现偏差,但是偏差的编辑距离不得超过2
GET /heima/_search
{
"query": {
"fuzzy": {
"title": "appla"
}
}
}
3.4.过滤(filter) 条件查询中进行过滤 所有的查询都会影响到文档的评分及排名。如果我们需要在查询结果中进行过滤,并且不希望过滤条件影响评分,那么就不要把过滤条件作为查询条件来用。而是使用filter方式:
GET text_demo/_search
{
"query": {
"bool": {
"must": [
{"match": {
"title": "超米手机2"
}}
],
"filter":{
"price": {
"stock": {
"gte": 200,
"lte": 300
}
}
}
}
}
}
无查询条件,直接过滤 如果一次查询只有过滤,没有查询条件,不希望进行评分,我们可以使用constant_score取代只有 filter 语句的 bool 查询。在性能上是完全相同的,但对于提高查询简洁性和清晰度有很大帮助。
无查询条件,直接过滤 如果一次查询只有过滤,没有查询条件,不希望进行评分,我们可以使用constant_score取代只有 filter 语句的 bool 查询。在性能上是完全相同的,但对于提高查询简洁性和清晰度有很大帮助。
GET /heima/_search
{
"query":{
"constant_score": {
"filter": {
"range":{"price":{"gt":2000.00,"lt":3000.00}}
}
}
}
3.5排序
3.5.1单字段排序(Sort) sort 可以让我们按照不同的字段进行排序,并且通过order指定排序的方式
GET /heima/_search
{
"query": {
"match": {
"title": "小米手机"
}
},
"sort": [
{
"price": {
"order": "desc"
}
}
]
}
tips:sort是对查询后做的,不属于查询和过滤的条件,因此在query查询对象外面
tips:sort是对查询后做的,不属于查询和过滤的条件,因此在query查询对象外面
案例
案例1.判断不为空 exists
查询 danger_range_str 不为空的 相当于 select * from ydxj_danger_record where danger_range_str is not null
GET ydxj_danger_record/_search
{
"query": {
"bool": {
"must": {//must_not
"exists": {
"field": "danger_range_str"
}
}
}
}
}
/**
* exists过滤器
*/
@ResponseBody
@RequestMapping("/searchAccountBool")
public List<Account> searchAccountBool(){
BoolQueryBuilder accountBoolquery = QueryBuilders.boolQuery();
ExistsQueryBuilder sexQueryBuilder = QueryBuilders.existsQuery("sex");
accountBoolquery.must(sexQueryBuilder);
System.out.println(accountBoolquery);
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(accountBoolquery).build();
List<Account> accountList = elasticsearchTemplate.queryForList(searchQuery, Account.class);
System.out.println(accountList);
return accountList;
}
https://blog.csdn.net/weixin_40341116/article/details/81173016
案例2:分组后 过滤(分组:terms,过滤:filter )
场景:统计 缺陷隐患 分别总数 和闭环的数量
GET /ydxj_danger_record/_search
{
"size": "0",
"query": {
"bool": {
"must": [{
"match": {
"del_flag": "0"
}
},
{
"match": {
"line_id": "a"
}
}
]
}
},
"aggs": {
"group_field": {
"terms": {
"field": "danger_record_Type"//分组 第一层 通过 danger_record_Type 类型区分 缺陷和隐患总数分别是多少
},
"aggs": {
"my_name": {
"filter": {
"term": {// 可以用terms 对应下面 可以支持多个 条件 ["ybh","xx"]
"task_def_key": "ybh"// 分组统计好后,然后 过滤掉 未闭环的,统计出未闭环的数量
}
}
}
}
}
}
}
案例: 3.普通的分组(terms)
根据设置类型,分类统计 每个类型下数量
GET warn_equip/_search
{
"size": "0",
"query": {// 条件,线路a下,del_flag=0的数据
"bool": {
"must": [{
"match": {
"line_id": "a"
}
}, {
"match": {
"del_flag": "0"
}
}]
}
},
"aggs": {
"group_field": {
"terms": {
"field": "equiptype_name"// 分组字段
}
}
}
}

Java 聚合的实现(分组:terms)
public void addTermsAgg(SearchSourceBuilder sourceBuilder,String aggName, String fieldName) {
AggregationBuilder builder = AggregationBuilders.terms(aggName).field(fieldName);
sourceBuilder.aggregation(builder);
}
解析
public JSONObject getDataAgg(SearchResponse search, String aggName){
JSONObject dataMap=new JSONObject();
// 获取聚合结果
Aggregations aggregations = search.getAggregations();
Terms byAgeAggregation = aggregations.get(aggName);
for (Terms.Bucket buck:byAgeAggregation.getBuckets()){
dataMap.put(buck.getKeyAsString(),buck.getDocCount());
}
log.info("getDataAgg:解析"+dataMap.toString());
return dataMap;
}
案例4:嵌套后对 nested对象中的字段过滤统计(嵌套:nested)
场景: tower下 嵌套 yz_patrol_station 现在需要统计 还在用的驿站
GET /tower/_search
{
"size": 0,
"query": {
"bool": {
"must": [{
"match": {
"line_id": "b"
}
}
]
}
},
"aggs": {
"genres": {
"nested" : {
"path" : "yz_patrol_station"
},
"aggs":{
"filter_type":{
"filter": {
"term": {
"yz_patrol_station.del_flag" : "0"
}
}
}
}
}
}
}
案例5.:去重分组统计(去重:cardinality)
案例场景:统计 巡视记录中 在线人员,(weipai_flag=0,machine_flag=0)
排序、多条件、range
GET ydxj_job_op/_search
{
"size": "0",
"sort": [{
"optime": {
"order": "desc"
}
}],
"query": {
"bool": {
"must": [{
"match": {
"line_id": "a"
}
},
{
"match": {
"weipai_flag": "0"
}
},
{
"match": {
"machine_flag": "0"
}
},
{
"range": {
"optime": {
"gte": "2020-08-01 00:00:00"
}
}
}
]
}
},
"aggs" : {
"distinct_user" : {
"cardinality" : {// 去重 统计数量
"field" : "user_id"
}
}
}
}
案例6:nested 对象 分组 统计数量(nested +group + filter)
场景:bas_c_device 是 tower索引的 nested对象 根据 统计GT下 bas_c_device对象 label进行分组 统计 每项的数量。需要满足bas_c_device对象下 type=device_major_type 并且 del_flag=0
注: nested 里面的字段 不管任何地方取 都是 需要 用 “对象.属性” 例:bas_c_device.type
GET tower/_search
{
"size": "0",
"query": {
"bool": {
"must": [{
"match": {
"line_id": "b"
}
}]
}
},
"aggs": {
"nested_name": {
"nested": {
"path": "bas_c_device"
},
"aggs": {
"nested_group_field": {
"terms": {
"field": "bas_c_device.label"
},
"aggs": {
"nested_filter": {
"filter": {
"bool": {
"must": [{
"term": {
"nested.del_flag": "0"
}
}, {
"term": {
"bas_c_device.type": "device_major_type"
}
}]
}
}
}
}
}
}
}
}
}
查询结果
"aggregations" : {
"nested_name" : {
"doc_count" : 10,
"nested_group_field" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "导线弧垂监测",
"doc_count" : 4,
"nested_filter" : {
"doc_count" : 0
}
},
{
"key" : "GT倾斜监测",
"doc_count" : 2,
"nested_filter" : {
"doc_count" : 0
}
},
{
"key" : "覆冰监测",
"doc_count" : 2,
"nested_filter" : {
"doc_count" : 0
}
},
{
"key" : "风偏监测",
"doc_count" : 2,
"nested_filter" : {
"doc_count" : 0
}
}
]
}
}
}
Java 聚合的实现(聚合:nested 、分组:terms)
// 构成说明,外桶是定义nested,然后对nested 中的字段进行分组,然后 对分组的字段进行 过滤
public void addTermsAggByNestedGroupFilter(SearchSourceBuilder sourceBuilder,String nestedName, String nestedPath,
String groupName,String groupFieldName,
String fileterName,Map<String, Object> fileterMap) {
NestedAggregationBuilder nested = AggregationBuilders.nested(nestedName, nestedPath);// 指定 nested
AggregationBuilder builder = AggregationBuilders.terms(groupName).field(groupFieldName);
nested.subAggregation(builder);// 指定分组字段
BoolQueryBuilder addCustomer=QueryBuilders.boolQuery();
if (!CollectionUtils.isEmpty(fileterMap)) {
for (String field : fileterMap.keySet()) {
addCustomer.must(QueryBuilders.termQuery(field, fileterMap.get(field)));
}
}
//过滤条件是对分组字段进行的所以builder.subAggregation(xxxx) ,而不是 nested.subAggregation(xxxxxx)
builder.subAggregation(AggregationBuilders.filter(fileterName,addCustomer));
sourceBuilder.aggregation(nested);
}
解析
//[{value:40, name:'防山火设备'}, {value:30, name:'防钓鱼设备'},{value:30, name:'微拍设备'}, {value:60, name:'视频设备'}]
public JSONArray getDataAggByNestedGroupFilter(SearchResponse search, String nestedName,String groupName,String groupFieldName){
JSONArray dataArr=new JSONArray();
// 获取聚合结果
ParsedNested parsedNested = (ParsedNested) search.getAggregations().get(nestedName);// 1.外桶 是 Nested,所以查询数据强转为 ParsedNested
Aggregations aggregations = parsedNested.getAggregations();//2.然后由聚合 中取数据
Terms byAgeAggregation = aggregations.get(groupName);
for (Terms.Bucket buck:byAgeAggregation.getBuckets()){
Filter parsedNested2 = (Filter)buck.getAggregations().get(groupFieldName);//3. 由聚合中 的filter 中取数据
JSONObject dataMap=new JSONObject();
dataMap.put("value",parsedNested2.getDocCount());
dataMap.put("name",buck.getKeyAsString());
dataArr.add(dataMap);
}
log.info("getDataAgg:解析"+dataArr.toString());
return dataArr;
}
案例7:统计某个GT下 设备主人的数量(nested 下对象 去重统计 )
索引 tower 有个nested 对象ydxj_tower_owner(设备主人)
GET tower/_search
{
"size": 0,
"query": {
"bool": {
"must": [{
"term": {
"line_id": {
"value": "6834ADC85E0E484DA99E986803A6D2D1",
"boost": 1.0
}
}
}],
"adjust_pure_negative": true,
"boost": 1.0
}
},
"aggregations": {
"owner_nested": {
"nested": {
"path": "ydxj_tower_owner"
},
"aggregations": {
"distinct_user": {
"cardinality": {
"field": "ydxj_tower_owner.user_id"
}
}
}
}
}
}
java 实现聚合
/**
* 统计嵌套(nested)去重统计数量
* @param sourceBuilder // ES查询 语句
* @param nestedName : 外桶 别名,
* @param nestedPath : nested 对象
* @param distinctName: 去重
* @param distinctFieldName: 去重 字段名称
* {"size":"0","query":{"bool":{"must":[{"match":{"line_id":"a"}}]}},
* "aggs":{"nested_name":{"nested":{"path":"ydxj_tower_owner"},"aggs":{"distinct_user":{"cardinality":{"field":"user_id"}}}}}}
*/
public void addTermsAggByNestedDistinct(SearchSourceBuilder sourceBuilder,String nestedName, String nestedPath
,String distinctName,String distinctFieldName) {
NestedAggregationBuilder nested = AggregationBuilders.nested(nestedName, nestedPath);
nested.subAggregation(AggregationBuilders.cardinality(distinctName).field(distinctFieldName));
sourceBuilder.aggregation(nested);
}
对 nested 对象 去重分组
/**
* 解析 统计嵌套(nested)去重统计数量
* @param search
* @return
*/
public JSONObject getDataAggByNestedDistinct(SearchResponse search,String nestedName, String distinctName){
JSONObject dataMap=new JSONObject();
ParsedNested parsedNested = (ParsedNested) search.getAggregations().get(nestedName);// 外桶 是 Nested,所以查询数据强转为 ParsedNested
Cardinality cardinality = (Cardinality) parsedNested.getAggregations().get(distinctName);// 外桶 是 Cardinality 去重
dataMap.put(distinctName,cardinality.getValue());
return dataMap;
}
案例 10:分组后再分组统计
场景:分别 缺陷、隐患多少,每个 等级下 格多少
GET /ydxj_danger_record/_search
{
"size": "0",
"query": {
"bool": {
"must_not": [{
"match": {
"task_def_key": "ybh"
}
}, {
"match": {
"danger_record_type": "0"
}
}]
}
},
"aggs": {
"group_field": {
"terms": {
"field": "danger_record_type"
},
"aggs": {
"z_group_field": {
"terms": {
"field": "danger_range"
}
}
}
}
}
}
查询结构
"aggregations" : {
"group_field" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 2,
"doc_count" : 3537,
"z_group_field" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "一般",
"doc_count" : 3041
},
{
"key" : "严重",
"doc_count" : 9
},
{
"key" : "其他",
"doc_count" : 1
},
{
"key" : "危急",
"doc_count" : 1
}
]
}
},
{
"key" : 1,
"doc_count" : 1101,
"z_group_field" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "一般",
"doc_count" : 752
},
{
"key" : "严重",
"doc_count" : 125
},
{
"key" : "其他",
"doc_count" : 9
},
{
"key" : "危急",
"doc_count" : 7
},
{
"key" : "",
"doc_count" : 1
}
]
}
}
]
}
}
java 构成分组
public void addTermsAgg2(SearchSourceBuilder sourceBuilder,String aggName,String fieldName,String sonAggName,String sonFieldName) {
AggregationBuilder builder = AggregationBuilders.terms(aggName).field(fieldName);
sourceBuilder.aggregation(builder.subAggregation(AggregationBuilders.terms(sonAggName).field(sonFieldName)));
}
解析
使用
索引暂时不支持字段的删除,如果字段添加错误,或者修改类型需要 先 复制mapping 新建一个索引,然后迁移数据