基于SpringBoot的ElasticSearch操作(超详细教程)

一、ElasticSearch 简介

1、简介


ElasticSearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式多员工能力的全文搜索引擎,基于 RESTful web 接口。Elasticsearch 是用 Java 语言开发的,并作为 Apache 许可条款下的开放源码发布,是一种流行的企业级搜索引擎。

ElasticSearch 用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

2、特性

分布式的文档存储引擎
分布式的搜索引擎和分析引擎
分布式,支持PB级数据

3、使用场景


搜索领域:如百度、谷歌,全文检索等。
门户网站:访问统计、文章点赞、留言评论等。
广告推广:记录员工行为数据、消费趋势、员工群体进行定制推广等。
信息采集:记录应用的埋点数据、访问日志数据等,方便大数据进行分析。

二、ElasticSearch 基础概念

1、ElaticSearch 和 DB 的关系

在 Elasticsearch 中,文档归属于一种类型 type,而这些类型存在于索引 index 中,我们可以列一些简单的不同点,来类比传统关系型数据库:

Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices -> Types -> Documents -> Fields
Elasticsearch 集群可以包含多个索引 indices,每一个索引可以包含多个类型 types,每一个类型包含多个文档 documents,然后每个文档包含多个字段 Fields。而在 DB 中可以有多个数据库 Databases,每个库中可以有多张表 Tables,没个表中又包含多行Rows,每行包含多列Columns。

2、索引

索引基本概念(indices):

索引是含义相同属性的文档集合,是 ElasticSearch 的一个逻辑存储,可以理解为关系型数据库中的数据库,ElasticSearch 可以把索引数据存放到一台服务器上,也可以 sharding 后存到多台服务器上,每个索引有一个或多个分片,每个分片可以有多个副本。

索引类型(index_type):

索引可以定义一个或多个类型,文档必须属于一个类型。在 ElasticSearch 中,一个索引对象可以存储多个不同用途的对象,通过索引类型可以区分单个索引中的不同对象,可以理解为关系型数据库中的表。每个索引类型可以有不同的结构,但是不同的索引类型不能为相同的属性设置不同的类型。

3、文档

文档(document):

文档是可以被索引的基本数据单位。存储在 ElasticSearch 中的主要实体叫文档 document,可以理解为关系型数据库中表的一行记录。每个文档由多个字段构成,ElasticSearch 是一个非结构化的数据库,每个文档可以有不同的字段,并且有一个唯一的标识符。

4、映射

映射(mapping):

ElasticSearch 的 Mapping 非常类似于静态语言中的数据类型:声明一个变量为 int 类型的变量,以后这个变量都只能存储 int 类型的数据。同样的,一个 number 类型的 mapping 字段只能存储 number 类型的数据。

同语言的数据类型相比,Mapping 还有一些其他的含义,Mapping 不仅告诉 ElasticSearch 一个 Field 中是什么类型的值, 它还告诉 ElasticSearch 如何索引数据以及数据是否能被搜索到。

ElaticSearch 默认是动态创建索引和索引类型的 Mapping 的。这就相当于无需定义 Solr 中的 Schema,无需指定各个字段的索引规则就可以索引文件,很方便。但有时方便就代表着不灵活。比如,ElasticSearch 默认一个字段是要做分词的,但我们有时要搜索匹配整个字段却不行。如有统计工作要记录每个城市出现的次数。对于 name 字段,若记录 new york 文本,ElasticSearch 可能会把它拆分成 new 和 york 这两个词,分别计算这个两个单词的次数,而不是我们期望的 new york。

三、SpringBoot 项目引入 ElasticSearch 依赖

下面介绍下 SpringBoot 如何通过 elasticsearch-rest-high-level-client 工具操作 ElasticSearch,这里需要说一下,为什么没有使用 Spring 家族封装的 spring-data-elasticsearch。

主要原因是灵活性和更新速度,Spring 将 ElasticSearch 过度封装,让开发者很难跟 ES 的 DSL 查询语句进行关联。再者就是更新速度,ES 的更新速度是非常快,但是 spring-data-elasticsearch 更新速度比较缓慢。

由于上面两点,所以选择了官方推出的 Java 客户端 elasticsearch-rest-high-level-client,它的代码写法跟 DSL 语句很相似,懂 ES 查询的使用其上手很快。

【注意SpringBoot的版本-es的版本对应】

1、Maven 引入相关依赖

  • lombok:lombok 工具依赖。
  • fastjson:用于将 JSON 转换对象的依赖。
  • spring-boot-starter-web: SpringBoot 的 Web 依赖。
  • elasticsearch:ElasticSearch:依赖,需要和 ES 版本保持一致。
  • elasticsearch-rest-high-level-client:用于操作 ES 的 Java 客户端。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>elasticsearch</artifactId><version>0.0.1-SNAPSHOT</version><name>elasticsearch</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.3.12.RELEASE</spring-boot.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--fastjson--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.61</version></dependency><!--elasticsearch--><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.6.1</version></dependency><dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.6.1</version></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><mainClass>com.example.elasticsearch.ElasticsearchApplication</mainClass><skip>true</skip></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>

2、ElasticSearch 连接配置

(1)、application.yml 配置文件

为了方便更改连接 ES 的连接配置,所以我们将配置信息放置于 application.yml 中:

server:port: 8080servlet:context-path: /searchelasticsearch:schema: httpaddress: 127.0.0.1:9200connectTimeout: 10000socketTimeout: 10000connectionRequestTimeout: 10000maxConnectNum: 100maxConnectPerRoute: 100myindex: testindex

(2)、java 连接配置类

这里需要写一个 Java 配置类读取 application 中的配置信息:

package com.example.elasticsearch.demos.config;import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.ArrayList;
import java.util.List;/*** ElasticSearch 配置*/
@Configuration
public class ElasticSearchConfig {/** 协议 */@Value("${elasticsearch.schema:http}")private String schema;/** 集群地址,如果有多个用“,”隔开 */@Value("${elasticsearch.address}")private String address;/** 连接超时时间 */@Value("${elasticsearch.connectTimeout:5000}")private int connectTimeout;/** Socket 连接超时时间 */@Value("${elasticsearch.socketTimeout:10000}")private int socketTimeout;/** 获取连接的超时时间 */@Value("${elasticsearch.connectionRequestTimeout:5000}")private int connectionRequestTimeout;/** 最大连接数 */@Value("${elasticsearch.maxConnectNum:100}")private int maxConnectNum;/** 最大路由连接数 */@Value("${elasticsearch.maxConnectPerRoute:100}")private int maxConnectPerRoute;@Beanpublic RestHighLevelClient restHighLevelClient() {// 拆分地址List<HttpHost> hostLists = new ArrayList<>();String[] hostList = address.split(",");for (String addr : hostList) {String host = addr.split(":")[0];String port = addr.split(":")[1];hostLists.add(new HttpHost(host, Integer.parseInt(port), schema));}// 转换成 HttpHost 数组HttpHost[] httpHost = hostLists.toArray(new HttpHost[]{});// 构建连接对象RestClientBuilder builder = RestClient.builder(httpHost);// 异步连接延时配置builder.setRequestConfigCallback(requestConfigBuilder -> {requestConfigBuilder.setConnectTimeout(connectTimeout);requestConfigBuilder.setSocketTimeout(socketTimeout);requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeout);return requestConfigBuilder;});// 异步连接数配置builder.setHttpClientConfigCallback(httpClientBuilder -> {httpClientBuilder.setMaxConnTotal(maxConnectNum);httpClientBuilder.setMaxConnPerRoute(maxConnectPerRoute);return httpClientBuilder;});return new RestHighLevelClient(builder);}}

四、索引操作示例

这里示例会指出通过Postman的 ​ ​Restful​​​ 工具操作与对应的 Java 代码操作的两个示例。

1、Restful 操作示例

创建索引

创建名为 testindex 的索引与对应 Mapping。

PUT http://localhost:9200/testindex{"mappings": {"doc": {"dynamic": true,"properties": {"name": {"type": "text","fields": {"keyword": {"type": "keyword"}}},"address": {"type": "text","fields": {"keyword": {"type": "keyword"}}},"remark": {"type": "text","fields": {"keyword": {"type": "keyword"}}},"age": {"type": "integer"},"salary": {"type": "float"},"birthDate": {"type": "date","format": "yyyy-MM-dd"},"createTime": {"type": "date"}}}}
}

删除索引

删除 mydlq-user 索引。

DELETE http://localhost:9200/testindex

2、Java 代码示例

package com.example.elasticsearch.demos.web.service.base;import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.io.IOException;/*** 索引操作*/
@Slf4j
@Service
public class IndexService {@Autowiredprivate RestHighLevelClient restHighLevelClient;/*** 验证索引是否存在*/public Object existsIndex(String indexName) {Object result = "";try {// 获取索引请求GetIndexRequest request = new GetIndexRequest();// 设置要查询的索引名称request.indices(indexName);// 执行请求,验证索引是否存在boolean isExist = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);log.info("是否存在:{}", isExist);// 根据具体业务逻辑返回不同结果,这里为了方便直接将结果返回result = isExist;} catch (IOException e) {log.error("", e);}return result;}/*** 创建索引*/public Object createIndex(String indexName) {Object result = "";try {// 创建 MappingXContentBuilder mapping = XContentFactory.jsonBuilder().startObject().field("dynamic", true).startObject("properties").startObject("name").field("type","text").startObject("fields").startObject("keyword").field("type","keyword").endObject().endObject().endObject().startObject("address").field("type","text").startObject("fields").startObject("keyword").field("type","keyword").endObject().endObject().endObject().startObject("remark").field("type","text").startObject("fields").startObject("keyword").field("type","keyword").endObject().endObject().endObject().startObject("age").field("type","integer").endObject().startObject("salary").field("type","float").endObject().startObject("birthDate").field("type","date").field("format", "yyyy-MM-dd").endObject().startObject("createTime").field("type","date").endObject().endObject().endObject();// 创建索引配置信息,配置Settings settings = Settings.builder().put("index.number_of_shards", 1).put("index.number_of_replicas", 0).build();// 新建创建索引请求对象,然后设置索引类型(ES 7.0 将不存在索引类型)和 mapping 与 index 配置CreateIndexRequest request = new CreateIndexRequest(indexName, settings);request.mapping("doc", mapping);// RestHighLevelClient 执行创建索引CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);// 判断是否创建成功boolean isCreated = createIndexResponse.isAcknowledged();log.info("是否创建成功:{}", isCreated);// 根据具体业务逻辑返回不同结果,这里为了方便直接将结果返回result = isCreated;} catch (IOException e) {log.error("", e);}return result;}/*** 删除索引*/public Object deleteIndex(String indexName) {Object result = "";try {// 新建删除索引请求对象DeleteIndexRequest request = new DeleteIndexRequest(indexName);// 执行删除索引AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);// 判断是否删除成功boolean siDeleted = acknowledgedResponse.isAcknowledged();log.info("是否删除成功:{}", siDeleted);// 根据具体业务逻辑返回不同结果,这里为了方便直接将结果返回result = siDeleted;} catch (IOException e) {log.error("", e);}return result;}}

五、文档操作示例

1、Restful 操作示例

增加文档信息

在索引 mydlq-user 中增加一条文档信息。

POST http://localhost:9200/testindex/doc
{"address": "北京市","age": 29,"birthDate": "1990-01-10","createTime": 1579530727699,"name": "张三","remark": "来自北京市的张先生","salary": 100
}//返回
{"_index": "testindex","_type": "doc","_id": "hZo5_4oBFE0BmNy_GMUN", //这个是插入生成的随机id"_version": 1,"result": "created","_shards": {"total": 1,"successful": 1,"failed": 0},"_seq_no": 29,"_primary_term": 3
}

获取文档信息

获取 testindex的索引 id=hZo5_4oBFE0BmNy_GMUN 的文档信息。

GET http://localhost:9200/testindex/doc/hZo5_4oBFE0BmNy_GMUN
//返回
{"_index": "testindex","_type": "doc","_id": "hZo5_4oBFE0BmNy_GMUN","_version": 1,"_seq_no": 29,"_primary_term": 3,"found": true,"_source": {"address": "北京市","age": 29,"birthDate": "1990-01-10","createTime": 1579530727699,"name": "张三","remark": "来自北京市的张先生","salary": 100}
}

更新文档信息

更新之前创建的 id=hZo5_4oBFE0BmNy_GMUN 的文档信息。

PUT http://localhost:9200/testindex/doc/hZo5_4oBFE0BmNy_GMUN
//请求
{"address": "北京市","age": 29,"birthDate": "1990-01-10","createTime": 1579530727699,"name": "张三(改名字)","remark": "来自北京市的张先生","salary": 100
}

删除文档信息

删除之前创建的 id=hZo5_4oBFE0BmNy_GMUN 的文档信息。

DELETE http://localhost:9200/testindex/doc/hZo5_4oBFE0BmNy_GMUN

2、Java 代码示例

package com.example.elasticsearch.demos.web.service.base;import com.alibaba.fastjson.JSON;
import com.example.elasticsearch.demos.web.model.dto.DocDto;
import com.example.elasticsearch.demos.web.model.entity.UserInfo;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.io.IOException;
import java.util.Date;/*** 文档操作*/
@Slf4j
@Service
public class DocumentService {@Autowiredprivate RestHighLevelClient restHighLevelClient;public Object existsDocument(DocDto docDto) {Object result = "";try {// 获取请求对象GetRequest getRequest = new GetRequest(docDto.getIndexName(), docDto.getDocId());// 是否获取源码内容getRequest.fetchSourceContext(new FetchSourceContext(false));// 执行请求,验证文档是否存在boolean isExist = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);log.info("文档是否存在:{}", isExist);// 根据具体业务逻辑返回不同结果,这里为了方便直接将结果返回result = isExist;} catch (IOException e) {log.error("", e);}return result;}public Object getDocument(DocDto docDto) {Object result = "";try {// 获取请求对象GetRequest getRequest = new GetRequest(docDto.getIndexName(), docDto.getDocId());// 获取文档信息GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);// 将 JSON 转换成对象if (getResponse.isExists()) {UserInfo userInfo = JSON.parseObject(getResponse.getSourceAsBytes(), UserInfo.class);log.info("用户信息:{}", userInfo);}// 根据具体业务逻辑返回不同结果,这里为了方便直接将结果返回result = getResponse;} catch (IOException e) {log.error("", e);}return result;}public Object addDocument(DocDto docDto) {Object result = "";try {// 创建索引请求对象IndexRequest indexRequest = new IndexRequest(docDto.getIndexName());// 创建用户信息UserInfo userInfo = new UserInfo();userInfo.setName(docDto.getName());userInfo.setAge(docDto.getAge());userInfo.setSalary(docDto.getSalary());userInfo.setAddress(docDto.getAddress());userInfo.setRemark(docDto.getRemark());userInfo.setCreateTime(new Date());userInfo.setBirthDate(docDto.getBirthDate());// 将对象转换为 byte 数组byte[] json = JSON.toJSONBytes(userInfo);// 设置文档内容indexRequest.source(json, XContentType.JSON);// 执行增加文档IndexResponse response = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);log.info("创建状态:{}", response.status());// 根据具体业务逻辑返回不同结果,这里为了方便直接将结果返回result = response;} catch (Exception e) {log.error("", e);}return result;}public Object updateDocument(DocDto docDto) {Object result = "";try {// 创建索引请求对象UpdateRequest updateRequest = new UpdateRequest(docDto.getIndexName(), docDto.getDocId());
//            UpdateRequest updateRequest = new UpdateRequest(docDto.getIndexName(), "doc", docDto.getDocId());// 设置用户更新信息UserInfo userInfo = new UserInfo();userInfo.setSalary(docDto.getSalary());userInfo.setAddress(docDto.getAddress());// 将对象转换为 byte 数组byte[] json = JSON.toJSONBytes(userInfo);// 设置更新文档内容updateRequest.doc(json, XContentType.JSON);// 执行更新文档UpdateResponse response = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);log.info("创建状态:{}", response.status());// 根据具体业务逻辑返回不同结果,这里为了方便直接将结果返回result = response;} catch (Exception e) {log.error("", e);}return result;}public Object deleteDocument(DocDto docDto) {Object result = "";try {// 创建删除请求对象DeleteRequest deleteRequest = new DeleteRequest(docDto.getIndexName(), docDto.getDocId());// 执行删除文档DeleteResponse response = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);log.info("删除状态:{}", response.status());// 根据具体业务逻辑返回不同结果,这里为了方便直接将结果返回result = response;} catch (IOException e) {log.error("", e);}return result;}}

六、插入初始化数据

执行查询示例前,先往索引中插入一批数据:

1、单条插入

POST http://localhost:9200/testindex/doc
//请求
{"name": "零零","address": "北京市丰台区","remark": "低层员工","age": 29,"salary": 3000,"birthDate": "1990-11-11","createTime": "2019-11-11T08:18:00.000Z"
}

2、批量插入

POST http://localhost:9200/_bulk
//header
Content-Type: application/json
//body
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"刘一","address":"北京市丰台区","remark":"低层员工","age":30,"salary":3000,"birthDate":"1989-11-11","createTime":"2019-03-15T08:18:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"陈二","address":"北京市昌平区","remark":"中层员工","age":27,"salary":7900,"birthDate":"1992-01-25","createTime":"2019-11-08T11:15:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}} 
{"name":"张三","address":"北京市房山区","remark":"中层员工","age":28,"salary":8800,"birthDate":"1991-10-05","createTime":"2019-07-22T13:22:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"李四","address":"北京市大兴区","remark":"高层员工","age":26,"salary":9000,"birthDate":"1993-08-18","createTime":"2019-10-17T15:00:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"王五","address":"北京市密云区","remark":"低层员工","age":31,"salary":4800,"birthDate":"1988-07-20","createTime":"2019-05-29T09:00:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"赵六","address":"北京市通州区","remark":"中层员工","age":32,"salary":6500,"birthDate":"1987-06-02","createTime":"2019-12-10T18:00:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"孙七","address":"北京市朝阳区","remark":"中层员工","age":33,"salary":7000,"birthDate":"1986-04-15","createTime":"2019-06-06T13:00:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"周八","address":"北京市西城区","remark":"低层员工","age":32,"salary":5000,"birthDate":"1987-09-26","createTime":"2019-01-26T14:00:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"吴九","address":"北京市海淀区","remark":"高层员工","age":30,"salary":11000,"birthDate":"1989-11-25","createTime":"2019-09-07T13:34:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"郑十","address":"北京市东城区","remark":"低层员工","age":29,"salary":5000,"birthDate":"1990-12-25","createTime":"2019-03-06T12:08:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"萧十一","address":"北京市平谷区","remark":"低层员工","age":29,"salary":3300,"birthDate":"1990-11-11","createTime":"2019-03-10T08:17:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}} 
{"name":"曹十二","address":"北京市怀柔区","remark":"中层员工","age":27,"salary":6800,"birthDate":"1992-01-25","createTime":"2019-12-03T11:09:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"吴十三","address":"北京市延庆区","remark":"中层员工","age":25,"salary":7000,"birthDate":"1994-10-05","createTime":"2019-07-27T14:22:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"冯十四","address":"北京市密云区","remark":"低层员工","age":25,"salary":3000,"birthDate":"1994-08-18","createTime":"2019-04-22T15:00:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"蒋十五","address":"北京市通州区","remark":"低层员工","age":31,"salary":2800,"birthDate":"1988-07-20","createTime":"2019-06-13T10:00:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"苗十六","address":"北京市门头沟区","remark":"高层员工","age":32,"salary":11500,"birthDate":"1987-06-02","createTime":"2019-11-11T18:00:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"鲁十七","address":"北京市石景山区","remark":"高员工","age":33,"salary":9500,"birthDate":"1986-04-15","createTime":"2019-06-06T14:00:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"沈十八","address":"北京市朝阳区","remark":"中层员工","age":31,"salary":8300,"birthDate":"1988-09-26","createTime":"2019-09-25T14:00:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"吕十九","address":"北京市西城区","remark":"低层员工","age":31,"salary":4500,"birthDate":"1988-11-25","createTime":"2019-09-22T13:34:00.000Z"} 
{"index":{"_index":"testindex","_type":"doc"}}
{"name":"丁二十","address":"北京市东城区","remark":"低层员工","age":33,"salary":2100,"birthDate":"1986-12-25","createTime":"2019-03-07T12:08:00.000Z"}

3、查询数据

插入完成后再查询数据,查看之前插入的数据是否存在:

GET http://localhost:9200/testindex/_search
//返回
{"took": 6,"timed_out": false,"_shards": {"total": 1,"successful": 1,"skipped": 0,"failed": 0},"hits": {"total": {"value": 2,"relation": "eq"},"max_score": 2.302585,"hits": [{"_index": "testindex","_type": "doc","_id": "3iDh-IoByPOFA_QWinlo","_score": 2.302585,"_source": {"name": "赵六","address": "北京市通州区","remark": "中层员工","age": 32,"salary": 6500,"birthDate": "1987-06-02","createTime": "2019-12-10T18:00:00.000Z"}},{"_index": "testindex","_type": "doc","_id": "5yDh-IoByPOFA_QWinlo","_score": 2.302585,"_source": {"name": "蒋十五","address": "北京市通州区","remark": "低层员工","age": 31,"salary": 2800,"birthDate": "1988-07-20","createTime": "2019-06-13T10:00:00.000Z"}}...]}
}

七、查询操作示例

1、精确查询(term)

(1)、Restful 操作示例

精确查询

精确查询,查询地址为 北京市通州区 的人员信息:

查询条件不会进行分词,但是查询内容可能会分词,导致查询不到。之前在创建索引时设置 Mapping 中 address 字段存在 keyword 字段是专门用于不分词查询的子字段。

GET http://localhost:9200/testindex/_search
//请求
{"query": {"term": {"address.keyword": {"value": "北京市通州区"}}}
}
//返回
{"took": 6,"timed_out": false,"_shards": {"total": 1,"successful": 1,"skipped": 0,"failed": 0},"hits": {"total": {"value": 2,"relation": "eq"},"max_score": 2.302585,"hits": [{"_index": "testindex","_type": "doc","_id": "3iDh-IoByPOFA_QWinlo","_score": 2.302585,"_source": {"name": "赵六","address": "北京市通州区","remark": "中层员工","age": 32,"salary": 6500,"birthDate": "1987-06-02","createTime": "2019-12-10T18:00:00.000Z"}},{"_index": "testindex","_type": "doc","_id": "5yDh-IoByPOFA_QWinlo","_score": 2.302585,"_source": {"name": "蒋十五","address": "北京市通州区","remark": "低层员工","age": 31,"salary": 2800,"birthDate": "1988-07-20","createTime": "2019-06-13T10:00:00.000Z"}}...]}
}
精确查询-多内容查询

精确查询,查询地址为 北京市丰台区、北京市昌平区 或 北京市大兴区 的人员信息:

 

GET http://localhost:9200/testindex/_search
//请求
{"query": {"terms": {"address.keyword": ["北京市丰台区","北京市昌平区","北京市大兴区"]}}
}

(2)、Java 代码示例 

package com.example.elasticsearch.demos.web.service.query;import com.alibaba.fastjson.JSON;
import com.example.elasticsearch.demos.web.model.dto.TermsQueryDto;
import com.example.elasticsearch.demos.web.model.entity.UserInfo;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.io.IOException;
import java.util.Arrays;/*** 精确查询*/
@Slf4j
@Service
public class TermQueryService {@Autowiredprivate RestHighLevelClient restHighLevelClient;/*** 精确查询(查询条件不会进行分词,但是查询内容可能会分词,导致查询不到)* @param queryDto*/public Object termQuery(TermsQueryDto queryDto) {Object result = "";try {// 构建查询条件(注意:termQuery 支持多种格式查询,如 boolean、int、double、string 等,这里使用的是 string 的查询)SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.termQuery(queryDto.getKey() +  ".keyword", queryDto.getValue()));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());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 转换成对象UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);// 输出查询信息log.info(userInfo.toString());}}result = searchResponse.getHits();} catch (IOException e) {log.error("", e);}return result;}/*** 多个内容在一个字段中进行查询* @param queryDto*/public Object termsQuery(TermsQueryDto queryDto) {Object result = "";try {// 构建查询条件(注意:termsQuery 支持多种格式查询,如 boolean、int、double、string 等,这里使用的是 string 的查询)SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.termsQuery(queryDto.getKey() +  ".keyword", queryDto.getValues()));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());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 转换成对象UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);// 输出查询信息log.info(userInfo.toString());}}result = searchResponse.getHits();} catch (IOException e) {log.error("", e);}return result;}}

2、匹配查询(match)

(1)、Restful 操作示例

匹配查询全部数据与分页

匹配查询符合条件的所有数据,并且设置以 salary 字段升序排序,并设置分页:

GET http://localhost:9200/testindex/_search
//请求
{"query": {"match_all": {}},"from": 0,"size": 10,"sort": [{"salary": {"order": "asc"}}]
}
匹配查询数据

匹配查询地址为 通州区 的数据:

GET http://localhost:9200/testindex/_search
//请求
{"query": {"match": {"address": "通州区"}}
}
 词语匹配查询

词语匹配进行查询,匹配 address 中为 北京市通州区 的员工信息:

GET http://localhost:9200/testindex/_search
//请求
{"query": {"match_phrase": {"address": "北京市通州区"}}
}
内容多字段查询

查询在字段 address、remark 中存在 北京 内容的员工信息:

GET http://localhost:9200/testindex/_search
//请求
{"query": {"multi_match": {"query": "北京","fields": ["address","remark"]}}
}

 (2)、Java 代码示例

package com.example.elasticsearch.demos.web.service.query;import com.alibaba.fastjson.JSON;
import com.example.elasticsearch.demos.web.model.dto.MatchQueryDto;
import com.example.elasticsearch.demos.web.model.entity.UserInfo;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.io.IOException;/*** 匹配查询*/
@Slf4j
@Service
public class MatchQueryService {@Autowiredprivate RestHighLevelClient restHighLevelClient;/*** 匹配查询符合条件的所有数据,并设置分页* @param queryDto*/public Object matchAllQuery(MatchQueryDto queryDto) {Object result = "";try {// 构建查询条件MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// 创建查询源构造器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);// 设置分页searchSourceBuilder.from((queryDto.getRows() - 1) * queryDto.getSize());searchSourceBuilder.size(queryDto.getSize());// 设置排序searchSourceBuilder.sort("salary", SortOrder.ASC);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());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 转换成对象UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);// 输出查询信息log.info(userInfo.toString());}}result = searchResponse.getHits();} catch (IOException e) {log.error("", e);}return result;}/*** 匹配查询数据-or的方式* @param queryDto*/public Object matchQuery(MatchQueryDto queryDto) {Object result = "";try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.matchQuery(queryDto.getKey(), queryDto.getValue()));
//            searchSourceBuilder.query(QueryBuilders.matchQuery("address", "通州区"));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());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 转换成对象UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);// 输出查询信息log.info(userInfo.toString());}}result = searchResponse.getHits();} catch (IOException e) {log.error("", e);}return result;}/*** 词语匹配查询* @param queryDto*/public Object matchPhraseQuery(MatchQueryDto queryDto) {Object result = "";try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.matchPhraseQuery(queryDto.getKey(), queryDto.getValue()));
//            searchSourceBuilder.query(QueryBuilders.matchPhraseQuery("address", "北京市通州区"));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());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 转换成对象UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);// 输出查询信息log.info(userInfo.toString());}}result = searchResponse.getHits();} catch (IOException e) {log.error("", e);}return result;}/*** 内容在多字段中进行查询* @param queryDto*/public Object matchMultiQuery(MatchQueryDto queryDto) {Object result = "";try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.multiMatchQuery(queryDto.getKey(), queryDto.getValues()));
//            searchSourceBuilder.query(QueryBuilders.multiMatchQuery("北京市", "address", "remark"));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());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 转换成对象UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);// 输出查询信息log.info(userInfo.toString());}}result = searchResponse.getHits();} catch (IOException e) {log.error("", e);}return result;}}

3、模糊查询(fuzzy)

(1)、Restful 操作示例

模糊查询所有以 三 结尾的姓名
GET http://localhost:9200/testindex/_search
//请求
{"query": {"fuzzy": {"name": "三"}}
}

 (2)、Java 代码示例

package com.example.elasticsearch.demos.web.service.query;import com.alibaba.fastjson.JSON;
import com.example.elasticsearch.demos.web.model.dto.MatchQueryDto;
import com.example.elasticsearch.demos.web.model.entity.UserInfo;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.io.IOException;/*** 模糊查询*/
@Slf4j
@Service
public class FuzzyQueryService {@Autowiredprivate RestHighLevelClient restHighLevelClient;/*** 模糊查询所有以 “三” 结尾的姓名* @param queryDto*/public Object fuzzyQuery(MatchQueryDto queryDto) {Object result = "";try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.fuzzyQuery(queryDto.getKey(), queryDto.getValue()).fuzziness(Fuzziness.AUTO));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());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 转换成对象UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);// 输出查询信息log.info(userInfo.toString());}}result = searchResponse.getHits();} catch (IOException e) {log.error("", e);}return result;}}

4、范围查询(range)

(1)、Restful 操作示例

查询岁数 ≥ 30 岁的员工数据:
GET http://localhost:9200/testindex/_search
//请求
{"query": {"range": {"age": {"gte": 30}}}
}
查询生日距离现在 30 年间的员工数据: 
GET http://localhost:9200/testindex/_search
//请求
{"query": {"range": {"birthDate": {"gte": "now-30y"}}}
}

(2)、Java 代码示例 

package com.example.elasticsearch.demos.web.service.query;import com.alibaba.fastjson.JSON;
import com.example.elasticsearch.demos.web.model.dto.MatchQueryDto;
import com.example.elasticsearch.demos.web.model.entity.UserInfo;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.io.IOException;/*** 范围查询*/
@Slf4j
@Service
public class RangeQueryService {@Autowiredprivate RestHighLevelClient restHighLevelClient;/*** 查询岁数 ≥ 30 岁的员工数据* @param queryDto*/public Object rangeQuery(MatchQueryDto queryDto) {Object result = "";try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.rangeQuery("age").gte(30));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());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 转换成对象UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);// 输出查询信息log.info(userInfo.toString());}}result = searchResponse.getHits();} catch (IOException e) {log.error("", e);}return result;}/*** 查询距离现在 30 年间的员工数据* [年(y)、月(M)、星期(w)、天(d)、小时(h)、分钟(m)、秒(s)]* 例如:* now-1h 查询一小时内范围* now-1d 查询一天内时间范围* now-1y 查询最近一年内的时间范围* @param queryDto*/public Object dateRangeQuery(MatchQueryDto queryDto) {Object result = "";try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//            // includeLower(是否包含下边界)、includeUpper(是否包含上边界)
//            searchSourceBuilder.query(QueryBuilders.rangeQuery("birthDate")
//                    .gte("now-30y").includeLower(true).includeUpper(true));searchSourceBuilder.query(QueryBuilders.rangeQuery("birthDate").gte(queryDto.getFrom()).lte(queryDto.getEnd()));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());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 转换成对象UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);// 输出查询信息log.info(userInfo.toString());}}result = searchResponse.getHits();} catch (IOException e) {log.error("", e);}return result;}}

5、通配符查询(wildcard)

(1)、Restful 操作示例

查询所有以 “三” 结尾的姓名:
GET http://localhost:9200/testindex/_search
//请求
{"query": {"wildcard": {"name.keyword": {"value": "*三"}}}
}

(2)、Java 代码示例 

package com.example.elasticsearch.demos.web.service.query;import com.alibaba.fastjson.JSON;
import com.example.elasticsearch.demos.web.model.dto.MatchQueryDto;
import com.example.elasticsearch.demos.web.model.entity.UserInfo;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.io.IOException;/*** 通配符查询*/
@Slf4j
@Service
public class WildcardQueryService {@Autowiredprivate RestHighLevelClient restHighLevelClient;/*** 查询所有以 “三” 结尾的姓名* <p>* *:表示多个字符(0个或多个字符)* ?:表示单个字符* @param queryDto*/public Object wildcardQuery(MatchQueryDto queryDto) {Object result = "";try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.wildcardQuery(queryDto.getKey() + ".keyword", "*" + queryDto.getValue()));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());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 转换成对象UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);// 输出查询信息log.info(userInfo.toString());}}result = searchResponse.getHits();} catch (IOException e) {log.error("", e);}return result;}}

6、布尔查询(bool)

(1)、Restful 操作示例

查询出生在 1990-1995 年期间,且地址在 北京市昌平区、北京市大兴区、北京市房山区 的员工信息:

GET http://localhost:9200/testindex/_search
//请求
{"query": {"bool": {"filter": {"range": {"birthDate": {"format": "yyyy", "gte": 1990,"lte": 1995}}},"must": [{"terms": {"address.keyword": ["北京市昌平区","北京市大兴区","北京市房山区"]}}]}}
}

 (2)、Java 代码示例

package com.example.elasticsearch.demos.web.service.query;import com.alibaba.fastjson.JSON;
import com.example.elasticsearch.demos.web.model.dto.MatchQueryDto;
import com.example.elasticsearch.demos.web.model.entity.UserInfo;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.io.IOException;/*** 布尔查询*/
@Slf4j
@Service
public class BoolQueryService {@Autowiredprivate RestHighLevelClient restHighLevelClient;/*** 布尔查询* @param queryDto*/public Object boolQuery(MatchQueryDto queryDto) {Object result = "";try {// 创建 Bool 查询构建器BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();// 构建查询条件boolQueryBuilder.must(QueryBuilders.termsQuery("address.keyword", "北京市昌平区", "北京市大兴区", "北京市房山区")).filter().add(QueryBuilders.rangeQuery("birthDate").format("yyyy").gte("1990").lte("1995"));// 构建查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(boolQueryBuilder);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());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 转换成对象UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);// 输出查询信息log.info(userInfo.toString());}}result = searchResponse.getHits();} catch (IOException e) {log.error("", e);}return result;}}

八、聚合查询操作示例

1、Metric 聚合分析

(1)、Restful 操作示例

GET http://localhost:9200/testindex/_search1、统计员工总数、工资最高值、工资最低值、工资平均工资、工资总和:
//请求
{"size": 0,"aggs": {"salary_stats": {"stats": {"field": "salary"}}}
}2、统计员工工资最低值:
//请求
{"size": 0,"aggs": {"salary_min": {"min": {"field": "salary"}}}
}3、统计员工工资最高值:
//请求
{"size": 0,"aggs": {"salary_max": {"max": {"field": "salary"}}}
}4、统计员工工资平均值:
//请求
{"size": 0,"aggs": {"salary_avg": {"avg": {"field": "salary"}}}
}5、统计员工工资总值:
//请求
{"size": 0,"aggs": {"salary_sum": {"sum": {"field": "salary"}}}
}6、统计员工总数:
//请求
{"size": 0,"aggs": {"employee_count": {"value_count": {"field": "salary"}}}
}7、统计员工工资百分位:
//请求
{"size": 0,"aggs": {"salary_percentiles": {"percentiles": {"field": "salary"}}}
}

(2)、Java 代码示例

package com.example.elasticsearch.demos.web.service.aggregation;import com.example.elasticsearch.demos.web.model.dto.MatchQueryDto;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.metrics.*;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;import java.io.IOException;/*** 聚合 Metric*/
@Slf4j
@Service
public class AggrMetricService {@Autowiredprivate RestHighLevelClient restHighLevelClient;@Value("${myindex}")private String indexName;/*** stats 统计员工总数、工资最高值、工资最低值、工资平均工资、工资总和* @param queryDto*/public Object aggregationStats(MatchQueryDto queryDto) {String responseResult = "";try {// 设置聚合条件String field = queryDto.getKey();AggregationBuilder aggr = AggregationBuilders.stats(field + "_stats").field(field);// 查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.aggregation(aggr);// 设置查询结果不返回,只返回聚合结果searchSourceBuilder.size(0);// 创建查询请求对象,将查询条件配置到其中SearchRequest request = new SearchRequest(indexName);request.source(searchSourceBuilder);// 执行请求SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);// 获取响应中的聚合信息Aggregations aggregations = response.getAggregations();// 输出内容if (RestStatus.OK.equals(response.status()) || aggregations != null) {// 转换为 Stats 对象ParsedStats aggregation = aggregations.get(field + "_stats");log.info("-------------------------------------------");log.info("聚合信息: {}", field);log.info("count:{}", aggregation.getCount());log.info("avg:{}", aggregation.getAvg());log.info("max:{}", aggregation.getMax());log.info("min:{}", aggregation.getMin());log.info("sum:{}", aggregation.getSum());log.info("-------------------------------------------");}// 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串responseResult = response.toString();} catch (IOException e) {log.error("", e);}return responseResult;}/*** min 统计员工工资最低值*/public Object aggregationMin() {String responseResult = "";try {// 设置聚合条件AggregationBuilder aggr = AggregationBuilders.min("salary_min").field("salary");// 查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.aggregation(aggr);searchSourceBuilder.size(0);// 创建查询请求对象,将查询条件配置到其中SearchRequest request = new SearchRequest(indexName);request.source(searchSourceBuilder);// 执行请求SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);// 获取响应中的聚合信息Aggregations aggregations = response.getAggregations();// 输出内容if (RestStatus.OK.equals(response.status()) || aggregations != null) {// 转换为 Min 对象ParsedMin aggregation = aggregations.get("salary_min");log.info("-------------------------------------------");log.info("聚合信息:");log.info("min:{}", aggregation.getValue());log.info("-------------------------------------------");}// 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串responseResult = response.toString();} catch (IOException e) {log.error("", e);}return responseResult;}/*** max 统计员工工资最高值*/public Object aggregationMax() {String responseResult = "";try {// 设置聚合条件AggregationBuilder aggr = AggregationBuilders.max("salary_max").field("salary");// 查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.aggregation(aggr);searchSourceBuilder.size(0);// 创建查询请求对象,将查询条件配置到其中SearchRequest request = new SearchRequest(indexName);request.source(searchSourceBuilder);// 执行请求SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);// 获取响应中的聚合信息Aggregations aggregations = response.getAggregations();// 输出内容if (RestStatus.OK.equals(response.status()) || aggregations != null) {// 转换为 Max 对象ParsedMax aggregation = aggregations.get("salary_max");log.info("-------------------------------------------");log.info("聚合信息:");log.info("max:{}", aggregation.getValue());log.info("-------------------------------------------");}// 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串responseResult = response.toString();} catch (IOException e) {log.error("", e);}return responseResult;}/*** avg 统计员工工资平均值*/public Object aggregationAvg() {String responseResult = "";try {// 设置聚合条件AggregationBuilder aggr = AggregationBuilders.avg("salary_avg").field("salary");// 查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.aggregation(aggr);searchSourceBuilder.size(0);// 创建查询请求对象,将查询条件配置到其中SearchRequest request = new SearchRequest(indexName);request.source(searchSourceBuilder);// 执行请求SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);// 获取响应中的聚合信息Aggregations aggregations = response.getAggregations();// 输出内容if (RestStatus.OK.equals(response.status()) || aggregations != null) {// 转换为 Avg 对象ParsedAvg aggregation = aggregations.get("salary_avg");log.info("-------------------------------------------");log.info("聚合信息:");log.info("avg:{}", aggregation.getValue());log.info("-------------------------------------------");}// 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串responseResult = response.toString();} catch (IOException e) {log.error("", e);}return responseResult;}/*** sum 统计员工工资总值*/public Object aggregationSum() {String responseResult = "";try {// 设置聚合条件SumAggregationBuilder aggr = AggregationBuilders.sum("salary_sum").field("salary");// 查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.aggregation(aggr);searchSourceBuilder.size(0);// 创建查询请求对象,将查询条件配置到其中SearchRequest request = new SearchRequest(indexName);request.source(searchSourceBuilder);// 执行请求SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);// 获取响应中的聚合信息Aggregations aggregations = response.getAggregations();// 输出内容if (RestStatus.OK.equals(response.status()) || aggregations != null) {// 转换为 Sum 对象ParsedSum aggregation = aggregations.get("salary_sum");log.info("-------------------------------------------");log.info("聚合信息:");log.info("sum:{}", String.valueOf((aggregation.getValue())));log.info("-------------------------------------------");}// 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串responseResult = response.toString();} catch (IOException e) {log.error("", e);}return responseResult;}/*** count 统计员工总数*/public Object aggregationCount() {String responseResult = "";try {// 设置聚合条件AggregationBuilder aggr = AggregationBuilders.count("employee_count").field("salary");// 查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.aggregation(aggr);searchSourceBuilder.size(0);// 创建查询请求对象,将查询条件配置到其中SearchRequest request = new SearchRequest(indexName);request.source(searchSourceBuilder);// 执行请求SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);// 获取响应中的聚合信息Aggregations aggregations = response.getAggregations();// 输出内容if (RestStatus.OK.equals(response.status()) || aggregations != null) {// 转换为 ValueCount 对象ParsedValueCount aggregation = aggregations.get("employee_count");log.info("-------------------------------------------");log.info("聚合信息:");log.info("count:{}", aggregation.getValue());log.info("-------------------------------------------");}// 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串responseResult = response.toString();} catch (IOException e) {log.error("", e);}return responseResult;}/*** percentiles 统计员工工资百分位*/public Object aggregationPercentiles() {String responseResult = "";try {// 设置聚合条件AggregationBuilder aggr = AggregationBuilders.percentiles("salary_percentiles").field("salary");// 查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.aggregation(aggr);searchSourceBuilder.size(0);// 创建查询请求对象,将查询条件配置到其中SearchRequest request = new SearchRequest(indexName);request.source(searchSourceBuilder);// 执行请求SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);// 获取响应中的聚合信息Aggregations aggregations = response.getAggregations();// 输出内容if (RestStatus.OK.equals(response.status()) || aggregations != null) {// 转换为 Percentiles 对象ParsedPercentiles aggregation = aggregations.get("salary_percentiles");log.info("-------------------------------------------");log.info("聚合信息:");for (Percentile percentile : aggregation) {log.info("百分位:{}:{}", percentile.getPercent(), percentile.getValue());}log.info("-------------------------------------------");}// 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串responseResult = response.toString();} catch (IOException e) {log.error("", e);}return responseResult;}}

2、Bucket 聚合分析

(1)、Restful 操作示例

GET http://localhost:9200/testindex/_search1、按岁数进行聚合分桶,统计各个岁数员工的人数:
//请求
{"size": 0,"aggs": {"age_bucket": {"terms": {"field": "age","size": "10"}}}
}2、按工资范围进行聚合分桶,统计工资在 3000-5000、5000-9000 和 9000 以上的员工信息:
//请求
{"aggs": {"salary_range_bucket": {"range": {"field": "salary","ranges": [{"key": "低级员工", "to": 3000},{"key": "中级员工","from": 5000,"to": 9000},{"key": "高级员工","from": 9000}]}}}
}3、按照时间范围进行分桶,统计 1985-1990 年和 1990-1995 年出生的员工信息:
//请求
{"size": 10,"aggs": {"date_range_bucket": {"date_range": {"field": "birthDate","format": "yyyy", "ranges": [{"key": "出生日期1985-1990的员工", "from": "1985","to": "1990"},{"key": "出生日期1990-1995的员工", "from": "1990","to": "1995"}]}}}
}4、按工资多少进行聚合分桶,设置统计的最小值为 0,最大值为 12000,区段间隔为 3000:
//请求
{"size": 0,"aggs": {"salary_histogram": {"histogram": {"field": "salary","extended_bounds": {"min": 0,"max": 12000}, "interval": 3000}}}
}5、按出生日期进行分桶:
//请求
{"size": 0,"aggs": {"birthday_histogram": {"date_histogram": {"format": "yyyy", "field": "birthDate","interval": "year"}}}
}

(2)、Java 代码示例 

package com.example.elasticsearch.demos.web.service.aggregation;import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.aggregations.bucket.range.Range;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 聚合 Bucket*/
@Slf4j
@Service
public class AggrBucketService {@Autowiredprivate RestHighLevelClient restHighLevelClient;@Value("${myindex}")private String indexName;/*** 按岁数进行聚合分桶,统计各个岁数员工的人数:*/public Object aggrBucketTerms() {Map<String, Long> keyCountMap = new HashMap<>();try {AggregationBuilder aggr = AggregationBuilders.terms("age_bucket").field("age");// 查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.size(10);searchSourceBuilder.aggregation(aggr);// 创建查询请求对象,将查询条件配置到其中SearchRequest request = new SearchRequest(indexName);request.source(searchSourceBuilder);// 执行请求SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);// 获取响应中的聚合信息Aggregations aggregations = response.getAggregations();// 输出内容if (RestStatus.OK.equals(response.status())) {// 分桶Terms byCompanyAggregation = aggregations.get("age_bucket");List<? extends Terms.Bucket> buckets = byCompanyAggregation.getBuckets();// 输出各个桶的内容log.info("-------------------------------------------");log.info("聚合信息:");for (Terms.Bucket bucket : buckets) {keyCountMap.put(bucket.getKeyAsString(),  bucket.getDocCount());log.info("桶名:{} | 总数:{}", bucket.getKeyAsString(), bucket.getDocCount());}log.info("-------------------------------------------");}} catch (IOException e) {log.error("", e);}return keyCountMap;}/*** 按工资范围进行聚合分桶,统计工资在 3000-5000、5000-9000 和 9000 以上的员工信息:*/public Object aggrBucketRange() {Map<String, Long> keyCountMap = new HashMap<>();try {AggregationBuilder aggr = AggregationBuilders.range("salary_range_bucket").field("salary").addUnboundedTo("低级员工", 3000).addRange("中级员工", 5000, 9000).addUnboundedFrom("高级员工", 9000);// 查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.size(0);searchSourceBuilder.aggregation(aggr);// 创建查询请求对象,将查询条件配置到其中SearchRequest request = new SearchRequest(indexName);request.source(searchSourceBuilder);// 执行请求SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);// 获取响应中的聚合信息Aggregations aggregations = response.getAggregations();// 输出内容if (RestStatus.OK.equals(response.status())) {// 分桶Range byCompanyAggregation = aggregations.get("salary_range_bucket");List<? extends Range.Bucket> buckets = byCompanyAggregation.getBuckets();// 输出各个桶的内容log.info("-------------------------------------------");log.info("聚合信息:");for (Range.Bucket bucket : buckets) {keyCountMap.put(bucket.getKeyAsString(),  bucket.getDocCount());log.info("桶名:{} | 总数:{}", bucket.getKeyAsString(), bucket.getDocCount());}log.info("-------------------------------------------");}} catch (IOException e) {log.error("", e);}return keyCountMap;}/*** 按照时间范围进行分桶,统计 1985-1990 年和 1990-1995 年出生的员工信息:*/public Object aggrBucketDateRange() {Map<String, Long> keyCountMap = new HashMap<>();try {AggregationBuilder aggr = AggregationBuilders.dateRange("date_range_bucket").field("birthDate").format("yyyy").addRange("出生日期1985-1990的员工", "1985", "1990").addRange("出生日期1990-1995的员工", "1990", "1995");// 查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.size(0);searchSourceBuilder.aggregation(aggr);// 创建查询请求对象,将查询条件配置到其中SearchRequest request = new SearchRequest(indexName);request.source(searchSourceBuilder);// 执行请求SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);// 获取响应中的聚合信息Aggregations aggregations = response.getAggregations();// 输出内容if (RestStatus.OK.equals(response.status())) {// 分桶Range byCompanyAggregation = aggregations.get("date_range_bucket");List<? extends Range.Bucket> buckets = byCompanyAggregation.getBuckets();// 输出各个桶的内容log.info("-------------------------------------------");log.info("聚合信息:");for (Range.Bucket bucket : buckets) {keyCountMap.put(bucket.getKeyAsString(),  bucket.getDocCount());log.info("桶名:{} | 总数:{}", bucket.getKeyAsString(), bucket.getDocCount());}log.info("-------------------------------------------");}} catch (IOException e) {log.error("", e);}return keyCountMap;}/*** 按工资多少进行聚合分桶*/public Object aggrBucketHistogram() {Map<String, Long> keyCountMap = new HashMap<>();try {//按工资多少进行聚合分桶,设置统计的最小值为 0,最大值为 12000,区段间隔为 3000:AggregationBuilder aggr = AggregationBuilders.histogram("salary_histogram").field("salary").extendedBounds(0, 12000).interval(3000);// 查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.size(0);searchSourceBuilder.aggregation(aggr);// 创建查询请求对象,将查询条件配置到其中SearchRequest request = new SearchRequest(indexName);request.source(searchSourceBuilder);// 执行请求SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);// 获取响应中的聚合信息Aggregations aggregations = response.getAggregations();// 输出内容if (RestStatus.OK.equals(response.status())) {// 分桶Histogram byCompanyAggregation = aggregations.get("salary_histogram");List<? extends Histogram.Bucket> buckets = byCompanyAggregation.getBuckets();// 输出各个桶的内容log.info("-------------------------------------------");log.info("聚合信息:");for (Histogram.Bucket bucket : buckets) {keyCountMap.put(bucket.getKeyAsString(),  bucket.getDocCount());log.info("桶名:{} | 总数:{}", bucket.getKeyAsString(), bucket.getDocCount());}log.info("-------------------------------------------");}} catch (IOException e) {log.error("", e);}return keyCountMap;}/*** 按出生日期进行分桶:*/public Object aggrBucketDateHistogram() {Map<String, Long> keyCountMap = new HashMap<>();try {AggregationBuilder aggr = AggregationBuilders.dateHistogram("birthday_histogram").field("birthDate").interval(1).dateHistogramInterval(DateHistogramInterval.YEAR).format("yyyy");// 查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.size(0);searchSourceBuilder.aggregation(aggr);// 创建查询请求对象,将查询条件配置到其中SearchRequest request = new SearchRequest(indexName);request.source(searchSourceBuilder);// 执行请求SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);// 获取响应中的聚合信息Aggregations aggregations = response.getAggregations();// 输出内容if (RestStatus.OK.equals(response.status())) {// 分桶Histogram byCompanyAggregation = aggregations.get("birthday_histogram");List<? extends Histogram.Bucket> buckets = byCompanyAggregation.getBuckets();// 输出各个桶的内容log.info("-------------------------------------------");log.info("聚合信息:");for (Histogram.Bucket bucket : buckets) {keyCountMap.put(bucket.getKeyAsString(),  bucket.getDocCount());log.info("桶名:{} | 总数:{}", bucket.getKeyAsString(), bucket.getDocCount());}log.info("-------------------------------------------");}} catch (IOException e) {log.error("", e);}return keyCountMap;}}

3、Metric 与 Bucket 聚合分析

(1)、Restful 操作示例

按照员工岁数分桶、然后统计每个岁数员工工资最高值:

GET http://localhost:9200/testindex/_search
//请求
{"size": 0,"aggs": {"salary_bucket": {"terms": {"field": "age","size": "10"},"aggs": {"salary_max_user": {"top_hits": {"size": 1,"sort": [{"salary": {"order": "desc"}}]}}}}}
}

(2)、Java 代码示例

package com.example.elasticsearch.demos.web.service.aggregation;import com.alibaba.fastjson.JSON;
import com.example.elasticsearch.demos.web.model.entity.UserInfo;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.metrics.ParsedTopHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 聚合 Bucket 与 Metric*/
@Slf4j
@Service
public class AggrBucketMetricService {@Autowiredprivate RestHighLevelClient restHighLevelClient;@Value("${myindex}")private String indexName;/*** topHits 按照员工岁数分桶、然后统计每个岁数员工工资最高值*/public Object aggregationTopHits() {Map<String, Float> ageMaxSalaryMap = new HashMap<>();try {AggregationBuilder testTop = AggregationBuilders.topHits("salary_max_user").size(1).sort("salary", SortOrder.DESC);AggregationBuilder salaryBucket = AggregationBuilders.terms("salary_bucket").field("age").size(10);salaryBucket.subAggregation(testTop);// 查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.size(0);searchSourceBuilder.aggregation(salaryBucket);// 创建查询请求对象,将查询条件配置到其中SearchRequest request = new SearchRequest(indexName);request.source(searchSourceBuilder);// 执行请求SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);// 获取响应中的聚合信息Aggregations aggregations = response.getAggregations();// 输出内容if (RestStatus.OK.equals(response.status())) {// 分桶Terms byCompanyAggregation = aggregations.get("salary_bucket");List<? extends Terms.Bucket> buckets = byCompanyAggregation.getBuckets();// 输出各个桶的内容log.info("-------------------------------------------");log.info("聚合信息:");for (Terms.Bucket bucket : buckets) {log.info("桶名:{}", bucket.getKeyAsString());ParsedTopHits topHits = bucket.getAggregations().get("salary_max_user");for (SearchHit hit : topHits.getHits()) {UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);ageMaxSalaryMap.put(bucket.getKeyAsString(),  userInfo.getSalary());log.info(hit.getSourceAsString());}}log.info("-------------------------------------------");}} catch (IOException e) {log.error("", e);}return ageMaxSalaryMap;}}

九、项目源码及对应ES安装包

1、elasticsearch-7.6.1安装包

elasticsearch7.6.1icon-default.png?t=N7T8https://download.csdn.net/download/asd051377305/88397087

2、项目源代码

基于SpringBoot+elasticsearch的操作项目icon-default.png?t=N7T8https://download.csdn.net/download/asd051377305/88397090

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/148844.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

天地无用 - 修改朋友圈的定位: 高德地图 + 爱思助手

1&#xff0c;电脑上打开高德地图网页版 高德地图 (amap.com) 2&#xff0c;网页最下一栏&#xff0c;点击“开放平台” 高德开放平台 | 高德地图API (amap.com) 3&#xff0c;在新网页中&#xff0c;需要登录高德账户才能操作。 可以使用手机号和验证码登录。 4&#xff0c…

经典算法-----汉诺塔问题

前言 今天我们学习一个老经典的问题-----汉诺塔问题&#xff0c;可能在学习编程之前我们就听说过这个问题&#xff0c;那这里我们如何去通过编程的方式去解决这么一个问题呢&#xff1f;下面接着看。 汉诺塔问题 问题描述 这里是引用汉诺塔问题源自印度一个古老的传说&#x…

微信公众号模板消息First,Remark字段不显示,备注字段不见了

今天在开发公众号过程中有个需求发模板消息我设置的如下 成绩单打印通知&#xff01;姓名&#xff1a;{{name.DATA}} 学号&#xff1a;{{stuid.DATA}}状态&#xff1a;{{status.DATA}}时间&#xff1a;{{date.DATA}} 备注&#xff1a;{{remark.DATA}} 然后发完通知发现《…

AtCoder Beginner Contest 232(A-G)

A - QQ solver (atcoder.jp)直接按题意模拟即可。 B - Caesar Cipher (atcoder.jp)按题意模拟即可 C - Graph Isomorphism (atcoder.jp)按题意模拟即可 D - Weak Takahashi (atcoder.jp) 一个非常套路的网格dp E - Rook Path (atcoder.jp) &#xff08;1&#xff09;题意 有…

Python的NumPy库(一)基础用法

NumPy库并不是Python的标准库&#xff0c;但其在机器学习、大数据等很多领域有非常广泛的应用&#xff0c;NumPy本身就有比较多的内容&#xff0c;全部的学习可能涉及许多的内容&#xff0c;但我们在这里仅学习常见的使用&#xff0c;这些内容对于我们日常使用NumPy是足够的。 …

5个适合初学者的初级网络安全工作,网络安全就业必看

前言 网络安全涉及保护计算机系统、网络和数据免受未经授权的访问、破坏和盗窃 - 防止数字活动和数据访问的中断 - 同时也保护用户的资产和隐私。鉴于公共事业、医疗保健、金融以及联邦政府等行业的网络犯罪攻击不断升级&#xff0c;对网络专业人员的需求很高&#xff0c;这并…

Linux系统编程系列之线程池

Linux系统编程系列&#xff08;16篇管饱&#xff0c;吃货都投降了&#xff01;&#xff09; 1、Linux系统编程系列之进程基础 2、Linux系统编程系列之进程间通信(IPC)-信号 3、Linux系统编程系列之进程间通信(IPC)-管道 4、Linux系统编程系列之进程间通信-IPC对象 5、Linux系统…

IPsec_SSL VPN身份鉴别过程简要

一、IPsec VPN身份鉴别&#xff08;参考国密标准《GMT 0022-2014 IPsec VPN技术规范》&#xff09; IKE第一阶段&#xff08;主模式&#xff09; “消息2”由响应方发出&#xff0c;消息中具体包含一个SA载荷&#xff08;确认所接受的SA提议&#xff09;、响应方的签名证书和…

Jmeter排查正则表达式提取器未生效问题

今天在使用Jmeter的时候遇到一个很简单的问题&#xff0c;使用正则表达式提取token一直未生效&#xff0c;原因是正则表达式中多了一个空格。虽然问题很简单&#xff0c;但是觉得排查问题的方法很普适&#xff0c;所以记录下&#xff0c;也希望能够给遇到问题的大家一个参考。 …

制作 3 档可调灯程序编写

PWM 0~255 可以将数据映射到0 75 150 225 尽可能均匀电压间隔

很普通的四非生,保研破局经验贴

推免之路 个人情况简介夏令营深圳大学情况机试面试结果 预推免湖南师范大学面试结果 安徽大学面试结果 北京科技大学笔试面试结果 合肥工业大学南京航空航天大学面试结果 暨南大学东北大学 最终结果一些建议写在后面 个人情况简介 教育水平&#xff1a;某中医药院校的医学信息…

计算机网络(第8版)-第4章 网络层

4.1 网络层的几个重要概念 4.1.1 网络层提供的两种服务 如果主机&#xff08;即端系统&#xff09;进程之间需要进行可靠的通信&#xff0c;那么就由主机中的运输层负责&#xff08;包括差错处理、流量控制等&#xff09;。 4.1.2 网络层的两个层面 4.2 网际协议 IP 图4-4 网…

PyTorch实例:简单线性回归的训练和反向传播解析

文章目录 &#x1f966;引言&#x1f966;什么是反向传播&#xff1f;&#x1f966;反向传播的实现&#xff08;代码&#xff09;&#x1f966;反向传播在深度学习中的应用&#x1f966;链式求导法则&#x1f966;总结 &#x1f966;引言 在神经网络中&#xff0c;反向传播算法…

【Java 进阶篇】JDBC 数据库连接池 C3P0 详解

数据库连接池是数据库编程中常用的一种技术&#xff0c;它可以有效地管理数据库连接&#xff0c;提高数据库访问的性能和效率。在 Java 编程中&#xff0c;有多种数据库连接池可供选择&#xff0c;其中之一就是 C3P0。本文将详细介绍 C3P0 数据库连接池的使用&#xff0c;包括原…

linux内核分析:网络协议栈

从本质上来讲,所谓的建立连接,其实是为了在客户端和服务端维护连接,而建立一定的数据结构来维护双方交互的状态,并用这样的数据结构来保证面向连接的特性。TCP 无法左右中间的任何通路,也没有什么虚拟的连接,中间的通路根本意识不到两端使用了 TCP 还是 UDP。 所谓的连接…

redis持久化与调优

一 、Redis 高可用&#xff1a; 在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务&#xff08;99.9%、99.99%、99.999%等等&#xff09;。但是在Redis语境中&#xff0c;高可用的含义似乎要宽泛一些&#x…

OpenCV利用Camshift实现目标追踪

目录 原理 做法 代码实现 结果展示 原理 做法 代码实现 import numpy as np import cv2 as cv# 读取视频 cap cv.VideoCapture(video.mp4)# 检查视频是否成功打开 if not cap.isOpened():print("Error: Cannot open video file.")exit()# 获取第一帧图像&#x…

【赠书活动第3期】《构建新型网络形态下的网络空间安全体系》——用“价值”的视角来看安全

目录 一、内容简介二、读者受众三、图书目录四、编辑推荐五、获奖名单 一、内容简介 经过30多年的发展&#xff0c;安全已经深入到信息化的方方面面&#xff0c;形成了一个庞大的产业和复杂的理论、技术和产品体系。 因此&#xff0c;需要站在网络空间的高度看待安全与网络的…

UE5报错及解决办法

1、编译报错&#xff0c;内容如下&#xff1a; Unable to build while Live Coding is active. Exit the editor and game, or press CtrlAltF11 if iterating on code in the editor or game 解决办法 取消Enable Live Coding勾选

企业部署,springboot+vue+vue,Linux上部署mysql与redis,docker中部署nginx,jenkins。完整详细。

企业项目部署全流程笔记 前言 涉及&#xff1a;Linux服务器&#xff0c;docker&#xff0c;Jenkins&#xff0c;nginx&#xff0c;springoot&#xff0c;vue&#xff0c;mysql&#xff0c;redis&#xff0c;git&#xff0c; docker生成容器类型&#xff1a;MySql&#xff0c…