文章目录

  • RestHighLevelClient介绍
  • 引入依赖
  • ES的配置
    • (1)、创建索引
    • (2)、application.yml 配置文件
    • (3)、实体对象
    • (4)、java 连接配置类
  • 索引操作
  • 文档操作
  • Bulk操作
  • DSL高级查询操作
    • 精确查询(term)
    • 全文查询(match)
    • 通配符查询(wildcard)
    • 模糊查询(fuzzy)
    • 排序查询(sort)
    • 分页查询(page)
      • 方式一: from + size
      • 方式二: scroll
      • 方式三: search_after
    • 范围查询(range)
    • 布尔查询(bool)
    • queryString查询
    • 查询结果过滤 (fetchSource)
    • 高亮查询(highlight)
    • 聚合查询
    • 集群健康状况
    • 索引信息

官方文档 : https://www.elastic.co/guide/en/elasticsearch/reference/8.3/index.html

RestHighLevelClient介绍

JavaREST客户端有两种模式:

  • Java Low Level REST Client:ES官方的低级客户端。低级别的客户端通过http与Elasticearch集群通信。
  • Java High Level REST Client:ES官方的高级客户端。基于上面的低级客户端,也是通过HTTP与ES集群进行通信。它提供了更多的接口。

下面介绍下 SpringBoot 如何通过 elasticsearch-rest-high-level-client 工具操作ElasticSearch。

注意事项:客户端(Client) Jar包的版本尽量不要大于Elasticsearch本体的版本,否则可能出现客户端中使用的某些API在Elasticsearch中不支持。

这里需要说一下,能使用RestHighLevelClient尽量使用它,笔记该RestClient是官方支持的ES客户端,更新和维护都是比较好的。

为什么不推荐使用 Spring 家族封装的 spring-data-elasticsearch。主要原因是灵活性和更新速度,Spring 将 ElasticSearch 过度封装,让开发者很难跟 ES 的 DSL 查询语句进行关联。再者就是更新速度,ES 的更新速度是非常快,但是 spring-data-elasticsearch 更新速度比较缓慢。并且spring-data-elasticsearch在Elasticsearch6.x和7.x版本上的Java API差距很大,如果升级版本需要花点时间来了解。

引入依赖

<!--引入es-high-level-client的坐标-->
<dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.6.2</version>
</dependency>
<dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-client</artifactId><version>7.6.2</version>
</dependency>
<dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.6.2</version>
</dependency>

ES的配置

(1)、创建索引

PUT /goods
{"mappings": {"properties": {"brandName": {"type": "keyword"},"categoryName": {"type": "keyword"},"createTime": {"type": "date","format": "yyyy-MM-dd HH:mm:ss"},"id": {"type": "keyword"},"price": {"type": "double"},"saleNum": {"type": "integer"},"status": {"type": "integer"},"stock": {"type": "integer"},"title": {"type": "text","analyzer": "ik_max_word","search_analyzer": "ik_smart"}}}
}

(2)、application.yml 配置文件

elasticsearch:host: 116.201.200.113port: 9200spring:# 应用名称application:name: elasticsearch-spring-dataelasticsearch:rest:# 定位ES的位置uris: http://116.201.200.113:9200

(3)、实体对象

@Data
@Accessors(chain = true)   // 链式赋值(连续set方法)
@AllArgsConstructor        // 全参构造
@NoArgsConstructor         // 无参构造
public class Goods {/*** 商品编号*/private Long id;/*** 商品标题*/private String title;/*** 商品价格*/private BigDecimal price;/*** 商品库存*/private Integer stock;/*** 商品销售数量*/private Integer saleNum;/*** 商品分类*/private String categoryName;/*** 商品品牌*/private String brandName;/*** 上下架状态*/private Integer status;/*** 商品创建时间*/@JSONField(format = "yyyy-MM-dd HH:mm:ss")private Date createTime;
}

(4)、java 连接配置类

/*** ES的配置类* ElasticSearchConfig*/
@Data
@Configuration
@ConfigurationProperties(prefix = "elasticsearch")
public class ElasticSearchConfig {private String host;private Integer port;/*** 如果@Bean没有指定bean的名称,那么这个bean的名称就是方法名*/@Beanpublic RestHighLevelClient restHighLevelClient() {return new RestHighLevelClient(RestClient.builder(new HttpHost(host, port, "http")));}
}

索引操作

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestIndex {@AutowiredRestHighLevelClient restHighLevelClient;/*** 创建索引库和映射表结构* 注意:索引一般不会怎么创建*/@Testpublic void indexCreate() throws Exception {IndicesClient indicesClient = restHighLevelClient.indices();// 创建索引CreateIndexRequest indexRequest = new CreateIndexRequest("goods");// 创建表 结构String mapping = "{\n" +"    \"properties\": {\n" +"      \"brandName\": {\n" +"        \"type\": \"keyword\"\n" +"      },\n" +"      \"categoryName\": {\n" +"        \"type\": \"keyword\"\n" +"      },\n" +"      \"createTime\": {\n" +"        \"type\": \"date\",\n" +"        \"format\": \"yyyy-MM-dd HH:mm:ss\"\n" +"      },\n" +"      \"id\": {\n" +"        \"type\": \"keyword\"\n" +"      },\n" +"      \"price\": {\n" +"        \"type\": \"double\"\n" +"      },\n" +"      \"saleNum\": {\n" +"        \"type\": \"integer\"\n" +"      },\n" +"      \"status\": {\n" +"        \"type\": \"integer\"\n" +"      },\n" +"      \"stock\": {\n" +"        \"type\": \"integer\"\n" +"      },\n" +"      \"title\": {\n" +"        \"type\": \"text\",\n" +"        \"analyzer\": \"ik_max_word\",\n" +"        \"search_analyzer\": \"ik_smart\"\n" +"      }\n" +"    }\n" +"  }";// 把映射信息添加到request请求里面// 第一个参数:表示数据源// 第二个参数:表示请求的数据类型indexRequest.mapping(mapping, XContentType.JSON);// 请求服务器CreateIndexResponse response = indicesClient.create(indexRequest, RequestOptions.DEFAULT);System.out.println(response.isAcknowledged());}/*** 获取表结构* GET goods/_mapping*/@Testpublic void getMapping() throws Exception {IndicesClient indicesClient = restHighLevelClient.indices();// 创建get请求GetIndexRequest request = new GetIndexRequest("goods");// 发送get请求GetIndexResponse response = indicesClient.get(request, RequestOptions.DEFAULT);// 获取表结构Map<String, MappingMetaData> mappings = response.getMappings();for (String key : mappings.keySet()) {System.out.println("key--" + mappings.get(key).getSourceAsMap());}}/*** 删除索引库*/@Testpublic void indexDelete() throws Exception {IndicesClient indicesClient = restHighLevelClient.indices();// 创建delete请求方式DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("goods");// 发送delete请求AcknowledgedResponse response = indicesClient.delete(deleteIndexRequest, RequestOptions.DEFAULT);System.out.println(response.isAcknowledged());}/*** 判断索引库是否存在*/@Testpublic void indexExists() throws Exception {IndicesClient indicesClient = restHighLevelClient.indices();// 创建get请求GetIndexRequest request = new GetIndexRequest("goods");// 判断索引库是否存在boolean result = indicesClient.exists(request, RequestOptions.DEFAULT);System.out.println(result);}
}

文档操作

    /*** 增加文档信息*/@Testpublic void addDocument() throws IOException {// 创建商品信息Goods goods = new Goods();goods.setId(1L);goods.setTitle("Apple iPhone 13 Pro (A2639) 256GB 远峰蓝色 支持移动联通电信5G 双卡双待手机");goods.setPrice(new BigDecimal("8799.00"));goods.setStock(1000);goods.setSaleNum(599);goods.setCategoryName("手机");goods.setBrandName("Apple");goods.setStatus(0);goods.setCreateTime(new Date());// 将对象转为jsonString data = JSON.toJSONString(goods);// 创建索引请求对象IndexRequest indexRequest = new IndexRequest("goods").id(goods.getId() + "").source(data, XContentType.JSON);// 执行增加文档IndexResponse response = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);log.info("创建状态:{}", response.status());
/*// 也可以通过Map来接收文档数据//文档内容//准备json数据Map<String, Object> jsonMap = new HashMap<>();jsonMap.put("name", "spring cloud实战");jsonMap.put("description", "本课程主要从四个章节进行讲解: 1.微服务架构入门 2.spring cloud 基础入门 3.实战Spring Boot 4.注册中心eureka。");jsonMap.put("studymodel", "201001");SimpleDateFormat dateFormat =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");jsonMap.put("timestamp", dateFormat.format(new Date()));jsonMap.put("price", 5.6f);//创建索引创建对象IndexRequest indexRequest = new IndexRequest("xc_course","doc");//文档内容indexRequest.source(jsonMap);//通过client进行http的请求IndexResponse indexResponse = client.index(indexRequest);DocWriteResponse.Result result = indexResponse.getResult();System.out.println(result);
*/}/*** 获取文档信息*/@Testpublic void getDocument() throws IOException {// 创建获取请求对象GetRequest getRequest = new GetRequest("goods", "1");GetResponse response = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);// Map<String, Object> sourceAsMap = response.getSourceAsMap();System.out.println(response.getSourceAsString());}/*** 更新文档信息*/@Testpublic void updateDocument() throws IOException {// 设置商品更新信息Goods goods = new Goods();goods.setTitle("Apple iPhone 13 Pro Max (A2644) 256GB 远峰蓝色 支持移动联通电信5G 双卡双待手机");goods.setPrice(new BigDecimal("9999"));// 将对象转为jsonString data = JSON.toJSONString(goods);// 创建索引请求对象UpdateRequest updateRequest = new UpdateRequest("goods", "1");// 设置更新文档内容updateRequest.doc(data, XContentType.JSON);// 执行更新文档UpdateResponse response = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);log.info("创建状态:{}", response.status());}/*** 删除文档信息*/@Testpublic void deleteDocument() throws IOException {// 创建删除请求对象DeleteRequest deleteRequest = new DeleteRequest("goods", "1");// 执行删除文档DeleteResponse response = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);log.info("删除状态:{}", response.status());}

Bulk操作

从MySQL数据库中查询一些数据, 批量导入到ES中

/*** 批量导入测试数据*/
@Test
public void importData() throws IOException {//1.查询所有数据,mysqlList<Goods> goodsList = goodsMapper.findAll();//2.bulk导入BulkRequest bulkRequest = new BulkRequest();//2.1 循环goodsList,创建IndexRequest添加数据for (Goods goods : goodsList) {//将goods对象转换为json字符串String data = JSON.toJSONString(goods);//map --> {}IndexRequest indexRequest = new IndexRequest("goods");indexRequest.id(goods.getId() + "").source(data, XContentType.JSON);bulkRequest.add(indexRequest);}BulkResponse response = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);System.out.println(response.status());
}

DSL高级查询操作

精确查询(term)

  • term查询:不会分析查询条件,只有当词条和查询字符串完全匹配时才匹配,也就是精确查找,比如数字,日期,布尔值或 not_analyzed 的字符串(未经分析的文本数据类型)

  • terms查询:terms 跟 term 有点类似,但 terms 允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去 做匹配:

/*** 精确查询(termQuery)*/
@Test
public void termQuery() {try {// 构建查询条件(注意:termQuery 支持多种格式查询,如 boolean、int、double、string 等,这里使用的是 string 的查询)SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// 表示检索title字段值为华为的文档searchSourceBuilder.query(QueryBuilders.termQuery("title", "华为"));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info("=======" + userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}/*** terms:多个查询内容在一个字段中进行查询*/
@Test
public void termsQuery() {try {// 构建查询条件(注意:termsQuery 支持多种格式查询,如 boolean、int、double、string 等,这里使用的是 string 的查询)SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// 表示title字段值为华为,OPPO,TCL的文档都能检索出来searchSourceBuilder.query(QueryBuilders.termsQuery("title", "华为", "OPPO", "TCL"));// 展示100条,默认只展示10条记录searchSourceBuilder.size(100);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}

全文查询(match)

全文查询会分析查询条件,先将查询条件进行分词,然后查询,求并集。

term和match的区别是:match是经过analyer的(进行分词),也就是说,文档首先被分析器给处理了。根据不同的分析器,分析的结果也稍显不同,然后再根据分词结果进行匹配。term则不经过分词,它是直接去倒排索引中查找了精确的值了

match 查询语法汇总:

  • match_all:查询全部。
  • match:返回所有匹配的分词。
  • match_phrase:短语查询,在match的基础上进一步查询词组,可以指定slop分词间隔。
  • match_phrase_prefix:前缀查询,根据短语中最后一个词组做前缀匹配,可以应用于搜索提示,但注意和max_expanions搭配。其实默认是50…
  • multi_match:多字段查询,使用相当的灵活,可以完成match_phrase和match_phrase_prefix的工作。
/*** 匹配查询符合条件的所有数据,并设置分页*/
@Test
public void matchAllQuery() {try {// 构建查询条件 (查询全部)MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// 创建查询源构造器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);// 设置分页searchSourceBuilder.from(0);searchSourceBuilder.size(3);// 设置排序searchSourceBuilder.sort("price", SortOrder.ASC);//设置源字段过虑,第一个参数结果集包括哪些字段,第二个参数表示结果集不包括哪些字段;查询的文档只包含哪些指定的字段searchSourceBuilder.fetchSource(new String[]{"title","categoryName"},new String[]{});// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}/*** 匹配查询数据*/
@Test
public void matchQuery() {try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// 查询title为华为的文档searchSourceBuilder.query(QueryBuilders.matchQuery("title", "华为"));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}/*** 词语匹配查询*/
@Test
public void matchPhraseQuery() {try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.matchPhraseQuery("title", "三星"));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}/*** 内容在多字段中进行查询*/
@Test
public void matchMultiQuery() {try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// "手机"在title字段,categoryName字段中存在的文档searchSourceBuilder.query(QueryBuilders.multiMatchQuery("手机", "title", "categoryName").minimumShouldMatch("50%") // 设置匹配度.field("title",10)); // 指定指定设置匹配度// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}

通配符查询(wildcard)

wildcard查询:会对查询条件进行分词。还可以使用通配符 ?(任意单个字符) 和 * (0个或多个字符)

/*** 查询所有以 “三” 结尾的商品信息* <p>* *:表示多个字符(0个或多个字符)* ?:表示单个字符*/
@Test
public void wildcardQuery() {try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.wildcardQuery("title", "*三"));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}

模糊查询(fuzzy)

fuzzy 模糊查询 最大模糊错误 必须在0-2之间
搜索关键词长度为 2 不允许存在模糊 0
搜索关键词长度为3-5 允许一次模糊 0 1
搜索关键词长度大于5 允许最大2模糊

/*** 模糊查询所有以 “三” 结尾的商品信息*/
@Test
public void fuzzyQuery() {try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.fuzzyQuery("title", "三").fuzziness(Fuzziness.AUTO));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}

排序查询(sort)

注意:需要分词的字段不可以直接排序,比如:text类型,如果想要对这类字段进行排序,需要特别设置:对字段索引两次,一次索引分词(用于搜索)一次索引不分词(用于排序),es默认生成的text类型字段就是通过这样的方法实现可排序的。

/*** 排序查询(sort) 代码同matchAllQuery* 匹配查询符合条件的所有数据,并设置分页*/
@Test
public void matchAllQuery() {try {// 构建查询条件MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// 创建查询源构造器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);// 设置分页searchSourceBuilder.from(0);searchSourceBuilder.size(3);// 设置排序searchSourceBuilder.sort("price", SortOrder.ASC);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}

分页查询(page)

Elasticsearchde 的分页查询和 SQL 使用 LIMIT 关键字返回只有一页的结果一样,Elasticsearch 接受 from 和 size 参数:

  • size: 结果数,默认10
  • from: 跳过开始的结果数,即从哪一行开始获取数据,默认0

这种方式分页查询如果需要深度分页,那么这种方式性能不太好。

深度分页: 建议使用 scroll滚动查询 和 search_after滚动查询

方式一: from + size

实现原理

  • es是通过协调节点从每个shard(分片)中都获取from+size条数据返回给协调节点后,由协调节点汇总排序,然后查找[from , frome+size] 之间的数据,并返回给前端。

存在的问题

  • 该搜索请求占用的堆内存和时间与from+size大小成正比,值越大,性能越差、效率越低,一般用于浅分页中。 查询的值超过默认(10000)时会报错 :from+size > max_result_window时,会报错。

Kibana中测试示例

GET /book/_search
{"from" : 100 , "size" : 10,"query" : {"match" : { "title" : "区块链" }}
}

Java代码示例

// 构建搜索条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
...
searchSourceBuilder.from(100).size(10);
// 构建请求
SearchRequest searchRequest = new SearchRequest("goods");
searchRequest.source(searchSourceBuilder);
return restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

详细代码

/*** 分页查询(page) 代码同matchAllQuery* 匹配查询符合条件的所有数据,并设置分页*/
@Test
public void matchAllQuery() {try {// 构建查询条件MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// 创建查询源构造器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);// 设置分页searchSourceBuilder.from(0);searchSourceBuilder.size(3);// 设置排序searchSourceBuilder.sort("price", SortOrder.ASC);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}

方式二: scroll

实现原理

  • scroll API可用于从单个搜索请求中检索大量结果(甚至所有结果),其方式与在传统数据库上使用光标的方式大致相同。
  • 第一次查询时,会生产当时查询的快照,后续的查询只要携带上次返回的scroll_id即可(类似数据库游标) 。

特点

  • 可以深查询(甚至全部数据),性能、效率优于from+size方式。
  • 快照查询,在scroll查询期间 ,外部对索引内的数据进行增删改查等操作不会影响到这个快照的结果。

Kibana中测试示例

第一次查询 ,并设置上下文scroll_id存活时间为1分钟。

POST book/_search?scroll=1m
{"size": 10,"query": {"match" : {"name": "明朝的那些事"}}
}

响应

{"_scroll_id":"DXF1ZXJ5QW5kRmV0Y2gBAAAAAAD1K-0WdzM3OF9WRVFUcXk3bFNDcjZKZ1pZdw==","took":22,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":1302017,"max_score":2.5815613,"hits":Array[10]}
}

后续查询,使用前一个scroll_id即可:

POST _search/scroll
{"scroll":"1m",    "scroll_id":"DXF1ZXJ5QW5kRmV0Y2gBAAAAAAD1K-0WdzM3OF9WRVFUcXk3bFNDcjZKZ1pZdw=="
}

Java代码示例

首次调用

// 构建搜索条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); ...searchSourceBuilder.from(100).size(10);
// 构建请求
SearchRequest searchRequest = new SearchRequest("goods");
searchRequest.types("title");
// ===》 关键是多加了这句话
searchRequest.scroll(new Scroll(TimeValue.timeValueMinutes(1)));
searchRequest.source(searchSourceBuilder);
return restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

后续调用

// 传入上次返回的scroll_id
SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
scrollRequest.scroll(TimeValue.timeValueMinutes(1));
SearchResponse response = restHighLevelClient.getRestClient().scroll(scrollRequest, RequestOptions.DEFAULT);
return response;

详细代码

/*** 根据查询条件滚动查询* 可以用来解决深度分页查询问题*/
@Test
public void scrollQuery() {// 假设用户想获取第70页数据,其中每页10条int pageNo = 70;int pageSize = 10;// 定义请求对象SearchRequest searchRequest = new SearchRequest("goods");// 构建查询条件SearchSourceBuilder builder = new SearchSourceBuilder();searchRequest.source(builder.query(QueryBuilders.matchAllQuery()).size(pageSize));String scrollId = null;// 3、发送请求到ESSearchResponse scrollResponse = null;// 设置游标id存活时间Scroll scroll = new Scroll(TimeValue.timeValueMinutes(2));// 记录所有游标idList<String> scrollIds = new ArrayList<>();for (int i = 0; i < pageNo; i++) {try {// 首次检索if (i == 0) {//记录游标idsearchRequest.scroll(scroll);// 首次查询需要指定索引名称和查询条件SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 下一次搜索要用到该游标idscrollId = response.getScrollId();// 记录所有游标id}// 非首次检索else {// 不需要在使用其他条件,也不需要指定索引名称,只需要使用执行游标id存活时间和上次游标id即可,毕竟信息都在上次游标id里面呢SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);searchScrollRequest.scroll(scroll);scrollResponse = restHighLevelClient.scroll(searchScrollRequest, RequestOptions.DEFAULT);// 下一次搜索要用到该游标idscrollId = scrollResponse.getScrollId();// 记录所有游标id}scrollIds.add(scrollId);} catch (Exception e) {e.printStackTrace();}}//清除游标idClearScrollRequest clearScrollRequest = new ClearScrollRequest();clearScrollRequest.scrollIds(scrollIds);try {restHighLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);} catch (IOException e) {System.out.println("清除滚动查询游标id失败");e.printStackTrace();}// 4、处理响应结果System.out.println("滚动查询返回数据:");assert scrollResponse != null;SearchHits hits = scrollResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(goods.toString());}
}

方式三: search_after

实现原理

  • search_after 分页的方式和 scroll 基本类似, 比较明显的区别 就是首先根据排序等条件查询,然后根据上一页中任何一条数据(sort结果),来确定下一页开始的位置

特点

  • 可以深查询
  • 实时查询 :在查询期间 ,有索引数据增删改查,这些变更也会实时的反映到相应的分页中。
  • 需要文档有全局唯一值字段:用于排序中。例如使用业务层字段 id。

Kibana中测试示例

第一次查询: 需要设置查询条件,排序条件 ,我这里的排序是按照timestamp,_id 字段排序的。

GET book/_search
{"size": 10,"query": {"bool": {"must": [{"term": {"name": "明朝的那些事"}}]}},"sort": [{"timestamp": "asc"},{"_id": "asc"}]
}

响应 : 每条数据后面有个sort字段,

{"took":3,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":7721,"max_score":null,"hits":[Object{...},Object{...},Object{...},Object{...},Object{...},Object{...},Object{...},Object{...},Object{...},{"_index":"log-20191017","_type":"_doc","_id":"1297720911377862657","_score":null,"_source":{..."id":"1297720911377862657","ip":"127.0.0.1",..."timestamp":1571254494349},"sort":[1571254494349,"1297720911377862657"]}]}
}

后续查询, 增加一个查询字段search_after,里面使用上面任一sort值(如果是分页查询,那就用最后一个)

GET book/_search
{"size": 10,"query": {"bool": {"must": [{"term": {"name": "明朝的那些事"}}]}},"search_after": [1571254494349, "1297720911377862657"],"sort": [{"timestamp": "asc"},{"_id": "asc"}]
}

Java代码示例

首次调用: searchAfter 不需要打开


//查询条件
BoolQueryBuilder boolQueryBuild = QueryBuilders.boolQuery();
QueryBuilder matchQuery = QueryBuilders.matchQuery("title", "BigManing");
boolQueryBuild.filter(matchQuery);
// 构建请求  设置查询条件 排序条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.setQuery(boolQueryBuild);
searchSourceBuilder.addSort(SortBuilders.fieldSort("timestamp").order(SortOrder.ASC));
searchSourceBuilder.addSort(SortBuilders.fieldSort("_id").order(SortOrder.ASC));
searchSourceBuilder.setSize(pageSize);
//requestBuilder.searchAfter(null);
// 请求 es
SearchRequest searchRequest = new SearchRequest("goods");
searchRequest.source(searchSourceBuilder);
return restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT)

后续调用: 需要传入上页最后一个数据的sort值。 新加requestBuilder.searchAfter(sortData) 这句话。

 BoolQueryBuilder boolQueryBuild = QueryBuilders.boolQuery();
QueryBuilder matchQuery = QueryBuilders.matchQuery("title", "BigManing");
boolQueryBuild.filter(matchQuery);
// 构建请求  设置查询条件 排序条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.setQuery(boolQueryBuild);
searchSourceBuilder.addSort(SortBuilders.fieldSort("timestamp").order(SortOrder.ASC));
searchSourceBuilder.addSort(SortBuilders.fieldSort("_id").order(SortOrder.ASC));
searchSourceBuilder.setSize(pageSize);
// 接受上面的sort值 ,进行分页查询
Object[] sortData;
searchSourceBuilder.searchAfter(sortData);
// 请求 es
SearchRequest searchRequest = new SearchRequest("book");
searchRequest.source(searchSourceBuilder);
return restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT)

范围查询(range)

/*** 查询价格大于等于10000的商品信息*/
@Test
public void rangeQuery() {try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.rangeQuery("price").gte(10000));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();log.info(hits.getTotalHits().value + "");for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}/*** 查询距离现在 10 年间的商品信息* [年(y)、月(M)、星期(w)、天(d)、小时(h)、分钟(m)、秒(s)]* 例如:* now-1h 查询一小时内范围* now-1d 查询一天内时间范围* now-1y 查询最近一年内的时间范围*/
@Test
public void dateRangeQuery() {try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// includeLower(是否包含下边界)、includeUpper(是否包含上边界)searchSourceBuilder.query(QueryBuilders.rangeQuery("createTime").gte("now-10y").includeLower(true).includeUpper(true));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}

布尔查询(bool)

bool 查询可以用来合并多个条件查询结果的布尔逻辑,它包含一下操作符:

  • must:多个查询条件必须完全匹配,相当于关系型数据库中的 and。
  • should:至少有一个查询条件匹配,相当于关系型数据库中的 or。
  • must_not: 多个查询条件的相反匹配,相当于关系型数据库中的 not。
  • filter:过滤满足条件的数据。
    • range:条件筛选范围。

      • gt:大于,相当于关系型数据库中的 >。
      • gte:大于等于,相当于关系型数据库中的 >=。
      • lt:小于,相当于关系型数据库中的 <。
      • lte:小于等于,相当于关系型数据库中的 <=。
/*** boolQuery 查询* 案例:查询从2018-2022年间标题含 三星 的商品信息*/
@Test
public void boolQuery() {try {// 创建 Bool 查询构建器BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();// 构建查询条件boolQueryBuilder.must(QueryBuilders.matchQuery("title", "三星")).filter().add(QueryBuilders.rangeQuery("createTime").format("yyyy").gte("2018").lte("2022"));// 构建查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(boolQueryBuilder);searchSourceBuilder.size(100);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(goods.toString());}}} catch (IOException e) {log.error("", e);}
}

queryString查询

会对查询条件进行分词, 然后将分词后的查询条件和词条进行等值匹配,默认取并集(OR),可以指定单个字段也可多个查询字段

/*** queryStringQuery查询* 案例:查询出必须包含 华为手机 词语的商品信息*/
@Test
public void queryStringQuery() {try {// 创建 queryString 查询构建器// 会对华为手机进行分词,没有设置检索的field,默认对mapping中字符串类型的filed进行检索;QueryStringQueryBuilder queryStringQueryBuilder = QueryBuilders.queryStringQuery("华为手机").defaultOperator(Operator.AND);// 设置了field,就对指定的字段进行检索//QueryStringQueryBuilder queryStringQueryBuilder = QueryBuilders.queryStringQuery("华为手机").field("title").field("categoryName").defaultOperator(Operator.AND);// 构建查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(queryStringQueryBuilder);searchSourceBuilder.size(100);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(goods.toString());}}} catch (IOException e) {log.error("", e);}
}

  • Operator.AND : 要检索的内容, 进行分词后, 分词的内容, 要在文档中全部存在, 才能检索出来
  • Operator.OR : 要检索的内容, 进行分词后, 分词的内容, 在文档中存在一个就能检索出来

查询结果过滤 (fetchSource)

我们在查询数据的时候,返回的结果中,所有字段都给我们返回了,但是有时候我们并不需要那么多,所以可以对结果进行过滤处理。

/*** 过滤source获取部分字段内容* 案例:只获取 title、categoryName和price的数据*/
@Test
public void sourceFilter() {try {//查询条件(词条查询:对应ES query里的match)BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("title", "金立")).must(QueryBuilders.matchQuery("categoryName", "手机")).filter(QueryBuilders.rangeQuery("price").gt(1000).lt(2000));// 构建查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(boolQueryBuilder);// 如果查询的属性很少,那就使用includes,而excludes设置为空数组// 如果排序的属性很少,那就使用excludes,而includes设置为空数组String[] includes = {"title", "categoryName", "price"};String[] excludes = {};searchSourceBuilder.fetchSource(includes, excludes);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(goods.toString());}}} catch (IOException e) {log.error("", e);}
}

高亮查询(highlight)

/*** 高亮查询* 案例:把标题中为 三星手机 的词语高亮显示*/
@Test
public void highlightBuilder() {try {//查询条件(词条查询:对应ES query里的match)MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("title", "三星手机");//设置高亮三要素                                     field: 你的高亮字段    // preTags :前缀    // postTags:后缀HighlightBuilder highlightBuilder = new HighlightBuilder().field("title").preTags("<font color='red'>").postTags("</font>");// 构建查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchQueryBuilder);searchSourceBuilder.highlighter(highlightBuilder);searchSourceBuilder.size(100);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 获取高亮的数据HighlightField highlightField = hit.getHighlightFields().get("title");System.out.println("高亮名称:" + highlightField.getFragments()[0].string());// 替换掉原来的数据Text[] fragments = highlightField.getFragments();if (fragments != null && fragments.length > 0) {StringBuilder title = new StringBuilder();for (Text fragment : fragments) {//System.out.println(fragment);title.append(fragment);}goods.setTitle(title.toString());}// 输出查询信息log.info(goods.toString());}}} catch (IOException e) {log.error("", e);}
}

聚合查询

  • 聚合查询对比DB的讲解
  • DSL聚合

我们平时在使用Elasticsearch时,更多会用到聚合操作,它类似SQL中的group by操作。ES的聚合查询一定是先查出结果,然后对结果使用聚合函数做处理,常用的操作有:avg:求平均、max:最大值、min:最小值、sum:求和等。

在ES中聚合分为指标聚合和分桶聚合:

  • Metric 指标聚合:指标聚合对一个数据集求最大、最小、和、平均值
  • Bucket 分桶聚合:除了有上面的聚合函数外,还可以对查询出的数据进行分组group by,再在组上进行游标聚合。

Metric 指标聚合分析

/*** 聚合查询* Metric 指标聚合分析* 案例:分别获取最贵的商品和获取最便宜的商品*/
@Test
public void metricQuery() {try {// 构建查询条件MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// 创建查询源构造器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);// 获取最贵的商品AggregationBuilder maxPrice = AggregationBuilders.max("maxPrice").field("price");searchSourceBuilder.aggregation(maxPrice);// 获取最便宜的商品AggregationBuilder minPrice = AggregationBuilders.min("minPrice").field("price");searchSourceBuilder.aggregation(minPrice);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);Aggregations aggregations = searchResponse.getAggregations();ParsedMax max = aggregations.get("maxPrice");log.info("最贵的价格:" + max.getValue());ParsedMin min = aggregations.get("minPrice");log.info("最便宜的价格:" + min.getValue());} catch (IOException e) {log.error("", e);}
}/*** 聚合查询* Bucket 分桶聚合分析* 案例:根据品牌进行聚合查询*/
@Test
public void bucketQuery() {try {// 构建查询条件MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// 创建查询源构造器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);// 根据商品分类进行分组查询TermsAggregationBuilder aggBrandName = AggregationBuilders.terms("brandNameName").field("brandName");searchSourceBuilder.aggregation(aggBrandName);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);Aggregations aggregations = searchResponse.getAggregations();ParsedStringTerms aggBrandName1 = aggregations.get("brandNameName");for (Terms.Bucket bucket : aggBrandName1.getBuckets()) {// 分组名 ==== 数量System.out.println(bucket.getKeyAsString() + "====" + bucket.getDocCount());}} catch (IOException e) {log.error("", e);}
}

Bucket 分桶聚合分析

    /*** 聚合查询* Bucket 分桶聚合分析* 案例:根据品牌进行聚合查询*/@Testpublic void bucketQuery() {try {// 构建查询条件MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// 创建查询源构造器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);// 根据商品分类进行分组查询TermsAggregationBuilder aggBrandName = AggregationBuilders.terms("brandNameName").field("brandName");searchSourceBuilder.aggregation(aggBrandName);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);Aggregations aggregations = searchResponse.getAggregations();ParsedStringTerms aggBrandName1 = aggregations.get("brandNameName");for (Terms.Bucket bucket : aggBrandName1.getBuckets()) {System.out.println(bucket.getKeyAsString() + "====" + bucket.getDocCount());}} catch (IOException e) {log.error("", e);}}/*** 子聚合聚合查询* Bucket 分桶聚合分析* 案例:根据商品分类进行分组查询,并且获取分类商品中的平均价格*/@Testpublic void subBucketQuery() {try {// 构建查询条件MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// 创建查询源构造器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);// 根据商品分类进行分组查询,并且获取分类商品中的平均价格TermsAggregationBuilder subAggregation = AggregationBuilders.terms("brandNameName").field("brandName").subAggregation(AggregationBuilders.avg("avgPrice").field("price"));searchSourceBuilder.aggregation(subAggregation);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);Aggregations aggregations = searchResponse.getAggregations();ParsedStringTerms aggBrandName1 = aggregations.get("brandNameName");for (Terms.Bucket bucket : aggBrandName1.getBuckets()) {// 获取聚合后的品牌的平均价格,注意返回值不是Aggregation对象,而是指定的ParsedAvg对象ParsedAvg avgPrice = bucket.getAggregations().get("avgPrice");// 分组名 ==== 平均价格System.out.println(bucket.getKeyAsString() + "====" + avgPrice.getValueAsString());}} catch (IOException e) {log.error("", e);}}

综合聚合查询

/*** 综合聚合查询* 根据商品分类聚合,获取每个商品类的平均价格,并且在商品分类聚合之上子聚合每个品牌的平均价格*/
@Test
public void subSubAgg() throws IOException {// 构建查询条件MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// 创建查询源构造器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);// 注意这里聚合写的位置不要写错,很容易搞混,错一个括号就不对了TermsAggregationBuilder subAggregation = AggregationBuilders.terms("categoryNameAgg").field("categoryName").subAggregation(AggregationBuilders.avg("categoryNameAvgPrice").field("price")).subAggregation(AggregationBuilders.terms("brandNameAgg").field("brandName").subAggregation(AggregationBuilders.avg("brandNameAvgPrice").field("price")));searchSourceBuilder.aggregation(subAggregation);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);//获取总记录数System.out.println("totalHits = " + searchResponse.getHits().getTotalHits().value);// 获取聚合信息Aggregations aggregations = searchResponse.getAggregations();ParsedStringTerms categoryNameAgg = aggregations.get("categoryNameAgg");//获取值返回for (Terms.Bucket bucket : categoryNameAgg.getBuckets()) {// 获取聚合后的分类名称String categoryName = bucket.getKeyAsString();// 获取聚合命中的文档数量long docCount = bucket.getDocCount();// 获取聚合后的分类的平均价格,注意返回值不是Aggregation对象,而是指定的ParsedAvg对象ParsedAvg avgPrice = bucket.getAggregations().get("categoryNameAvgPrice");System.out.println(categoryName + "======平均价:" + avgPrice.getValue() + "======数量:" + docCount);ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brandNameAgg");for (Terms.Bucket brandeNameAggBucket : brandNameAgg.getBuckets()) {// 获取聚合后的品牌名称String brandName = brandeNameAggBucket.getKeyAsString();// 获取聚合后的品牌的平均价格,注意返回值不是Aggregation对象,而是指定的ParsedAvg对象ParsedAvg brandNameAvgPrice = brandeNameAggBucket.getAggregations().get("brandNameAvgPrice");System.out.println("     " + brandName + "======" + brandNameAvgPrice.getValue());}}
}

集群健康状况

@Test
public void clusterHealthStatus() throws IOException {ClusterHealthRequest request = new ClusterHealthRequest();ClusterHealthResponse response = restHighLevelClient.cluster().health(request, RequestOptions.DEFAULT);ClusterHealthStatus status = response.getStatus();System.out.println("集群名称:" + response.getClusterName());System.out.println("集群健康状态:" + status.name());
}

索引信息

@Test
public void printIndexInfo() throws IOException {Response response = client.getLowLevelClient().performRequest(new Request("GET", "/_cat/indices"));HttpEntity entity = response.getEntity();String responseStr = EntityUtils.toString(entity, StandardCharsets.UTF_8);String[] indexInfoArr = responseStr.split("\n");for (String indexInfo : indexInfoArr) {String[] infoArr = indexInfo.split("\\s+");String status = infoArr[0];String open = infoArr[1];String name = infoArr[2];String id = infoArr[3];String mainShardNum = infoArr[4];String viceShardNum = infoArr[5];String docNum = infoArr[6];String deletedDocNum = infoArr[7];String allShardSize = infoArr[8];String mainShardSize = infoArr[9];System.out.println("》》》》》》》》索引信息》》》》》》》》");System.out.println("名称:" + name);System.out.println("id:" + id);System.out.println("状态:" + status);System.out.println("是否开放:" + open);System.out.println("主分片数量:" + mainShardNum);System.out.println("副本分片数量:" + viceShardNum);System.out.println("Lucene文档数量:" + docNum);System.out.println("被删除文档数量:" + deletedDocNum);System.out.println("所有分片大小:" + allShardSize);System.out.println("主分片大小:" + mainShardSize);}
}

重学Elasticsearch第6章 : SpringBoot整合RestHighLevelClient相关推荐

  1. 重学Elasticsearch第8章 : SpringBoot整合Jest客户端

    文章目录 JestClient介绍 引入依赖 ES的配置 (1).application.yml 配置文件 (2).java 连接配置类 JestClient操作ElasticSearch 客户端初始 ...

  2. 重学Elasticsearch第1章 : Elasticsearch, Kibana概念、Elasticsearch相关术语

    文章目录 Elastic Stack 是什么 ElasticSearch 概念 什么是RestFul 什么是全文检索 什么是Elasticsearch ES的应用场景 安装Elasticsearch ...

  3. 第九章SpringBoot整合Spring Data JPA

    目录 1 概述 2 Spring Data JPA整合 2.1 pom文件 2.2 配置文件 2.3 实体类 2.4 Dao接口 2.5 启动类 2.6 编写测试类 3 Spring Data JPA ...

  4. ElasticSearch(三)springboot整合ES

    最基础的整合: 一.maven依赖 <parent><groupId>org.springframework.boot</groupId><artifactI ...

  5. 重学JavaSE 第12章 : 枚举和注解、注解的实战使用

    文章目录 一.枚举类的使用 1.1.枚举类的理解 1.2.自定义枚举类 1.3.使用enum关键字定义枚举类 1.4.Enum类中的常用方法 1.5.使用enum关键字定义的枚举类实现接口 二.注解的 ...

  6. 重学JavaSE 第4章 : 顺序结构、分支语句、循环结构、break, continue, return区别

    文章目录 一. 程序流程控概述 二. 顺序结构 三.分支语句 2.1.分支语句1:if-else结构 2.1.1.输入语句 2.2. 分支语句2:switch-case结构 四.循环结构 4.1.fo ...

  7. 重学JavaSE 第11章 : 常用类API、String、日期API、比较器、BigDecimal、System等

    文章目录 一.字符串相关的类 1.1.String类的概述 1.2.理解String的不可变性 1.3.String不同实例化方式的对比 1.4.String不同拼接操作的对比 1.4.1.Strin ...

  8. SpringBoot整合RestHighLevelClient实现查询操作

  9. 七、SpringBoot整合elasticsearch集群

    @Author : By Runsen @Date : 2020/6/12 作者介绍:Runsen目前大三下学期,专业化学工程与工艺,大学沉迷日语,Python, Java和一系列数据分析软件.导致翘 ...

最新文章

  1. 为什么阿里巴巴要禁用Executors创建线程池?
  2. MS SQLSERVER通用存储过程分页
  3. ​【安全牛学习笔记】操作系统识别
  4. Codeforces #1063C Dwarves, Hats and Extrasensory Abilities
  5. 爬虫cookie过期_python instagram 爬虫
  6. 超详细的jQuery的 DOM操作,一篇就足够!
  7. Android APK 签名比对
  8. 从零入门 Serverless | 使用 Spot 低成本运行 Job 任务
  9. Unity List的拷贝
  10. swift拖放的按钮如何在后台设置点击事件 www.cnblogs.com/foxting/p/SWIFT.html
  11. 胡嘉伟 :实时计算在提升播放体验的应用实践
  12. 图解技术原理,真的太赞了!
  13. [Android]Android P(9) WIFI学习笔记 - HAL (1)
  14. 雇佣兵战斗力c语言原理,暗黑2单机弓箭亚马逊应该怎么选技能和雇佣兵
  15. PS 图像调整算法——反相
  16. bmob php支付,个人开发者也能盈利!Bmob支付SDK使用实例
  17. 如何使用TensorRT加速深度学习推理
  18. iptables实战演练
  19. 制作简单的WPF时钟
  20. 动手学强化学习第三章(马尔可夫决策过程)

热门文章

  1. 贪心算法1——找零钱问题
  2. Fitbit协助执法部门破获一起犯罪案件
  3. Java也能做爬虫了?我爬取并下载了酷狗TOP500的歌曲!
  4. Tomcat for win7 搭建集群
  5. H3C任意命令执行漏洞
  6. 一汽集团2019美国招聘活动即将开启!
  7. Android 地图导航调用百度地图、高德地图、腾讯地图,腾讯T3团队整理
  8. 如何为您的插件创建一个很棒的WordPress.org页面
  9. 对PostgreSQL源代码中的is_pushed_down的理解
  10. No such file or directory处理