ElasticSearch

lishihuan大约 13 分钟

ElasticSearch

使用kibana操作ElasticSearch

参考网址:

https://blog.csdn.net/qq_37129281/article/details/89293591open in new window

https://blog.csdn.net/laoyang360/article/details/82950393open in new window

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/81173016open in new window

案例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"// 分组字段
			}
		}
	}
}
image-20200904110007563
image-20200904110007563

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 新建一个索引,然后迁移数据

索引添加字段

1.添加普通字段