• 基于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 客户端。
    1. "1.0" encoding="UTF-8"?>
    2. "http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    4. 4.0.0
    5. com.example
    6. elasticsearch
    7. 0.0.1-SNAPSHOT
    8. elasticsearch
    9. Demo project for Spring Boot
    10. 1.8
    11. UTF-8
    12. UTF-8
    13. 2.3.12.RELEASE
    14. org.springframework.boot
    15. spring-boot-starter-web
    16. org.springframework.boot
    17. spring-boot-starter-test
    18. test
    19. org.junit.vintage
    20. junit-vintage-engine
    21. org.projectlombok
    22. lombok
    23. true
    24. com.alibaba
    25. fastjson
    26. 1.2.61
    27. org.elasticsearch.client
    28. elasticsearch-rest-high-level-client
    29. 7.6.1
    30. org.elasticsearch
    31. elasticsearch
    32. 7.6.1
    33. org.springframework.boot
    34. spring-boot-dependencies
    35. ${spring-boot.version}
    36. pom
    37. import
    38. org.apache.maven.plugins
    39. maven-compiler-plugin
    40. 3.8.1
    41. 1.8
    42. 1.8
    43. UTF-8
    44. org.springframework.boot
    45. spring-boot-maven-plugin
    46. ${spring-boot.version}
    47. com.example.elasticsearch.ElasticsearchApplication
    48. true
    49. repackage
    50. repackage

    2、ElasticSearch 连接配置

    (1)、application.yml 配置文件

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

    1. server:
    2. port: 8080
    3. servlet:
    4. context-path: /search
    5. elasticsearch:
    6. schema: http
    7. address: 127.0.0.1:9200
    8. connectTimeout: 10000
    9. socketTimeout: 10000
    10. connectionRequestTimeout: 10000
    11. maxConnectNum: 100
    12. maxConnectPerRoute: 100
    13. myindex: testindex

    (2)、java 连接配置类

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

    1. package com.example.elasticsearch.demos.config;
    2. import org.apache.http.HttpHost;
    3. import org.elasticsearch.client.RestClient;
    4. import org.elasticsearch.client.RestClientBuilder;
    5. import org.elasticsearch.client.RestHighLevelClient;
    6. import org.springframework.beans.factory.annotation.Value;
    7. import org.springframework.context.annotation.Bean;
    8. import org.springframework.context.annotation.Configuration;
    9. import java.util.ArrayList;
    10. import java.util.List;
    11. /**
    12. * ElasticSearch 配置
    13. */
    14. @Configuration
    15. public class ElasticSearchConfig {
    16. /** 协议 */
    17. @Value("${elasticsearch.schema:http}")
    18. private String schema;
    19. /** 集群地址,如果有多个用“,”隔开 */
    20. @Value("${elasticsearch.address}")
    21. private String address;
    22. /** 连接超时时间 */
    23. @Value("${elasticsearch.connectTimeout:5000}")
    24. private int connectTimeout;
    25. /** Socket 连接超时时间 */
    26. @Value("${elasticsearch.socketTimeout:10000}")
    27. private int socketTimeout;
    28. /** 获取连接的超时时间 */
    29. @Value("${elasticsearch.connectionRequestTimeout:5000}")
    30. private int connectionRequestTimeout;
    31. /** 最大连接数 */
    32. @Value("${elasticsearch.maxConnectNum:100}")
    33. private int maxConnectNum;
    34. /** 最大路由连接数 */
    35. @Value("${elasticsearch.maxConnectPerRoute:100}")
    36. private int maxConnectPerRoute;
    37. @Bean
    38. public RestHighLevelClient restHighLevelClient() {
    39. // 拆分地址
    40. List hostLists = new ArrayList<>();
    41. String[] hostList = address.split(",");
    42. for (String addr : hostList) {
    43. String host = addr.split(":")[0];
    44. String port = addr.split(":")[1];
    45. hostLists.add(new HttpHost(host, Integer.parseInt(port), schema));
    46. }
    47. // 转换成 HttpHost 数组
    48. HttpHost[] httpHost = hostLists.toArray(new HttpHost[]{});
    49. // 构建连接对象
    50. RestClientBuilder builder = RestClient.builder(httpHost);
    51. // 异步连接延时配置
    52. builder.setRequestConfigCallback(requestConfigBuilder -> {
    53. requestConfigBuilder.setConnectTimeout(connectTimeout);
    54. requestConfigBuilder.setSocketTimeout(socketTimeout);
    55. requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeout);
    56. return requestConfigBuilder;
    57. });
    58. // 异步连接数配置
    59. builder.setHttpClientConfigCallback(httpClientBuilder -> {
    60. httpClientBuilder.setMaxConnTotal(maxConnectNum);
    61. httpClientBuilder.setMaxConnPerRoute(maxConnectPerRoute);
    62. return httpClientBuilder;
    63. });
    64. return new RestHighLevelClient(builder);
    65. }
    66. }

    四、索引操作示例

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

    1、Restful 操作示例

    创建索引

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

    1. PUT http://localhost:9200/testindex
    2. {
    3. "mappings": {
    4. "doc": {
    5. "dynamic": true,
    6. "properties": {
    7. "name": {
    8. "type": "text",
    9. "fields": {
    10. "keyword": {
    11. "type": "keyword"
    12. }
    13. }
    14. },
    15. "address": {
    16. "type": "text",
    17. "fields": {
    18. "keyword": {
    19. "type": "keyword"
    20. }
    21. }
    22. },
    23. "remark": {
    24. "type": "text",
    25. "fields": {
    26. "keyword": {
    27. "type": "keyword"
    28. }
    29. }
    30. },
    31. "age": {
    32. "type": "integer"
    33. },
    34. "salary": {
    35. "type": "float"
    36. },
    37. "birthDate": {
    38. "type": "date",
    39. "format": "yyyy-MM-dd"
    40. },
    41. "createTime": {
    42. "type": "date"
    43. }
    44. }
    45. }
    46. }
    47. }

    删除索引

    删除 mydlq-user 索引。

    DELETE http://localhost:9200/testindex

    2、Java 代码示例

    1. package com.example.elasticsearch.demos.web.service.base;
    2. import lombok.extern.slf4j.Slf4j;
    3. import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
    4. import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
    5. import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
    6. import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
    7. import org.elasticsearch.action.support.master.AcknowledgedResponse;
    8. import org.elasticsearch.client.RequestOptions;
    9. import org.elasticsearch.client.RestHighLevelClient;
    10. import org.elasticsearch.common.settings.Settings;
    11. import org.elasticsearch.common.xcontent.XContentBuilder;
    12. import org.elasticsearch.common.xcontent.XContentFactory;
    13. import org.springframework.beans.factory.annotation.Autowired;
    14. import org.springframework.stereotype.Service;
    15. import java.io.IOException;
    16. /**
    17. * 索引操作
    18. */
    19. @Slf4j
    20. @Service
    21. public class IndexService {
    22. @Autowired
    23. private RestHighLevelClient restHighLevelClient;
    24. /**
    25. * 验证索引是否存在
    26. */
    27. public Object existsIndex(String indexName) {
    28. Object result = "";
    29. try {
    30. // 获取索引请求
    31. GetIndexRequest request = new GetIndexRequest();
    32. // 设置要查询的索引名称
    33. request.indices(indexName);
    34. // 执行请求,验证索引是否存在
    35. boolean isExist = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
    36. log.info("是否存在:{}", isExist);
    37. // 根据具体业务逻辑返回不同结果,这里为了方便直接将结果返回
    38. result = isExist;
    39. } catch (IOException e) {
    40. log.error("", e);
    41. }
    42. return result;
    43. }
    44. /**
    45. * 创建索引
    46. */
    47. public Object createIndex(String indexName) {
    48. Object result = "";
    49. try {
    50. // 创建 Mapping
    51. XContentBuilder mapping = XContentFactory.jsonBuilder()
    52. .startObject()
    53. .field("dynamic", true)
    54. .startObject("properties")
    55. .startObject("name")
    56. .field("type","text")
    57. .startObject("fields")
    58. .startObject("keyword")
    59. .field("type","keyword")
    60. .endObject()
    61. .endObject()
    62. .endObject()
    63. .startObject("address")
    64. .field("type","text")
    65. .startObject("fields")
    66. .startObject("keyword")
    67. .field("type","keyword")
    68. .endObject()
    69. .endObject()
    70. .endObject()
    71. .startObject("remark")
    72. .field("type","text")
    73. .startObject("fields")
    74. .startObject("keyword")
    75. .field("type","keyword")
    76. .endObject()
    77. .endObject()
    78. .endObject()
    79. .startObject("age")
    80. .field("type","integer")
    81. .endObject()
    82. .startObject("salary")
    83. .field("type","float")
    84. .endObject()
    85. .startObject("birthDate")
    86. .field("type","date")
    87. .field("format", "yyyy-MM-dd")
    88. .endObject()
    89. .startObject("createTime")
    90. .field("type","date")
    91. .endObject()
    92. .endObject()
    93. .endObject();
    94. // 创建索引配置信息,配置
    95. Settings settings = Settings.builder()
    96. .put("index.number_of_shards", 1)
    97. .put("index.number_of_replicas", 0)
    98. .build();
    99. // 新建创建索引请求对象,然后设置索引类型(ES 7.0 将不存在索引类型)和 mapping 与 index 配置
    100. CreateIndexRequest request = new CreateIndexRequest(indexName, settings);
    101. request.mapping("doc", mapping);
    102. // RestHighLevelClient 执行创建索引
    103. CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
    104. // 判断是否创建成功
    105. boolean isCreated = createIndexResponse.isAcknowledged();
    106. log.info("是否创建成功:{}", isCreated);
    107. // 根据具体业务逻辑返回不同结果,这里为了方便直接将结果返回
    108. result = isCreated;
    109. } catch (IOException e) {
    110. log.error("", e);
    111. }
    112. return result;
    113. }
    114. /**
    115. * 删除索引
    116. */
    117. public Object deleteIndex(String indexName) {
    118. Object result = "";
    119. try {
    120. // 新建删除索引请求对象
    121. DeleteIndexRequest request = new DeleteIndexRequest(indexName);
    122. // 执行删除索引
    123. AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
    124. // 判断是否删除成功
    125. boolean siDeleted = acknowledgedResponse.isAcknowledged();
    126. log.info("是否删除成功:{}", siDeleted);
    127. // 根据具体业务逻辑返回不同结果,这里为了方便直接将结果返回
    128. result = siDeleted;
    129. } catch (IOException e) {
    130. log.error("", e);
    131. }
    132. return result;
    133. }
    134. }

    五、文档操作示例

    1、Restful 操作示例

    增加文档信息

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

    1. POST http://localhost:9200/testindex/doc
    2. {
    3. "address": "北京市",
    4. "age": 29,
    5. "birthDate": "1990-01-10",
    6. "createTime": 1579530727699,
    7. "name": "张三",
    8. "remark": "来自北京市的张先生",
    9. "salary": 100
    10. }
    11. //返回
    12. {
    13. "_index": "testindex",
    14. "_type": "doc",
    15. "_id": "hZo5_4oBFE0BmNy_GMUN", //这个是插入生成的随机id
    16. "_version": 1,
    17. "result": "created",
    18. "_shards": {
    19. "total": 1,
    20. "successful": 1,
    21. "failed": 0
    22. },
    23. "_seq_no": 29,
    24. "_primary_term": 3
    25. }

    获取文档信息

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

    1. GET http://localhost:9200/testindex/doc/hZo5_4oBFE0BmNy_GMUN
    2. //返回
    3. {
    4. "_index": "testindex",
    5. "_type": "doc",
    6. "_id": "hZo5_4oBFE0BmNy_GMUN",
    7. "_version": 1,
    8. "_seq_no": 29,
    9. "_primary_term": 3,
    10. "found": true,
    11. "_source": {
    12. "address": "北京市",
    13. "age": 29,
    14. "birthDate": "1990-01-10",
    15. "createTime": 1579530727699,
    16. "name": "张三",
    17. "remark": "来自北京市的张先生",
    18. "salary": 100
    19. }
    20. }

    更新文档信息

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

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

    删除文档信息

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

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

    2、Java 代码示例

    1. package com.example.elasticsearch.demos.web.service.base;
    2. import com.alibaba.fastjson.JSON;
    3. import com.example.elasticsearch.demos.web.model.dto.DocDto;
    4. import com.example.elasticsearch.demos.web.model.entity.UserInfo;
    5. import lombok.extern.slf4j.Slf4j;
    6. import org.elasticsearch.action.delete.DeleteRequest;
    7. import org.elasticsearch.action.delete.DeleteResponse;
    8. import org.elasticsearch.action.get.GetRequest;
    9. import org.elasticsearch.action.get.GetResponse;
    10. import org.elasticsearch.action.index.IndexRequest;
    11. import org.elasticsearch.action.index.IndexResponse;
    12. import org.elasticsearch.action.update.UpdateRequest;
    13. import org.elasticsearch.action.update.UpdateResponse;
    14. import org.elasticsearch.client.RequestOptions;
    15. import org.elasticsearch.client.RestHighLevelClient;
    16. import org.elasticsearch.common.xcontent.XContentType;
    17. import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
    18. import org.springframework.beans.factory.annotation.Autowired;
    19. import org.springframework.stereotype.Service;
    20. import java.io.IOException;
    21. import java.util.Date;
    22. /**
    23. * 文档操作
    24. */
    25. @Slf4j
    26. @Service
    27. public class DocumentService {
    28. @Autowired
    29. private RestHighLevelClient restHighLevelClient;
    30. public Object existsDocument(DocDto docDto) {
    31. Object result = "";
    32. try {
    33. // 获取请求对象
    34. GetRequest getRequest = new GetRequest(docDto.getIndexName(), docDto.getDocId());
    35. // 是否获取源码内容
    36. getRequest.fetchSourceContext(new FetchSourceContext(false));
    37. // 执行请求,验证文档是否存在
    38. boolean isExist = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
    39. log.info("文档是否存在:{}", isExist);
    40. // 根据具体业务逻辑返回不同结果,这里为了方便直接将结果返回
    41. result = isExist;
    42. } catch (IOException e) {
    43. log.error("", e);
    44. }
    45. return result;
    46. }
    47. public Object getDocument(DocDto docDto) {
    48. Object result = "";
    49. try {
    50. // 获取请求对象
    51. GetRequest getRequest = new GetRequest(docDto.getIndexName(), docDto.getDocId());
    52. // 获取文档信息
    53. GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
    54. // 将 JSON 转换成对象
    55. if (getResponse.isExists()) {
    56. UserInfo userInfo = JSON.parseObject(getResponse.getSourceAsBytes(), UserInfo.class);
    57. log.info("用户信息:{}", userInfo);
    58. }
    59. // 根据具体业务逻辑返回不同结果,这里为了方便直接将结果返回
    60. result = getResponse;
    61. } catch (IOException e) {
    62. log.error("", e);
    63. }
    64. return result;
    65. }
    66. public Object addDocument(DocDto docDto) {
    67. Object result = "";
    68. try {
    69. // 创建索引请求对象
    70. IndexRequest indexRequest = new IndexRequest(docDto.getIndexName());
    71. // 创建用户信息
    72. UserInfo userInfo = new UserInfo();
    73. userInfo.setName(docDto.getName());
    74. userInfo.setAge(docDto.getAge());
    75. userInfo.setSalary(docDto.getSalary());
    76. userInfo.setAddress(docDto.getAddress());
    77. userInfo.setRemark(docDto.getRemark());
    78. userInfo.setCreateTime(new Date());
    79. userInfo.setBirthDate(docDto.getBirthDate());
    80. // 将对象转换为 byte 数组
    81. byte[] json = JSON.toJSONBytes(userInfo);
    82. // 设置文档内容
    83. indexRequest.source(json, XContentType.JSON);
    84. // 执行增加文档
    85. IndexResponse response = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
    86. log.info("创建状态:{}", response.status());
    87. // 根据具体业务逻辑返回不同结果,这里为了方便直接将结果返回
    88. result = response;
    89. } catch (Exception e) {
    90. log.error("", e);
    91. }
    92. return result;
    93. }
    94. public Object updateDocument(DocDto docDto) {
    95. Object result = "";
    96. try {
    97. // 创建索引请求对象
    98. UpdateRequest updateRequest = new UpdateRequest(docDto.getIndexName(), docDto.getDocId());
    99. // UpdateRequest updateRequest = new UpdateRequest(docDto.getIndexName(), "doc", docDto.getDocId());
    100. // 设置用户更新信息
    101. UserInfo userInfo = new UserInfo();
    102. userInfo.setSalary(docDto.getSalary());
    103. userInfo.setAddress(docDto.getAddress());
    104. // 将对象转换为 byte 数组
    105. byte[] json = JSON.toJSONBytes(userInfo);
    106. // 设置更新文档内容
    107. updateRequest.doc(json, XContentType.JSON);
    108. // 执行更新文档
    109. UpdateResponse response = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
    110. log.info("创建状态:{}", response.status());
    111. // 根据具体业务逻辑返回不同结果,这里为了方便直接将结果返回
    112. result = response;
    113. } catch (Exception e) {
    114. log.error("", e);
    115. }
    116. return result;
    117. }
    118. public Object deleteDocument(DocDto docDto) {
    119. Object result = "";
    120. try {
    121. // 创建删除请求对象
    122. DeleteRequest deleteRequest = new DeleteRequest(docDto.getIndexName(), docDto.getDocId());
    123. // 执行删除文档
    124. DeleteResponse response = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
    125. log.info("删除状态:{}", response.status());
    126. // 根据具体业务逻辑返回不同结果,这里为了方便直接将结果返回
    127. result = response;
    128. } catch (IOException e) {
    129. log.error("", e);
    130. }
    131. return result;
    132. }
    133. }

    六、插入初始化数据

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

    1、单条插入

    1. POST http://localhost:9200/testindex/doc
    2. //请求
    3. {
    4. "name": "零零",
    5. "address": "北京市丰台区",
    6. "remark": "低层员工",
    7. "age": 29,
    8. "salary": 3000,
    9. "birthDate": "1990-11-11",
    10. "createTime": "2019-11-11T08:18:00.000Z"
    11. }

    2、批量插入

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

    3、查询数据

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

    1. GET http://localhost:9200/testindex/_search
    2. //返回
    3. {
    4. "took": 6,
    5. "timed_out": false,
    6. "_shards": {
    7. "total": 1,
    8. "successful": 1,
    9. "skipped": 0,
    10. "failed": 0
    11. },
    12. "hits": {
    13. "total": {
    14. "value": 2,
    15. "relation": "eq"
    16. },
    17. "max_score": 2.302585,
    18. "hits": [
    19. {
    20. "_index": "testindex",
    21. "_type": "doc",
    22. "_id": "3iDh-IoByPOFA_QWinlo",
    23. "_score": 2.302585,
    24. "_source": {
    25. "name": "赵六",
    26. "address": "北京市通州区",
    27. "remark": "中层员工",
    28. "age": 32,
    29. "salary": 6500,
    30. "birthDate": "1987-06-02",
    31. "createTime": "2019-12-10T18:00:00.000Z"
    32. }
    33. },
    34. {
    35. "_index": "testindex",
    36. "_type": "doc",
    37. "_id": "5yDh-IoByPOFA_QWinlo",
    38. "_score": 2.302585,
    39. "_source": {
    40. "name": "蒋十五",
    41. "address": "北京市通州区",
    42. "remark": "低层员工",
    43. "age": 31,
    44. "salary": 2800,
    45. "birthDate": "1988-07-20",
    46. "createTime": "2019-06-13T10:00:00.000Z"
    47. }
    48. }
    49. ...
    50. ]
    51. }
    52. }

    七、查询操作示例

    1、精确查询(term)

    (1)、Restful 操作示例

    精确查询

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

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

    1. GET http://localhost:9200/testindex/_search
    2. //请求
    3. {
    4. "query": {
    5. "term": {
    6. "address.keyword": {
    7. "value": "北京市通州区"
    8. }
    9. }
    10. }
    11. }
    12. //返回
    13. {
    14. "took": 6,
    15. "timed_out": false,
    16. "_shards": {
    17. "total": 1,
    18. "successful": 1,
    19. "skipped": 0,
    20. "failed": 0
    21. },
    22. "hits": {
    23. "total": {
    24. "value": 2,
    25. "relation": "eq"
    26. },
    27. "max_score": 2.302585,
    28. "hits": [
    29. {
    30. "_index": "testindex",
    31. "_type": "doc",
    32. "_id": "3iDh-IoByPOFA_QWinlo",
    33. "_score": 2.302585,
    34. "_source": {
    35. "name": "赵六",
    36. "address": "北京市通州区",
    37. "remark": "中层员工",
    38. "age": 32,
    39. "salary": 6500,
    40. "birthDate": "1987-06-02",
    41. "createTime": "2019-12-10T18:00:00.000Z"
    42. }
    43. },
    44. {
    45. "_index": "testindex",
    46. "_type": "doc",
    47. "_id": "5yDh-IoByPOFA_QWinlo",
    48. "_score": 2.302585,
    49. "_source": {
    50. "name": "蒋十五",
    51. "address": "北京市通州区",
    52. "remark": "低层员工",
    53. "age": 31,
    54. "salary": 2800,
    55. "birthDate": "1988-07-20",
    56. "createTime": "2019-06-13T10:00:00.000Z"
    57. }
    58. }
    59. ...
    60. ]
    61. }
    62. }
    精确查询-多内容查询

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

     

    1. GET http://localhost:9200/testindex/_search
    2. //请求
    3. {
    4. "query": {
    5. "terms": {
    6. "address.keyword": [
    7. "北京市丰台区",
    8. "北京市昌平区",
    9. "北京市大兴区"
    10. ]
    11. }
    12. }
    13. }

    (2)、Java 代码示例 

    1. package com.example.elasticsearch.demos.web.service.query;
    2. import com.alibaba.fastjson.JSON;
    3. import com.example.elasticsearch.demos.web.model.dto.TermsQueryDto;
    4. import com.example.elasticsearch.demos.web.model.entity.UserInfo;
    5. import lombok.extern.slf4j.Slf4j;
    6. import org.elasticsearch.action.search.SearchRequest;
    7. import org.elasticsearch.action.search.SearchResponse;
    8. import org.elasticsearch.client.RequestOptions;
    9. import org.elasticsearch.client.RestHighLevelClient;
    10. import org.elasticsearch.index.query.QueryBuilders;
    11. import org.elasticsearch.rest.RestStatus;
    12. import org.elasticsearch.search.SearchHit;
    13. import org.elasticsearch.search.SearchHits;
    14. import org.elasticsearch.search.builder.SearchSourceBuilder;
    15. import org.springframework.beans.factory.annotation.Autowired;
    16. import org.springframework.stereotype.Service;
    17. import java.io.IOException;
    18. import java.util.Arrays;
    19. /**
    20. * 精确查询
    21. */
    22. @Slf4j
    23. @Service
    24. public class TermQueryService {
    25. @Autowired
    26. private RestHighLevelClient restHighLevelClient;
    27. /**
    28. * 精确查询(查询条件不会进行分词,但是查询内容可能会分词,导致查询不到)
    29. * @param queryDto
    30. */
    31. public Object termQuery(TermsQueryDto queryDto) {
    32. Object result = "";
    33. try {
    34. // 构建查询条件(注意:termQuery 支持多种格式查询,如 boolean、int、double、string 等,这里使用的是 string 的查询)
    35. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    36. searchSourceBuilder.query(QueryBuilders.termQuery(queryDto.getKey() + ".keyword", queryDto.getValue()));
    37. // 创建查询请求对象,将查询对象配置到其中
    38. SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());
    39. searchRequest.source(searchSourceBuilder);
    40. // 执行查询,然后处理响应结果
    41. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    42. // 根据状态和数据条数验证是否返回了数据
    43. if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
    44. SearchHits hits = searchResponse.getHits();
    45. for (SearchHit hit : hits) {
    46. // 将 JSON 转换成对象
    47. UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
    48. // 输出查询信息
    49. log.info(userInfo.toString());
    50. }
    51. }
    52. result = searchResponse.getHits();
    53. } catch (IOException e) {
    54. log.error("", e);
    55. }
    56. return result;
    57. }
    58. /**
    59. * 多个内容在一个字段中进行查询
    60. * @param queryDto
    61. */
    62. public Object termsQuery(TermsQueryDto queryDto) {
    63. Object result = "";
    64. try {
    65. // 构建查询条件(注意:termsQuery 支持多种格式查询,如 boolean、int、double、string 等,这里使用的是 string 的查询)
    66. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    67. searchSourceBuilder.query(QueryBuilders.termsQuery(queryDto.getKey() + ".keyword", queryDto.getValues()));
    68. // 创建查询请求对象,将查询对象配置到其中
    69. SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());
    70. searchRequest.source(searchSourceBuilder);
    71. // 执行查询,然后处理响应结果
    72. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    73. // 根据状态和数据条数验证是否返回了数据
    74. if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
    75. SearchHits hits = searchResponse.getHits();
    76. for (SearchHit hit : hits) {
    77. // 将 JSON 转换成对象
    78. UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
    79. // 输出查询信息
    80. log.info(userInfo.toString());
    81. }
    82. }
    83. result = searchResponse.getHits();
    84. } catch (IOException e) {
    85. log.error("", e);
    86. }
    87. return result;
    88. }
    89. }

    2、匹配查询(match)

    (1)、Restful 操作示例

    匹配查询全部数据与分页

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

    1. GET http://localhost:9200/testindex/_search
    2. //请求
    3. {
    4. "query": {
    5. "match_all": {}
    6. },
    7. "from": 0,
    8. "size": 10,
    9. "sort": [
    10. {
    11. "salary": {
    12. "order": "asc"
    13. }
    14. }
    15. ]
    16. }
    匹配查询数据

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

    1. GET http://localhost:9200/testindex/_search
    2. //请求
    3. {
    4. "query": {
    5. "match": {
    6. "address": "通州区"
    7. }
    8. }
    9. }
     词语匹配查询

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

    1. GET http://localhost:9200/testindex/_search
    2. //请求
    3. {
    4. "query": {
    5. "match_phrase": {
    6. "address": "北京市通州区"
    7. }
    8. }
    9. }
    内容多字段查询

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

    1. GET http://localhost:9200/testindex/_search
    2. //请求
    3. {
    4. "query": {
    5. "multi_match": {
    6. "query": "北京",
    7. "fields": ["address","remark"]
    8. }
    9. }
    10. }

     (2)、Java 代码示例

    1. package com.example.elasticsearch.demos.web.service.query;
    2. import com.alibaba.fastjson.JSON;
    3. import com.example.elasticsearch.demos.web.model.dto.MatchQueryDto;
    4. import com.example.elasticsearch.demos.web.model.entity.UserInfo;
    5. import lombok.extern.slf4j.Slf4j;
    6. import org.elasticsearch.action.search.SearchRequest;
    7. import org.elasticsearch.action.search.SearchResponse;
    8. import org.elasticsearch.client.RequestOptions;
    9. import org.elasticsearch.client.RestHighLevelClient;
    10. import org.elasticsearch.index.query.MatchAllQueryBuilder;
    11. import org.elasticsearch.index.query.QueryBuilders;
    12. import org.elasticsearch.rest.RestStatus;
    13. import org.elasticsearch.search.SearchHit;
    14. import org.elasticsearch.search.SearchHits;
    15. import org.elasticsearch.search.builder.SearchSourceBuilder;
    16. import org.elasticsearch.search.sort.SortOrder;
    17. import org.springframework.beans.factory.annotation.Autowired;
    18. import org.springframework.stereotype.Service;
    19. import java.io.IOException;
    20. /**
    21. * 匹配查询
    22. */
    23. @Slf4j
    24. @Service
    25. public class MatchQueryService {
    26. @Autowired
    27. private RestHighLevelClient restHighLevelClient;
    28. /**
    29. * 匹配查询符合条件的所有数据,并设置分页
    30. * @param queryDto
    31. */
    32. public Object matchAllQuery(MatchQueryDto queryDto) {
    33. Object result = "";
    34. try {
    35. // 构建查询条件
    36. MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
    37. // 创建查询源构造器
    38. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    39. searchSourceBuilder.query(matchAllQueryBuilder);
    40. // 设置分页
    41. searchSourceBuilder.from((queryDto.getRows() - 1) * queryDto.getSize());
    42. searchSourceBuilder.size(queryDto.getSize());
    43. // 设置排序
    44. searchSourceBuilder.sort("salary", SortOrder.ASC);
    45. // 创建查询请求对象,将查询对象配置到其中
    46. SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());
    47. searchRequest.source(searchSourceBuilder);
    48. // 执行查询,然后处理响应结果
    49. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    50. // 根据状态和数据条数验证是否返回了数据
    51. if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
    52. SearchHits hits = searchResponse.getHits();
    53. for (SearchHit hit : hits) {
    54. // 将 JSON 转换成对象
    55. UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
    56. // 输出查询信息
    57. log.info(userInfo.toString());
    58. }
    59. }
    60. result = searchResponse.getHits();
    61. } catch (IOException e) {
    62. log.error("", e);
    63. }
    64. return result;
    65. }
    66. /**
    67. * 匹配查询数据-or的方式
    68. * @param queryDto
    69. */
    70. public Object matchQuery(MatchQueryDto queryDto) {
    71. Object result = "";
    72. try {
    73. // 构建查询条件
    74. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    75. searchSourceBuilder.query(QueryBuilders.matchQuery(queryDto.getKey(), queryDto.getValue()));
    76. // searchSourceBuilder.query(QueryBuilders.matchQuery("address", "通州区"));
    77. // 创建查询请求对象,将查询对象配置到其中
    78. SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());
    79. searchRequest.source(searchSourceBuilder);
    80. // 执行查询,然后处理响应结果
    81. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    82. // 根据状态和数据条数验证是否返回了数据
    83. if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
    84. SearchHits hits = searchResponse.getHits();
    85. for (SearchHit hit : hits) {
    86. // 将 JSON 转换成对象
    87. UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
    88. // 输出查询信息
    89. log.info(userInfo.toString());
    90. }
    91. }
    92. result = searchResponse.getHits();
    93. } catch (IOException e) {
    94. log.error("", e);
    95. }
    96. return result;
    97. }
    98. /**
    99. * 词语匹配查询
    100. * @param queryDto
    101. */
    102. public Object matchPhraseQuery(MatchQueryDto queryDto) {
    103. Object result = "";
    104. try {
    105. // 构建查询条件
    106. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    107. searchSourceBuilder.query(QueryBuilders.matchPhraseQuery(queryDto.getKey(), queryDto.getValue()));
    108. // searchSourceBuilder.query(QueryBuilders.matchPhraseQuery("address", "北京市通州区"));
    109. // 创建查询请求对象,将查询对象配置到其中
    110. SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());
    111. searchRequest.source(searchSourceBuilder);
    112. // 执行查询,然后处理响应结果
    113. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    114. // 根据状态和数据条数验证是否返回了数据
    115. if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
    116. SearchHits hits = searchResponse.getHits();
    117. for (SearchHit hit : hits) {
    118. // 将 JSON 转换成对象
    119. UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
    120. // 输出查询信息
    121. log.info(userInfo.toString());
    122. }
    123. }
    124. result = searchResponse.getHits();
    125. } catch (IOException e) {
    126. log.error("", e);
    127. }
    128. return result;
    129. }
    130. /**
    131. * 内容在多字段中进行查询
    132. * @param queryDto
    133. */
    134. public Object matchMultiQuery(MatchQueryDto queryDto) {
    135. Object result = "";
    136. try {
    137. // 构建查询条件
    138. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    139. searchSourceBuilder.query(QueryBuilders.multiMatchQuery(queryDto.getKey(), queryDto.getValues()));
    140. // searchSourceBuilder.query(QueryBuilders.multiMatchQuery("北京市", "address", "remark"));
    141. // 创建查询请求对象,将查询对象配置到其中
    142. SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());
    143. searchRequest.source(searchSourceBuilder);
    144. // 执行查询,然后处理响应结果
    145. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    146. // 根据状态和数据条数验证是否返回了数据
    147. if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
    148. SearchHits hits = searchResponse.getHits();
    149. for (SearchHit hit : hits) {
    150. // 将 JSON 转换成对象
    151. UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
    152. // 输出查询信息
    153. log.info(userInfo.toString());
    154. }
    155. }
    156. result = searchResponse.getHits();
    157. } catch (IOException e) {
    158. log.error("", e);
    159. }
    160. return result;
    161. }
    162. }

    3、模糊查询(fuzzy)

    (1)、Restful 操作示例

    模糊查询所有以 三 结尾的姓名
    1. GET http://localhost:9200/testindex/_search
    2. //请求
    3. {
    4. "query": {
    5. "fuzzy": {
    6. "name": "三"
    7. }
    8. }
    9. }

     (2)、Java 代码示例

    1. package com.example.elasticsearch.demos.web.service.query;
    2. import com.alibaba.fastjson.JSON;
    3. import com.example.elasticsearch.demos.web.model.dto.MatchQueryDto;
    4. import com.example.elasticsearch.demos.web.model.entity.UserInfo;
    5. import lombok.extern.slf4j.Slf4j;
    6. import org.elasticsearch.action.search.SearchRequest;
    7. import org.elasticsearch.action.search.SearchResponse;
    8. import org.elasticsearch.client.RequestOptions;
    9. import org.elasticsearch.client.RestHighLevelClient;
    10. import org.elasticsearch.common.unit.Fuzziness;
    11. import org.elasticsearch.index.query.QueryBuilders;
    12. import org.elasticsearch.rest.RestStatus;
    13. import org.elasticsearch.search.SearchHit;
    14. import org.elasticsearch.search.SearchHits;
    15. import org.elasticsearch.search.builder.SearchSourceBuilder;
    16. import org.springframework.beans.factory.annotation.Autowired;
    17. import org.springframework.stereotype.Service;
    18. import java.io.IOException;
    19. /**
    20. * 模糊查询
    21. */
    22. @Slf4j
    23. @Service
    24. public class FuzzyQueryService {
    25. @Autowired
    26. private RestHighLevelClient restHighLevelClient;
    27. /**
    28. * 模糊查询所有以 “三” 结尾的姓名
    29. * @param queryDto
    30. */
    31. public Object fuzzyQuery(MatchQueryDto queryDto) {
    32. Object result = "";
    33. try {
    34. // 构建查询条件
    35. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    36. searchSourceBuilder.query(QueryBuilders.fuzzyQuery(queryDto.getKey(), queryDto.getValue()).fuzziness(Fuzziness.AUTO));
    37. // 创建查询请求对象,将查询对象配置到其中
    38. SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());
    39. searchRequest.source(searchSourceBuilder);
    40. // 执行查询,然后处理响应结果
    41. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    42. // 根据状态和数据条数验证是否返回了数据
    43. if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
    44. SearchHits hits = searchResponse.getHits();
    45. for (SearchHit hit : hits) {
    46. // 将 JSON 转换成对象
    47. UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
    48. // 输出查询信息
    49. log.info(userInfo.toString());
    50. }
    51. }
    52. result = searchResponse.getHits();
    53. } catch (IOException e) {
    54. log.error("", e);
    55. }
    56. return result;
    57. }
    58. }

    4、范围查询(range)

    (1)、Restful 操作示例

    查询岁数 ≥ 30 岁的员工数据:
    1. GET http://localhost:9200/testindex/_search
    2. //请求
    3. {
    4. "query": {
    5. "range": {
    6. "age": {
    7. "gte": 30
    8. }
    9. }
    10. }
    11. }
    查询生日距离现在 30 年间的员工数据: 
    1. GET http://localhost:9200/testindex/_search
    2. //请求
    3. {
    4. "query": {
    5. "range": {
    6. "birthDate": {
    7. "gte": "now-30y"
    8. }
    9. }
    10. }
    11. }

    (2)、Java 代码示例 

    1. package com.example.elasticsearch.demos.web.service.query;
    2. import com.alibaba.fastjson.JSON;
    3. import com.example.elasticsearch.demos.web.model.dto.MatchQueryDto;
    4. import com.example.elasticsearch.demos.web.model.entity.UserInfo;
    5. import lombok.extern.slf4j.Slf4j;
    6. import org.elasticsearch.action.search.SearchRequest;
    7. import org.elasticsearch.action.search.SearchResponse;
    8. import org.elasticsearch.client.RequestOptions;
    9. import org.elasticsearch.client.RestHighLevelClient;
    10. import org.elasticsearch.index.query.QueryBuilders;
    11. import org.elasticsearch.rest.RestStatus;
    12. import org.elasticsearch.search.SearchHit;
    13. import org.elasticsearch.search.SearchHits;
    14. import org.elasticsearch.search.builder.SearchSourceBuilder;
    15. import org.springframework.beans.factory.annotation.Autowired;
    16. import org.springframework.stereotype.Service;
    17. import java.io.IOException;
    18. /**
    19. * 范围查询
    20. */
    21. @Slf4j
    22. @Service
    23. public class RangeQueryService {
    24. @Autowired
    25. private RestHighLevelClient restHighLevelClient;
    26. /**
    27. * 查询岁数 ≥ 30 岁的员工数据
    28. * @param queryDto
    29. */
    30. public Object rangeQuery(MatchQueryDto queryDto) {
    31. Object result = "";
    32. try {
    33. // 构建查询条件
    34. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    35. searchSourceBuilder.query(QueryBuilders.rangeQuery("age").gte(30));
    36. // 创建查询请求对象,将查询对象配置到其中
    37. SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());
    38. searchRequest.source(searchSourceBuilder);
    39. // 执行查询,然后处理响应结果
    40. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    41. // 根据状态和数据条数验证是否返回了数据
    42. if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
    43. SearchHits hits = searchResponse.getHits();
    44. for (SearchHit hit : hits) {
    45. // 将 JSON 转换成对象
    46. UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
    47. // 输出查询信息
    48. log.info(userInfo.toString());
    49. }
    50. }
    51. result = searchResponse.getHits();
    52. } catch (IOException e) {
    53. log.error("", e);
    54. }
    55. return result;
    56. }
    57. /**
    58. * 查询距离现在 30 年间的员工数据
    59. * [年(y)、月(M)、星期(w)、天(d)、小时(h)、分钟(m)、秒(s)]
    60. * 例如:
    61. * now-1h 查询一小时内范围
    62. * now-1d 查询一天内时间范围
    63. * now-1y 查询最近一年内的时间范围
    64. * @param queryDto
    65. */
    66. public Object dateRangeQuery(MatchQueryDto queryDto) {
    67. Object result = "";
    68. try {
    69. // 构建查询条件
    70. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    71. // // includeLower(是否包含下边界)、includeUpper(是否包含上边界)
    72. // searchSourceBuilder.query(QueryBuilders.rangeQuery("birthDate")
    73. // .gte("now-30y").includeLower(true).includeUpper(true));
    74. searchSourceBuilder.query(QueryBuilders.rangeQuery("birthDate").gte(queryDto.getFrom()).lte(queryDto.getEnd()));
    75. // 创建查询请求对象,将查询对象配置到其中
    76. SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());
    77. searchRequest.source(searchSourceBuilder);
    78. // 执行查询,然后处理响应结果
    79. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    80. // 根据状态和数据条数验证是否返回了数据
    81. if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
    82. SearchHits hits = searchResponse.getHits();
    83. for (SearchHit hit : hits) {
    84. // 将 JSON 转换成对象
    85. UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
    86. // 输出查询信息
    87. log.info(userInfo.toString());
    88. }
    89. }
    90. result = searchResponse.getHits();
    91. } catch (IOException e) {
    92. log.error("", e);
    93. }
    94. return result;
    95. }
    96. }

    5、通配符查询(wildcard)

    (1)、Restful 操作示例

    查询所有以 “三” 结尾的姓名:
    1. GET http://localhost:9200/testindex/_search
    2. //请求
    3. {
    4. "query": {
    5. "wildcard": {
    6. "name.keyword": {
    7. "value": "*三"
    8. }
    9. }
    10. }
    11. }

    (2)、Java 代码示例 

    1. package com.example.elasticsearch.demos.web.service.query;
    2. import com.alibaba.fastjson.JSON;
    3. import com.example.elasticsearch.demos.web.model.dto.MatchQueryDto;
    4. import com.example.elasticsearch.demos.web.model.entity.UserInfo;
    5. import lombok.extern.slf4j.Slf4j;
    6. import org.elasticsearch.action.search.SearchRequest;
    7. import org.elasticsearch.action.search.SearchResponse;
    8. import org.elasticsearch.client.RequestOptions;
    9. import org.elasticsearch.client.RestHighLevelClient;
    10. import org.elasticsearch.index.query.QueryBuilders;
    11. import org.elasticsearch.rest.RestStatus;
    12. import org.elasticsearch.search.SearchHit;
    13. import org.elasticsearch.search.SearchHits;
    14. import org.elasticsearch.search.builder.SearchSourceBuilder;
    15. import org.springframework.beans.factory.annotation.Autowired;
    16. import org.springframework.stereotype.Service;
    17. import java.io.IOException;
    18. /**
    19. * 通配符查询
    20. */
    21. @Slf4j
    22. @Service
    23. public class WildcardQueryService {
    24. @Autowired
    25. private RestHighLevelClient restHighLevelClient;
    26. /**
    27. * 查询所有以 “三” 结尾的姓名
    28. *

    29. * *:表示多个字符(0个或多个字符)
    30. * ?:表示单个字符
    31. * @param queryDto
    32. */
    33. public Object wildcardQuery(MatchQueryDto queryDto) {
    34. Object result = "";
    35. try {
    36. // 构建查询条件
    37. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    38. searchSourceBuilder.query(QueryBuilders.wildcardQuery(queryDto.getKey() + ".keyword", "*" + queryDto.getValue()));
    39. // 创建查询请求对象,将查询对象配置到其中
    40. SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());
    41. searchRequest.source(searchSourceBuilder);
    42. // 执行查询,然后处理响应结果
    43. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    44. // 根据状态和数据条数验证是否返回了数据
    45. if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
    46. SearchHits hits = searchResponse.getHits();
    47. for (SearchHit hit : hits) {
    48. // 将 JSON 转换成对象
    49. UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
    50. // 输出查询信息
    51. log.info(userInfo.toString());
    52. }
    53. }
    54. result = searchResponse.getHits();
    55. } catch (IOException e) {
    56. log.error("", e);
    57. }
    58. return result;
    59. }
    60. }

    6、布尔查询(bool)

    (1)、Restful 操作示例

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

    1. GET http://localhost:9200/testindex/_search
    2. //请求
    3. {
    4. "query": {
    5. "bool": {
    6. "filter": {
    7. "range": {
    8. "birthDate": {
    9. "format": "yyyy",
    10. "gte": 1990,
    11. "lte": 1995
    12. }
    13. }
    14. },
    15. "must": [
    16. {
    17. "terms": {
    18. "address.keyword": [
    19. "北京市昌平区",
    20. "北京市大兴区",
    21. "北京市房山区"
    22. ]
    23. }
    24. }
    25. ]
    26. }
    27. }
    28. }

     (2)、Java 代码示例

    1. package com.example.elasticsearch.demos.web.service.query;
    2. import com.alibaba.fastjson.JSON;
    3. import com.example.elasticsearch.demos.web.model.dto.MatchQueryDto;
    4. import com.example.elasticsearch.demos.web.model.entity.UserInfo;
    5. import lombok.extern.slf4j.Slf4j;
    6. import org.elasticsearch.action.search.SearchRequest;
    7. import org.elasticsearch.action.search.SearchResponse;
    8. import org.elasticsearch.client.RequestOptions;
    9. import org.elasticsearch.client.RestHighLevelClient;
    10. import org.elasticsearch.index.query.BoolQueryBuilder;
    11. import org.elasticsearch.index.query.QueryBuilders;
    12. import org.elasticsearch.rest.RestStatus;
    13. import org.elasticsearch.search.SearchHit;
    14. import org.elasticsearch.search.SearchHits;
    15. import org.elasticsearch.search.builder.SearchSourceBuilder;
    16. import org.springframework.beans.factory.annotation.Autowired;
    17. import org.springframework.stereotype.Service;
    18. import java.io.IOException;
    19. /**
    20. * 布尔查询
    21. */
    22. @Slf4j
    23. @Service
    24. public class BoolQueryService {
    25. @Autowired
    26. private RestHighLevelClient restHighLevelClient;
    27. /**
    28. * 布尔查询
    29. * @param queryDto
    30. */
    31. public Object boolQuery(MatchQueryDto queryDto) {
    32. Object result = "";
    33. try {
    34. // 创建 Bool 查询构建器
    35. BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    36. // 构建查询条件
    37. boolQueryBuilder.must(QueryBuilders.termsQuery("address.keyword", "北京市昌平区", "北京市大兴区", "北京市房山区"))
    38. .filter().add(QueryBuilders.rangeQuery("birthDate").format("yyyy").gte("1990").lte("1995"));
    39. // 构建查询源构建器
    40. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    41. searchSourceBuilder.query(boolQueryBuilder);
    42. // 创建查询请求对象,将查询对象配置到其中
    43. SearchRequest searchRequest = new SearchRequest(queryDto.getIndexName());
    44. searchRequest.source(searchSourceBuilder);
    45. // 执行查询,然后处理响应结果
    46. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    47. // 根据状态和数据条数验证是否返回了数据
    48. if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
    49. SearchHits hits = searchResponse.getHits();
    50. for (SearchHit hit : hits) {
    51. // 将 JSON 转换成对象
    52. UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
    53. // 输出查询信息
    54. log.info(userInfo.toString());
    55. }
    56. }
    57. result = searchResponse.getHits();
    58. } catch (IOException e) {
    59. log.error("", e);
    60. }
    61. return result;
    62. }
    63. }

    八、聚合查询操作示例

    1、Metric 聚合分析

    (1)、Restful 操作示例

    1. GET http://localhost:9200/testindex/_search
    2. 1、统计员工总数、工资最高值、工资最低值、工资平均工资、工资总和:
    3. //请求
    4. {
    5. "size": 0,
    6. "aggs": {
    7. "salary_stats": {
    8. "stats": {
    9. "field": "salary"
    10. }
    11. }
    12. }
    13. }
    14. 2、统计员工工资最低值:
    15. //请求
    16. {
    17. "size": 0,
    18. "aggs": {
    19. "salary_min": {
    20. "min": {
    21. "field": "salary"
    22. }
    23. }
    24. }
    25. }
    26. 3、统计员工工资最高值:
    27. //请求
    28. {
    29. "size": 0,
    30. "aggs": {
    31. "salary_max": {
    32. "max": {
    33. "field": "salary"
    34. }
    35. }
    36. }
    37. }
    38. 4、统计员工工资平均值:
    39. //请求
    40. {
    41. "size": 0,
    42. "aggs": {
    43. "salary_avg": {
    44. "avg": {
    45. "field": "salary"
    46. }
    47. }
    48. }
    49. }
    50. 5、统计员工工资总值:
    51. //请求
    52. {
    53. "size": 0,
    54. "aggs": {
    55. "salary_sum": {
    56. "sum": {
    57. "field": "salary"
    58. }
    59. }
    60. }
    61. }
    62. 6、统计员工总数:
    63. //请求
    64. {
    65. "size": 0,
    66. "aggs": {
    67. "employee_count": {
    68. "value_count": {
    69. "field": "salary"
    70. }
    71. }
    72. }
    73. }
    74. 7、统计员工工资百分位:
    75. //请求
    76. {
    77. "size": 0,
    78. "aggs": {
    79. "salary_percentiles": {
    80. "percentiles": {
    81. "field": "salary"
    82. }
    83. }
    84. }
    85. }

    (2)、Java 代码示例

    1. package com.example.elasticsearch.demos.web.service.aggregation;
    2. import com.example.elasticsearch.demos.web.model.dto.MatchQueryDto;
    3. import lombok.extern.slf4j.Slf4j;
    4. import org.elasticsearch.action.search.SearchRequest;
    5. import org.elasticsearch.action.search.SearchResponse;
    6. import org.elasticsearch.client.RequestOptions;
    7. import org.elasticsearch.client.RestHighLevelClient;
    8. import org.elasticsearch.rest.RestStatus;
    9. import org.elasticsearch.search.aggregations.AggregationBuilder;
    10. import org.elasticsearch.search.aggregations.AggregationBuilders;
    11. import org.elasticsearch.search.aggregations.Aggregations;
    12. import org.elasticsearch.search.aggregations.metrics.*;
    13. import org.elasticsearch.search.builder.SearchSourceBuilder;
    14. import org.springframework.beans.factory.annotation.Autowired;
    15. import org.springframework.beans.factory.annotation.Value;
    16. import org.springframework.stereotype.Component;
    17. import org.springframework.stereotype.Service;
    18. import java.io.IOException;
    19. /**
    20. * 聚合 Metric
    21. */
    22. @Slf4j
    23. @Service
    24. public class AggrMetricService {
    25. @Autowired
    26. private RestHighLevelClient restHighLevelClient;
    27. @Value("${myindex}")
    28. private String indexName;
    29. /**
    30. * stats 统计员工总数、工资最高值、工资最低值、工资平均工资、工资总和
    31. * @param queryDto
    32. */
    33. public Object aggregationStats(MatchQueryDto queryDto) {
    34. String responseResult = "";
    35. try {
    36. // 设置聚合条件
    37. String field = queryDto.getKey();
    38. AggregationBuilder aggr = AggregationBuilders.stats(field + "_stats").field(field);
    39. // 查询源构建器
    40. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    41. searchSourceBuilder.aggregation(aggr);
    42. // 设置查询结果不返回,只返回聚合结果
    43. searchSourceBuilder.size(0);
    44. // 创建查询请求对象,将查询条件配置到其中
    45. SearchRequest request = new SearchRequest(indexName);
    46. request.source(searchSourceBuilder);
    47. // 执行请求
    48. SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    49. // 获取响应中的聚合信息
    50. Aggregations aggregations = response.getAggregations();
    51. // 输出内容
    52. if (RestStatus.OK.equals(response.status()) || aggregations != null) {
    53. // 转换为 Stats 对象
    54. ParsedStats aggregation = aggregations.get(field + "_stats");
    55. log.info("-------------------------------------------");
    56. log.info("聚合信息: {}", field);
    57. log.info("count:{}", aggregation.getCount());
    58. log.info("avg:{}", aggregation.getAvg());
    59. log.info("max:{}", aggregation.getMax());
    60. log.info("min:{}", aggregation.getMin());
    61. log.info("sum:{}", aggregation.getSum());
    62. log.info("-------------------------------------------");
    63. }
    64. // 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串
    65. responseResult = response.toString();
    66. } catch (IOException e) {
    67. log.error("", e);
    68. }
    69. return responseResult;
    70. }
    71. /**
    72. * min 统计员工工资最低值
    73. */
    74. public Object aggregationMin() {
    75. String responseResult = "";
    76. try {
    77. // 设置聚合条件
    78. AggregationBuilder aggr = AggregationBuilders.min("salary_min").field("salary");
    79. // 查询源构建器
    80. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    81. searchSourceBuilder.aggregation(aggr);
    82. searchSourceBuilder.size(0);
    83. // 创建查询请求对象,将查询条件配置到其中
    84. SearchRequest request = new SearchRequest(indexName);
    85. request.source(searchSourceBuilder);
    86. // 执行请求
    87. SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    88. // 获取响应中的聚合信息
    89. Aggregations aggregations = response.getAggregations();
    90. // 输出内容
    91. if (RestStatus.OK.equals(response.status()) || aggregations != null) {
    92. // 转换为 Min 对象
    93. ParsedMin aggregation = aggregations.get("salary_min");
    94. log.info("-------------------------------------------");
    95. log.info("聚合信息:");
    96. log.info("min:{}", aggregation.getValue());
    97. log.info("-------------------------------------------");
    98. }
    99. // 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串
    100. responseResult = response.toString();
    101. } catch (IOException e) {
    102. log.error("", e);
    103. }
    104. return responseResult;
    105. }
    106. /**
    107. * max 统计员工工资最高值
    108. */
    109. public Object aggregationMax() {
    110. String responseResult = "";
    111. try {
    112. // 设置聚合条件
    113. AggregationBuilder aggr = AggregationBuilders.max("salary_max").field("salary");
    114. // 查询源构建器
    115. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    116. searchSourceBuilder.aggregation(aggr);
    117. searchSourceBuilder.size(0);
    118. // 创建查询请求对象,将查询条件配置到其中
    119. SearchRequest request = new SearchRequest(indexName);
    120. request.source(searchSourceBuilder);
    121. // 执行请求
    122. SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    123. // 获取响应中的聚合信息
    124. Aggregations aggregations = response.getAggregations();
    125. // 输出内容
    126. if (RestStatus.OK.equals(response.status()) || aggregations != null) {
    127. // 转换为 Max 对象
    128. ParsedMax aggregation = aggregations.get("salary_max");
    129. log.info("-------------------------------------------");
    130. log.info("聚合信息:");
    131. log.info("max:{}", aggregation.getValue());
    132. log.info("-------------------------------------------");
    133. }
    134. // 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串
    135. responseResult = response.toString();
    136. } catch (IOException e) {
    137. log.error("", e);
    138. }
    139. return responseResult;
    140. }
    141. /**
    142. * avg 统计员工工资平均值
    143. */
    144. public Object aggregationAvg() {
    145. String responseResult = "";
    146. try {
    147. // 设置聚合条件
    148. AggregationBuilder aggr = AggregationBuilders.avg("salary_avg").field("salary");
    149. // 查询源构建器
    150. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    151. searchSourceBuilder.aggregation(aggr);
    152. searchSourceBuilder.size(0);
    153. // 创建查询请求对象,将查询条件配置到其中
    154. SearchRequest request = new SearchRequest(indexName);
    155. request.source(searchSourceBuilder);
    156. // 执行请求
    157. SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    158. // 获取响应中的聚合信息
    159. Aggregations aggregations = response.getAggregations();
    160. // 输出内容
    161. if (RestStatus.OK.equals(response.status()) || aggregations != null) {
    162. // 转换为 Avg 对象
    163. ParsedAvg aggregation = aggregations.get("salary_avg");
    164. log.info("-------------------------------------------");
    165. log.info("聚合信息:");
    166. log.info("avg:{}", aggregation.getValue());
    167. log.info("-------------------------------------------");
    168. }
    169. // 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串
    170. responseResult = response.toString();
    171. } catch (IOException e) {
    172. log.error("", e);
    173. }
    174. return responseResult;
    175. }
    176. /**
    177. * sum 统计员工工资总值
    178. */
    179. public Object aggregationSum() {
    180. String responseResult = "";
    181. try {
    182. // 设置聚合条件
    183. SumAggregationBuilder aggr = AggregationBuilders.sum("salary_sum").field("salary");
    184. // 查询源构建器
    185. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    186. searchSourceBuilder.aggregation(aggr);
    187. searchSourceBuilder.size(0);
    188. // 创建查询请求对象,将查询条件配置到其中
    189. SearchRequest request = new SearchRequest(indexName);
    190. request.source(searchSourceBuilder);
    191. // 执行请求
    192. SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    193. // 获取响应中的聚合信息
    194. Aggregations aggregations = response.getAggregations();
    195. // 输出内容
    196. if (RestStatus.OK.equals(response.status()) || aggregations != null) {
    197. // 转换为 Sum 对象
    198. ParsedSum aggregation = aggregations.get("salary_sum");
    199. log.info("-------------------------------------------");
    200. log.info("聚合信息:");
    201. log.info("sum:{}", String.valueOf((aggregation.getValue())));
    202. log.info("-------------------------------------------");
    203. }
    204. // 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串
    205. responseResult = response.toString();
    206. } catch (IOException e) {
    207. log.error("", e);
    208. }
    209. return responseResult;
    210. }
    211. /**
    212. * count 统计员工总数
    213. */
    214. public Object aggregationCount() {
    215. String responseResult = "";
    216. try {
    217. // 设置聚合条件
    218. AggregationBuilder aggr = AggregationBuilders.count("employee_count").field("salary");
    219. // 查询源构建器
    220. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    221. searchSourceBuilder.aggregation(aggr);
    222. searchSourceBuilder.size(0);
    223. // 创建查询请求对象,将查询条件配置到其中
    224. SearchRequest request = new SearchRequest(indexName);
    225. request.source(searchSourceBuilder);
    226. // 执行请求
    227. SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    228. // 获取响应中的聚合信息
    229. Aggregations aggregations = response.getAggregations();
    230. // 输出内容
    231. if (RestStatus.OK.equals(response.status()) || aggregations != null) {
    232. // 转换为 ValueCount 对象
    233. ParsedValueCount aggregation = aggregations.get("employee_count");
    234. log.info("-------------------------------------------");
    235. log.info("聚合信息:");
    236. log.info("count:{}", aggregation.getValue());
    237. log.info("-------------------------------------------");
    238. }
    239. // 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串
    240. responseResult = response.toString();
    241. } catch (IOException e) {
    242. log.error("", e);
    243. }
    244. return responseResult;
    245. }
    246. /**
    247. * percentiles 统计员工工资百分位
    248. */
    249. public Object aggregationPercentiles() {
    250. String responseResult = "";
    251. try {
    252. // 设置聚合条件
    253. AggregationBuilder aggr = AggregationBuilders.percentiles("salary_percentiles").field("salary");
    254. // 查询源构建器
    255. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    256. searchSourceBuilder.aggregation(aggr);
    257. searchSourceBuilder.size(0);
    258. // 创建查询请求对象,将查询条件配置到其中
    259. SearchRequest request = new SearchRequest(indexName);
    260. request.source(searchSourceBuilder);
    261. // 执行请求
    262. SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    263. // 获取响应中的聚合信息
    264. Aggregations aggregations = response.getAggregations();
    265. // 输出内容
    266. if (RestStatus.OK.equals(response.status()) || aggregations != null) {
    267. // 转换为 Percentiles 对象
    268. ParsedPercentiles aggregation = aggregations.get("salary_percentiles");
    269. log.info("-------------------------------------------");
    270. log.info("聚合信息:");
    271. for (Percentile percentile : aggregation) {
    272. log.info("百分位:{}:{}", percentile.getPercent(), percentile.getValue());
    273. }
    274. log.info("-------------------------------------------");
    275. }
    276. // 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串
    277. responseResult = response.toString();
    278. } catch (IOException e) {
    279. log.error("", e);
    280. }
    281. return responseResult;
    282. }
    283. }

    2、Bucket 聚合分析

    (1)、Restful 操作示例

    1. GET http://localhost:9200/testindex/_search
    2. 1、按岁数进行聚合分桶,统计各个岁数员工的人数:
    3. //请求
    4. {
    5. "size": 0,
    6. "aggs": {
    7. "age_bucket": {
    8. "terms": {
    9. "field": "age",
    10. "size": "10"
    11. }
    12. }
    13. }
    14. }
    15. 2、按工资范围进行聚合分桶,统计工资在 3000-5000、5000-9000 和 9000 以上的员工信息:
    16. //请求
    17. {
    18. "aggs": {
    19. "salary_range_bucket": {
    20. "range": {
    21. "field": "salary",
    22. "ranges": [
    23. {
    24. "key": "低级员工",
    25. "to": 3000
    26. },{
    27. "key": "中级员工",
    28. "from": 5000,
    29. "to": 9000
    30. },{
    31. "key": "高级员工",
    32. "from": 9000
    33. }
    34. ]
    35. }
    36. }
    37. }
    38. }
    39. 3、按照时间范围进行分桶,统计 1985-1990 年和 1990-1995 年出生的员工信息:
    40. //请求
    41. {
    42. "size": 10,
    43. "aggs": {
    44. "date_range_bucket": {
    45. "date_range": {
    46. "field": "birthDate",
    47. "format": "yyyy",
    48. "ranges": [
    49. {
    50. "key": "出生日期1985-1990的员工",
    51. "from": "1985",
    52. "to": "1990"
    53. },{
    54. "key": "出生日期1990-1995的员工",
    55. "from": "1990",
    56. "to": "1995"
    57. }
    58. ]
    59. }
    60. }
    61. }
    62. }
    63. 4、按工资多少进行聚合分桶,设置统计的最小值为 0,最大值为 12000,区段间隔为 3000:
    64. //请求
    65. {
    66. "size": 0,
    67. "aggs": {
    68. "salary_histogram": {
    69. "histogram": {
    70. "field": "salary",
    71. "extended_bounds": {
    72. "min": 0,
    73. "max": 12000
    74. },
    75. "interval": 3000
    76. }
    77. }
    78. }
    79. }
    80. 5、按出生日期进行分桶:
    81. //请求
    82. {
    83. "size": 0,
    84. "aggs": {
    85. "birthday_histogram": {
    86. "date_histogram": {
    87. "format": "yyyy",
    88. "field": "birthDate",
    89. "interval": "year"
    90. }
    91. }
    92. }
    93. }

    (2)、Java 代码示例 

    1. package com.example.elasticsearch.demos.web.service.aggregation;
    2. import lombok.extern.slf4j.Slf4j;
    3. import org.elasticsearch.action.search.SearchRequest;
    4. import org.elasticsearch.action.search.SearchResponse;
    5. import org.elasticsearch.client.RequestOptions;
    6. import org.elasticsearch.client.RestHighLevelClient;
    7. import org.elasticsearch.rest.RestStatus;
    8. import org.elasticsearch.search.aggregations.AggregationBuilder;
    9. import org.elasticsearch.search.aggregations.AggregationBuilders;
    10. import org.elasticsearch.search.aggregations.Aggregations;
    11. import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
    12. import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
    13. import org.elasticsearch.search.aggregations.bucket.range.Range;
    14. import org.elasticsearch.search.aggregations.bucket.terms.Terms;
    15. import org.elasticsearch.search.builder.SearchSourceBuilder;
    16. import org.springframework.beans.factory.annotation.Autowired;
    17. import org.springframework.beans.factory.annotation.Value;
    18. import org.springframework.stereotype.Service;
    19. import java.io.IOException;
    20. import java.util.HashMap;
    21. import java.util.List;
    22. import java.util.Map;
    23. /**
    24. * 聚合 Bucket
    25. */
    26. @Slf4j
    27. @Service
    28. public class AggrBucketService {
    29. @Autowired
    30. private RestHighLevelClient restHighLevelClient;
    31. @Value("${myindex}")
    32. private String indexName;
    33. /**
    34. * 按岁数进行聚合分桶,统计各个岁数员工的人数:
    35. */
    36. public Object aggrBucketTerms() {
    37. Map keyCountMap = new HashMap<>();
    38. try {
    39. AggregationBuilder aggr = AggregationBuilders.terms("age_bucket").field("age");
    40. // 查询源构建器
    41. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    42. searchSourceBuilder.size(10);
    43. searchSourceBuilder.aggregation(aggr);
    44. // 创建查询请求对象,将查询条件配置到其中
    45. SearchRequest request = new SearchRequest(indexName);
    46. request.source(searchSourceBuilder);
    47. // 执行请求
    48. SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    49. // 获取响应中的聚合信息
    50. Aggregations aggregations = response.getAggregations();
    51. // 输出内容
    52. if (RestStatus.OK.equals(response.status())) {
    53. // 分桶
    54. Terms byCompanyAggregation = aggregations.get("age_bucket");
    55. Listextends Terms.Bucket> buckets = byCompanyAggregation.getBuckets();
    56. // 输出各个桶的内容
    57. log.info("-------------------------------------------");
    58. log.info("聚合信息:");
    59. for (Terms.Bucket bucket : buckets) {
    60. keyCountMap.put(bucket.getKeyAsString(), bucket.getDocCount());
    61. log.info("桶名:{} | 总数:{}", bucket.getKeyAsString(), bucket.getDocCount());
    62. }
    63. log.info("-------------------------------------------");
    64. }
    65. } catch (IOException e) {
    66. log.error("", e);
    67. }
    68. return keyCountMap;
    69. }
    70. /**
    71. * 按工资范围进行聚合分桶,统计工资在 3000-5000、5000-9000 和 9000 以上的员工信息:
    72. */
    73. public Object aggrBucketRange() {
    74. Map keyCountMap = new HashMap<>();
    75. try {
    76. AggregationBuilder aggr = AggregationBuilders.range("salary_range_bucket")
    77. .field("salary")
    78. .addUnboundedTo("低级员工", 3000)
    79. .addRange("中级员工", 5000, 9000)
    80. .addUnboundedFrom("高级员工", 9000);
    81. // 查询源构建器
    82. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    83. searchSourceBuilder.size(0);
    84. searchSourceBuilder.aggregation(aggr);
    85. // 创建查询请求对象,将查询条件配置到其中
    86. SearchRequest request = new SearchRequest(indexName);
    87. request.source(searchSourceBuilder);
    88. // 执行请求
    89. SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    90. // 获取响应中的聚合信息
    91. Aggregations aggregations = response.getAggregations();
    92. // 输出内容
    93. if (RestStatus.OK.equals(response.status())) {
    94. // 分桶
    95. Range byCompanyAggregation = aggregations.get("salary_range_bucket");
    96. Listextends Range.Bucket> buckets = byCompanyAggregation.getBuckets();
    97. // 输出各个桶的内容
    98. log.info("-------------------------------------------");
    99. log.info("聚合信息:");
    100. for (Range.Bucket bucket : buckets) {
    101. keyCountMap.put(bucket.getKeyAsString(), bucket.getDocCount());
    102. log.info("桶名:{} | 总数:{}", bucket.getKeyAsString(), bucket.getDocCount());
    103. }
    104. log.info("-------------------------------------------");
    105. }
    106. } catch (IOException e) {
    107. log.error("", e);
    108. }
    109. return keyCountMap;
    110. }
    111. /**
    112. * 按照时间范围进行分桶,统计 1985-1990 年和 1990-1995 年出生的员工信息:
    113. */
    114. public Object aggrBucketDateRange() {
    115. Map keyCountMap = new HashMap<>();
    116. try {
    117. AggregationBuilder aggr = AggregationBuilders.dateRange("date_range_bucket")
    118. .field("birthDate")
    119. .format("yyyy")
    120. .addRange("出生日期1985-1990的员工", "1985", "1990")
    121. .addRange("出生日期1990-1995的员工", "1990", "1995");
    122. // 查询源构建器
    123. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    124. searchSourceBuilder.size(0);
    125. searchSourceBuilder.aggregation(aggr);
    126. // 创建查询请求对象,将查询条件配置到其中
    127. SearchRequest request = new SearchRequest(indexName);
    128. request.source(searchSourceBuilder);
    129. // 执行请求
    130. SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    131. // 获取响应中的聚合信息
    132. Aggregations aggregations = response.getAggregations();
    133. // 输出内容
    134. if (RestStatus.OK.equals(response.status())) {
    135. // 分桶
    136. Range byCompanyAggregation = aggregations.get("date_range_bucket");
    137. Listextends Range.Bucket> buckets = byCompanyAggregation.getBuckets();
    138. // 输出各个桶的内容
    139. log.info("-------------------------------------------");
    140. log.info("聚合信息:");
    141. for (Range.Bucket bucket : buckets) {
    142. keyCountMap.put(bucket.getKeyAsString(), bucket.getDocCount());
    143. log.info("桶名:{} | 总数:{}", bucket.getKeyAsString(), bucket.getDocCount());
    144. }
    145. log.info("-------------------------------------------");
    146. }
    147. } catch (IOException e) {
    148. log.error("", e);
    149. }
    150. return keyCountMap;
    151. }
    152. /**
    153. * 按工资多少进行聚合分桶
    154. */
    155. public Object aggrBucketHistogram() {
    156. Map keyCountMap = new HashMap<>();
    157. try {
    158. //按工资多少进行聚合分桶,设置统计的最小值为 0,最大值为 12000,区段间隔为 3000:
    159. AggregationBuilder aggr = AggregationBuilders.histogram("salary_histogram")
    160. .field("salary")
    161. .extendedBounds(0, 12000)
    162. .interval(3000);
    163. // 查询源构建器
    164. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    165. searchSourceBuilder.size(0);
    166. searchSourceBuilder.aggregation(aggr);
    167. // 创建查询请求对象,将查询条件配置到其中
    168. SearchRequest request = new SearchRequest(indexName);
    169. request.source(searchSourceBuilder);
    170. // 执行请求
    171. SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    172. // 获取响应中的聚合信息
    173. Aggregations aggregations = response.getAggregations();
    174. // 输出内容
    175. if (RestStatus.OK.equals(response.status())) {
    176. // 分桶
    177. Histogram byCompanyAggregation = aggregations.get("salary_histogram");
    178. Listextends Histogram.Bucket> buckets = byCompanyAggregation.getBuckets();
    179. // 输出各个桶的内容
    180. log.info("-------------------------------------------");
    181. log.info("聚合信息:");
    182. for (Histogram.Bucket bucket : buckets) {
    183. keyCountMap.put(bucket.getKeyAsString(), bucket.getDocCount());
    184. log.info("桶名:{} | 总数:{}", bucket.getKeyAsString(), bucket.getDocCount());
    185. }
    186. log.info("-------------------------------------------");
    187. }
    188. } catch (IOException e) {
    189. log.error("", e);
    190. }
    191. return keyCountMap;
    192. }
    193. /**
    194. * 按出生日期进行分桶:
    195. */
    196. public Object aggrBucketDateHistogram() {
    197. Map keyCountMap = new HashMap<>();
    198. try {
    199. AggregationBuilder aggr = AggregationBuilders.dateHistogram("birthday_histogram")
    200. .field("birthDate")
    201. .interval(1)
    202. .dateHistogramInterval(DateHistogramInterval.YEAR)
    203. .format("yyyy");
    204. // 查询源构建器
    205. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    206. searchSourceBuilder.size(0);
    207. searchSourceBuilder.aggregation(aggr);
    208. // 创建查询请求对象,将查询条件配置到其中
    209. SearchRequest request = new SearchRequest(indexName);
    210. request.source(searchSourceBuilder);
    211. // 执行请求
    212. SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    213. // 获取响应中的聚合信息
    214. Aggregations aggregations = response.getAggregations();
    215. // 输出内容
    216. if (RestStatus.OK.equals(response.status())) {
    217. // 分桶
    218. Histogram byCompanyAggregation = aggregations.get("birthday_histogram");
    219. Listextends Histogram.Bucket> buckets = byCompanyAggregation.getBuckets();
    220. // 输出各个桶的内容
    221. log.info("-------------------------------------------");
    222. log.info("聚合信息:");
    223. for (Histogram.Bucket bucket : buckets) {
    224. keyCountMap.put(bucket.getKeyAsString(), bucket.getDocCount());
    225. log.info("桶名:{} | 总数:{}", bucket.getKeyAsString(), bucket.getDocCount());
    226. }
    227. log.info("-------------------------------------------");
    228. }
    229. } catch (IOException e) {
    230. log.error("", e);
    231. }
    232. return keyCountMap;
    233. }
    234. }

    3、Metric 与 Bucket 聚合分析

    (1)、Restful 操作示例

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

    1. GET http://localhost:9200/testindex/_search
    2. //请求
    3. {
    4. "size": 0,
    5. "aggs": {
    6. "salary_bucket": {
    7. "terms": {
    8. "field": "age",
    9. "size": "10"
    10. },
    11. "aggs": {
    12. "salary_max_user": {
    13. "top_hits": {
    14. "size": 1,
    15. "sort": [
    16. {
    17. "salary": {
    18. "order": "desc"
    19. }
    20. }
    21. ]
    22. }
    23. }
    24. }
    25. }
    26. }
    27. }

    (2)、Java 代码示例

    1. package com.example.elasticsearch.demos.web.service.aggregation;
    2. import com.alibaba.fastjson.JSON;
    3. import com.example.elasticsearch.demos.web.model.entity.UserInfo;
    4. import lombok.extern.slf4j.Slf4j;
    5. import org.elasticsearch.action.search.SearchRequest;
    6. import org.elasticsearch.action.search.SearchResponse;
    7. import org.elasticsearch.client.RequestOptions;
    8. import org.elasticsearch.client.RestHighLevelClient;
    9. import org.elasticsearch.rest.RestStatus;
    10. import org.elasticsearch.search.SearchHit;
    11. import org.elasticsearch.search.aggregations.AggregationBuilder;
    12. import org.elasticsearch.search.aggregations.AggregationBuilders;
    13. import org.elasticsearch.search.aggregations.Aggregations;
    14. import org.elasticsearch.search.aggregations.bucket.terms.Terms;
    15. import org.elasticsearch.search.aggregations.metrics.ParsedTopHits;
    16. import org.elasticsearch.search.builder.SearchSourceBuilder;
    17. import org.elasticsearch.search.sort.SortOrder;
    18. import org.springframework.beans.factory.annotation.Autowired;
    19. import org.springframework.beans.factory.annotation.Value;
    20. import org.springframework.stereotype.Service;
    21. import java.io.IOException;
    22. import java.util.HashMap;
    23. import java.util.List;
    24. import java.util.Map;
    25. /**
    26. * 聚合 Bucket 与 Metric
    27. */
    28. @Slf4j
    29. @Service
    30. public class AggrBucketMetricService {
    31. @Autowired
    32. private RestHighLevelClient restHighLevelClient;
    33. @Value("${myindex}")
    34. private String indexName;
    35. /**
    36. * topHits 按照员工岁数分桶、然后统计每个岁数员工工资最高值
    37. */
    38. public Object aggregationTopHits() {
    39. Map ageMaxSalaryMap = new HashMap<>();
    40. try {
    41. AggregationBuilder testTop = AggregationBuilders.topHits("salary_max_user")
    42. .size(1)
    43. .sort("salary", SortOrder.DESC);
    44. AggregationBuilder salaryBucket = AggregationBuilders.terms("salary_bucket")
    45. .field("age")
    46. .size(10);
    47. salaryBucket.subAggregation(testTop);
    48. // 查询源构建器
    49. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    50. searchSourceBuilder.size(0);
    51. searchSourceBuilder.aggregation(salaryBucket);
    52. // 创建查询请求对象,将查询条件配置到其中
    53. SearchRequest request = new SearchRequest(indexName);
    54. request.source(searchSourceBuilder);
    55. // 执行请求
    56. SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    57. // 获取响应中的聚合信息
    58. Aggregations aggregations = response.getAggregations();
    59. // 输出内容
    60. if (RestStatus.OK.equals(response.status())) {
    61. // 分桶
    62. Terms byCompanyAggregation = aggregations.get("salary_bucket");
    63. Listextends Terms.Bucket> buckets = byCompanyAggregation.getBuckets();
    64. // 输出各个桶的内容
    65. log.info("-------------------------------------------");
    66. log.info("聚合信息:");
    67. for (Terms.Bucket bucket : buckets) {
    68. log.info("桶名:{}", bucket.getKeyAsString());
    69. ParsedTopHits topHits = bucket.getAggregations().get("salary_max_user");
    70. for (SearchHit hit : topHits.getHits()) {
    71. UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
    72. ageMaxSalaryMap.put(bucket.getKeyAsString(), userInfo.getSalary());
    73. log.info(hit.getSourceAsString());
    74. }
    75. }
    76. log.info("-------------------------------------------");
    77. }
    78. } catch (IOException e) {
    79. log.error("", e);
    80. }
    81. return ageMaxSalaryMap;
    82. }
    83. }

    九、项目源码及对应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

     

  • 相关阅读:
    Asymmetric channel bandwidths(非对称信道带宽)
    drools规则引擎04
    【Python】简记操作:Centos安装Python3虚拟环境-virtualenvwrapper
    Overleaf(Latex)论文里插入高清matlab图像,亲测有效!!
    VP牛客小白月赛88(A---F)(收获很多)
    基于ARM的环境参数检测系统设计(Labview+STM32+ZigBee)
    OpenAI:我们暂停了ChatGPT Plus新用户注册
    使用Spring Security保障你的Web应用安全
    个保法(PIPL)颁布实施一周年,给行业带来了哪些变化?
    【HBZ分享】ES的聚合函数汇总
  • 原文地址:https://blog.csdn.net/asd051377305/article/details/133581804