Java 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 查询的使用其上手很快。

1、Maven 引入相关依赖

  • lombok:lombok 工具依赖。
  • fastjson:用于将 JSON 转换对象的依赖。
  • spring-boot-starter-web: SpringBoot 的 Web 依赖。
  • elasticsearch:ElasticSearch:依赖,需要和 ES 版本保持一致。
  • elasticsearch-rest-high-level-client:用于操作 ES 的 Java 客户端。
    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <project xmlns="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. <modelVersion>4.0.0</modelVersion>
    5. <parent>
    6. <groupId>org.springframework.boot</groupId>
    7. <artifactId>spring-boot-starter-parent</artifactId>
    8. <version>2.2.4.RELEASE</version>
    9. <relativePath/> <!-- lookup parent from repository -->
    10. </parent>
    11. <groupId>club.mydlq</groupId>
    12. <artifactId>springboot-elasticsearch-example</artifactId>
    13. <version>0.0.1-SNAPSHOT</version>
    14. <name>springboot-elasticsearch-example</name>
    15. <description>Demo project for Spring Boot ElasticSearch</description>
    16. <properties>
    17. <java.version>1.8</java.version>
    18. </properties>
    19. <dependencies>
    20. <!--web-->
    21. <dependency>
    22. <groupId>org.springframework.boot</groupId>
    23. <artifactId>spring-boot-starter-web</artifactId>
    24. </dependency>
    25. <!--lombok-->
    26. <dependency>
    27. <groupId>org.projectlombok</groupId>
    28. <artifactId>lombok</artifactId>
    29. <optional>true</optional>
    30. </dependency>
    31. <!--fastjson-->
    32. <dependency>
    33. <groupId>com.alibaba</groupId>
    34. <artifactId>fastjson</artifactId>
    35. <version>1.2.61</version>
    36. </dependency>
    37. <!--elasticsearch-->
    38. <dependency>
    39. <groupId>org.elasticsearch.client</groupId>
    40. <artifactId>elasticsearch-rest-high-level-client</artifactId>
    41. <version>6.5.4</version>
    42. </dependency>
    43. <dependency>
    44. <groupId>org.elasticsearch</groupId>
    45. <artifactId>elasticsearch</artifactId>
    46. <version>6.5.4</version>
    47. </dependency>
    48. </dependencies>
    49. <build>
    50. <plugins>
    51. <plugin>
    52. <groupId>org.springframework.boot</groupId>
    53. <artifactId>spring-boot-maven-plugin</artifactId>
    54. </plugin>
    55. </plugins>
    56. </build>
    57. </project>

    2、ElasticSearch 连接配置

    (1)、application.yml 配置文件

    为了方便更改连接 ES 的连接配置,所以将配置信息放置于 application.yaml 中:
    1. #base
    2. server:
    3. port: 8080
    4. #spring
    5. spring:
    6. application:
    7. name: springboot-elasticsearch-example
    8. #elasticsearch
    9. elasticsearch:
    10. schema: http
    11. address: 127.0.0.1:9200
    12. connectTimeout: 5000
    13. socketTimeout: 5000
    14. connectionRequestTimeout: 5000
    15. maxConnectNum: 100
    16. maxConnectPerRoute: 100

    (2)、java 连接配置类

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

    四、索引操作示例

    这里示例会指出通过 Kibana 的 Restful 工具操作与对应的 Java 代码操作的两个示例。扩展:某小公司RESTful、共用接口、前后端分离、接口约定的实践

    1、Restful 操作示例

    创建索引

    创建名为 mydlq-user 的索引与对应 Mapping。
    1. PUT /mydlq-user
    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 索引。
    1. DELETE /mydlq-user

    2、Java 代码示例

    1. import lombok.extern.slf4j.Slf4j;
    2. import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
    3. import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
    4. import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
    5. import org.elasticsearch.action.support.master.AcknowledgedResponse;
    6. import org.elasticsearch.client.RequestOptions;
    7. import org.elasticsearch.client.RestHighLevelClient;
    8. import org.elasticsearch.common.settings.Settings;
    9. import org.elasticsearch.common.xcontent.XContentBuilder;
    10. import org.elasticsearch.common.xcontent.XContentFactory;
    11. import org.springframework.beans.factory.annotation.Autowired;
    12. import org.springframework.stereotype.Service;
    13. import java.io.IOException;
    14. @Slf4j
    15. @Service
    16. public class IndexService2 {
    17. @Autowired
    18. private RestHighLevelClient restHighLevelClient;
    19. /**
    20. * 创建索引
    21. */
    22. public void createIndex() {
    23. try {
    24. // 创建 Mapping
    25. XContentBuilder mapping = XContentFactory.jsonBuilder()
    26. .startObject()
    27. .field("dynamic", true)
    28. .startObject("properties")
    29. .startObject("name")
    30. .field("type","text")
    31. .startObject("fields")
    32. .startObject("keyword")
    33. .field("type","keyword")
    34. .endObject()
    35. .endObject()
    36. .endObject()
    37. .startObject("address")
    38. .field("type","text")
    39. .startObject("fields")
    40. .startObject("keyword")
    41. .field("type","keyword")
    42. .endObject()
    43. .endObject()
    44. .endObject()
    45. .startObject("remark")
    46. .field("type","text")
    47. .startObject("fields")
    48. .startObject("keyword")
    49. .field("type","keyword")
    50. .endObject()
    51. .endObject()
    52. .endObject()
    53. .startObject("age")
    54. .field("type","integer")
    55. .endObject()
    56. .startObject("salary")
    57. .field("type","float")
    58. .endObject()
    59. .startObject("birthDate")
    60. .field("type","date")
    61. .field("format", "yyyy-MM-dd")
    62. .endObject()
    63. .startObject("createTime")
    64. .field("type","date")
    65. .endObject()
    66. .endObject()
    67. .endObject();
    68. // 创建索引配置信息,配置
    69. Settings settings = Settings.builder()
    70. .put("index.number_of_shards", 1)
    71. .put("index.number_of_replicas", 0)
    72. .build();
    73. // 新建创建索引请求对象,然后设置索引类型(ES 7.0 将不存在索引类型)和 mapping 与 index 配置
    74. CreateIndexRequest request = new CreateIndexRequest("mydlq-user", settings);
    75. request.mapping("doc", mapping);
    76. // RestHighLevelClient 执行创建索引
    77. CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
    78. // 判断是否创建成功
    79. boolean isCreated = createIndexResponse.isAcknowledged();
    80. log.info("是否创建成功:{}", isCreated);
    81. } catch (IOException e) {
    82. log.error("", e);
    83. }
    84. }
    85. /**
    86. * 删除索引
    87. */
    88. public void deleteIndex() {
    89. try {
    90. // 新建删除索引请求对象
    91. DeleteIndexRequest request = new DeleteIndexRequest("mydlq-user");
    92. // 执行删除索引
    93. AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
    94. // 判断是否删除成功
    95. boolean siDeleted = acknowledgedResponse.isAcknowledged();
    96. log.info("是否删除成功:{}", siDeleted);
    97. } catch (IOException e) {
    98. log.error("", e);
    99. }
    100. }
    101. }

    五、文档操作示例

    1、Restful 操作示例

    增加文档信息

    在索引 mydlq-user 中增加一条文档信息。
    1. POST /mydlq-user/doc
    2. {
    3. "address": "北京市",
    4. "age": 29,
    5. "birthDate": "1990-01-10",
    6. "createTime": 1579530727699,
    7. "name": "张三",
    8. "remark": "来自北京市的张先生",
    9. "salary": 100
    10. }

    获取文档信息

    获取 mydlq-user 的索引 id=1 的文档信息。
    1. GET /mydlq-user/doc/1

    更新文档信息

    更新之前创建的 id=1 的文档信息。
    1. PUT /mydlq-user/doc/1
    2. {
    3. "address": "北京市海淀区",
    4. "age": 29,
    5. "birthDate": "1990-01-10",
    6. "createTime": 1579530727699,
    7. "name": "张三",
    8. "remark": "来自北京市的张先生",
    9. "salary": 100
    10. }

    删除文档信息

    删除之前创建的 id=1 的文档信息。
    1. DELETE /mydlq-user/doc/1

    2、Java 代码示例

    1. import club.mydlq.elasticsearch.model.entity.UserInfo;
    2. import com.alibaba.fastjson.JSON;
    3. import lombok.extern.slf4j.Slf4j;
    4. import org.elasticsearch.action.delete.DeleteRequest;
    5. import org.elasticsearch.action.delete.DeleteResponse;
    6. import org.elasticsearch.action.get.GetRequest;
    7. import org.elasticsearch.action.get.GetResponse;
    8. import org.elasticsearch.action.index.IndexRequest;
    9. import org.elasticsearch.action.index.IndexResponse;
    10. import org.elasticsearch.action.update.UpdateRequest;
    11. import org.elasticsearch.action.update.UpdateResponse;
    12. import org.elasticsearch.client.RequestOptions;
    13. import org.elasticsearch.client.RestHighLevelClient;
    14. import org.elasticsearch.common.xcontent.XContentType;
    15. import org.springframework.beans.factory.annotation.Autowired;
    16. import org.springframework.stereotype.Service;
    17. import java.io.IOException;
    18. import java.util.Date;
    19. @Slf4j
    20. @Service
    21. public class IndexService {
    22. @Autowired
    23. private RestHighLevelClient restHighLevelClient;
    24. /**
    25. * 增加文档信息
    26. */
    27. public void addDocument() {
    28. try {
    29. // 创建索引请求对象
    30. IndexRequest indexRequest = new IndexRequest("mydlq-user", "doc", "1");
    31. // 创建员工信息
    32. UserInfo userInfo = new UserInfo();
    33. userInfo.setName("张三");
    34. userInfo.setAge(29);
    35. userInfo.setSalary(100.00f);
    36. userInfo.setAddress("北京市");
    37. userInfo.setRemark("来自北京市的张先生");
    38. userInfo.setCreateTime(new Date());
    39. userInfo.setBirthDate("1990-01-10");
    40. // 将对象转换为 byte 数组
    41. byte[] json = JSON.toJSONBytes(userInfo);
    42. // 设置文档内容
    43. indexRequest.source(json, XContentType.JSON);
    44. // 执行增加文档
    45. IndexResponse response = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
    46. log.info("创建状态:{}", response.status());
    47. } catch (Exception e) {
    48. log.error("", e);
    49. }
    50. }
    51. /**
    52. * 获取文档信息
    53. */
    54. public void getDocument() {
    55. try {
    56. // 获取请求对象
    57. GetRequest getRequest = new GetRequest("mydlq-user", "doc", "1");
    58. // 获取文档信息
    59. GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
    60. // 将 JSON 转换成对象
    61. if (getResponse.isExists()) {
    62. UserInfo userInfo = JSON.parseObject(getResponse.getSourceAsBytes(), UserInfo.class);
    63. log.info("员工信息:{}", userInfo);
    64. }
    65. } catch (IOException e) {
    66. log.error("", e);
    67. }
    68. }
    69. /**
    70. * 更新文档信息
    71. */
    72. public void updateDocument() {
    73. try {
    74. // 创建索引请求对象
    75. UpdateRequest updateRequest = new UpdateRequest("mydlq-user", "doc", "1");
    76. // 设置员工更新信息
    77. UserInfo userInfo = new UserInfo();
    78. userInfo.setSalary(200.00f);
    79. userInfo.setAddress("北京市海淀区");
    80. // 将对象转换为 byte 数组
    81. byte[] json = JSON.toJSONBytes(userInfo);
    82. // 设置更新文档内容
    83. updateRequest.doc(json, XContentType.JSON);
    84. // 执行更新文档
    85. UpdateResponse response = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
    86. log.info("创建状态:{}", response.status());
    87. } catch (Exception e) {
    88. log.error("", e);
    89. }
    90. }
    91. /**
    92. * 删除文档信息
    93. */
    94. public void deleteDocument() {
    95. try {
    96. // 创建删除请求对象
    97. DeleteRequest deleteRequest = new DeleteRequest("mydlq-user", "doc", "1");
    98. // 执行删除文档
    99. DeleteResponse response = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
    100. log.info("删除状态:{}", response.status());
    101. } catch (IOException e) {
    102. log.error("", e);
    103. }
    104. }
    105. }

    六、插入初始化数据

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

    1、单条插入

    POST mydlq-user/_doc
    1. {"name":"零零","address":"北京市丰台区","remark":"低层员工","age":29,"salary":3000,"birthDate":"1990-11-11","createTime":"2019-11-11T08:18:00.000Z"}

    2、批量插入

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

    3、查询数据

    插入完成后再查询数据,查看之前插入的数据是否存在:
    1. GET mydlq-user/_search
    执行后得到下面记录:
    1. {
    2. "took": 2,
    3. "timed_out": false,
    4. "_shards": {
    5. "total": 1,
    6. "successful": 1,
    7. "skipped": 0,
    8. "failed": 0
    9. },
    10. "hits": {
    11. "total": 20,
    12. "max_score": 1,
    13. "hits": [
    14. {
    15. "_index": "mydlq-user",
    16. "_type": "_doc",
    17. "_id": "BeN0BW8B7BNodGwRFTRj",
    18. "_score": 1,
    19. "_source": {
    20. "name": "刘一",
    21. "address": "北京市丰台区",
    22. "remark": "低层员工",
    23. "age": 30,
    24. "salary": 3000,
    25. "birthDate": "1989-11-11",
    26. "createTime": "2019-03-15T08:18:00.000Z"
    27. }
    28. },
    29. {
    30. "_index": "mydlq-user",
    31. "_type": "_doc",
    32. "_id": "BuN0BW8B7BNodGwRFTRj",
    33. "_score": 1,
    34. "_source": {
    35. "name": "陈二",
    36. "address": "北京市昌平区",
    37. "remark": "中层员工",
    38. "age": 27,
    39. "salary": 7900,
    40. "birthDate": "1992-01-25",
    41. "createTime": "2019-11-08T11:15:00.000Z"
    42. }
    43. },
    44. {
    45. "_index": "mydlq-user",
    46. "_type": "_doc",
    47. "_id": "B-N0BW8B7BNodGwRFTRj",
    48. "_score": 1,
    49. "_source": {
    50. "name": "张三",
    51. "address": "北京市房山区",
    52. "remark": "中层员工",
    53. "age": 28,
    54. "salary": 8800,
    55. "birthDate": "1991-10-05",
    56. "createTime": "2019-07-22T13:22:00.000Z"
    57. }
    58. },
    59. {
    60. "_index": "mydlq-user",
    61. "_type": "_doc",
    62. "_id": "CON0BW8B7BNodGwRFTRj",
    63. "_score": 1,
    64. "_source": {
    65. "name": "李四",
    66. "address": "北京市大兴区",
    67. "remark": "高层员工",
    68. "age": 26,
    69. "salary": 9000,
    70. "birthDate": "1993-08-18",
    71. "createTime": "2019-10-17T15:00:00.000Z"
    72. }
    73. },
    74. {
    75. "_index": "mydlq-user",
    76. "_type": "_doc",
    77. "_id": "CeN0BW8B7BNodGwRFTRj",
    78. "_score": 1,
    79. "_source": {
    80. "name": "王五",
    81. "address": "北京市密云区",
    82. "remark": "低层员工",
    83. "age": 31,
    84. "salary": 4800,
    85. "birthDate": "1988-07-20",
    86. "createTime": "2019-05-29T09:00:00.000Z"
    87. }
    88. },
    89. {
    90. "_index": "mydlq-user",
    91. "_type": "_doc",
    92. "_id": "CuN0BW8B7BNodGwRFTRj",
    93. "_score": 1,
    94. "_source": {
    95. "name": "赵六",
    96. "address": "北京市通州区",
    97. "remark": "中层员工",
    98. "age": 32,
    99. "salary": 6500,
    100. "birthDate": "1987-06-02",
    101. "createTime": "2019-12-10T18:00:00.000Z"
    102. }
    103. },
    104. {
    105. "_index": "mydlq-user",
    106. "_type": "_doc",
    107. "_id": "C-N0BW8B7BNodGwRFTRj",
    108. "_score": 1,
    109. "_source": {
    110. "name": "孙七",
    111. "address": "北京市朝阳区",
    112. "remark": "中层员工",
    113. "age": 33,
    114. "salary": 7000,
    115. "birthDate": "1986-04-15",
    116. "createTime": "2019-06-06T13:00:00.000Z"
    117. }
    118. },
    119. {
    120. "_index": "mydlq-user",
    121. "_type": "_doc",
    122. "_id": "DON0BW8B7BNodGwRFTRj",
    123. "_score": 1,
    124. "_source": {
    125. "name": "周八",
    126. "address": "北京市西城区",
    127. "remark": "低层员工",
    128. "age": 32,
    129. "salary": 5000,
    130. "birthDate": "1987-09-26",
    131. "createTime": "2019-01-26T14:00:00.000Z"
    132. }
    133. },
    134. {
    135. "_index": "mydlq-user",
    136. "_type": "_doc",
    137. "_id": "DeN0BW8B7BNodGwRFTRj",
    138. "_score": 1,
    139. "_source": {
    140. "name": "吴九",
    141. "address": "北京市海淀区",
    142. "remark": "高层员工",
    143. "age": 30,
    144. "salary": 11000,
    145. "birthDate": "1989-11-25",
    146. "createTime": "2019-09-07T13:34:00.000Z"
    147. }
    148. },
    149. {
    150. "_index": "mydlq-user",
    151. "_type": "_doc",
    152. "_id": "DuN0BW8B7BNodGwRFTRj",
    153. "_score": 1,
    154. "_source": {
    155. "name": "郑十",
    156. "address": "北京市东城区",
    157. "remark": "低层员工",
    158. "age": 29,
    159. "salary": 5000,
    160. "birthDate": "1990-12-25",
    161. "createTime": "2019-03-06T12:08:00.000Z"
    162. }
    163. }
    164. ]
    165. }
    166. }

    七、查询操作示例

    1、精确查询(term)

    (1)、Restful 操作示例

    精确查询
    精确查询,查询地址为 北京市通州区 的人员信息:
    查询条件不会进行分词,但是查询内容可能会分词,导致查询不到。之前在创建索引时设置 Mapping 中 address 字段存在 keyword 字段是专门用于不分词查询的子字段。
    1. GET mydlq-user/_search
    2. {
    3. "query": {
    4. "term": {
    5. "address.keyword": {
    6. "value": "北京市通州区"
    7. }
    8. }
    9. }
    10. }
    精确查询-多内容查询
    精确查询,查询地址为 北京市丰台区、北京市昌平区 或 北京市大兴区 的人员信息:
    1. GET mydlq-user/_search
    2. {
    3. "query": {
    4. "terms": {
    5. "address.keyword": [
    6. "北京市丰台区",
    7. "北京市昌平区",
    8. "北京市大兴区"
    9. ]
    10. }
    11. }
    12. }

    (2)、Java 代码示例

    1. import club.mydlq.elasticsearch.model.entity.UserInfo;
    2. import com.alibaba.fastjson.JSON;
    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.index.query.QueryBuilders;
    9. import org.elasticsearch.rest.RestStatus;
    10. import org.elasticsearch.search.SearchHit;
    11. import org.elasticsearch.search.SearchHits;
    12. import org.elasticsearch.search.builder.SearchSourceBuilder;
    13. import org.springframework.beans.factory.annotation.Autowired;
    14. import org.springframework.stereotype.Service;
    15. import java.io.IOException;
    16. @Slf4j
    17. @Service
    18. public class TermQueryService {
    19. @Autowired
    20. private RestHighLevelClient restHighLevelClient;
    21. /**
    22. * 精确查询(查询条件不会进行分词,但是查询内容可能会分词,导致查询不到)
    23. */
    24. public void termQuery() {
    25. try {
    26. // 构建查询条件(注意:termQuery 支持多种格式查询,如 boolean、int、double、string 等,这里使用的是 string 的查询)
    27. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    28. searchSourceBuilder.query(QueryBuilders.termQuery("address.keyword", "北京市通州区"));
    29. // 创建查询请求对象,将查询对象配置到其中
    30. SearchRequest searchRequest = new SearchRequest("mydlq-user");
    31. searchRequest.source(searchSourceBuilder);
    32. // 执行查询,然后处理响应结果
    33. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    34. // 根据状态和数据条数验证是否返回了数据
    35. if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().totalHits > 0) {
    36. SearchHits hits = searchResponse.getHits();
    37. for (SearchHit hit : hits) {
    38. // 将 JSON 转换成对象
    39. UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
    40. // 输出查询信息
    41. log.info(userInfo.toString());
    42. }
    43. }
    44. } catch (IOException e) {
    45. log.error("", e);
    46. }
    47. }
    48. /**
    49. * 多个内容在一个字段中进行查询
    50. */
    51. public void termsQuery() {
    52. try {
    53. // 构建查询条件(注意:termsQuery 支持多种格式查询,如 boolean、int、double、string 等,这里使用的是 string 的查询)
    54. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    55. searchSourceBuilder.query(QueryBuilders.termsQuery("address.keyword", "北京市丰台区", "北京市昌平区", "北京市大兴区"));
    56. // 创建查询请求对象,将查询对象配置到其中
    57. SearchRequest searchRequest = new SearchRequest("mydlq-user");
    58. searchRequest.source(searchSourceBuilder);
    59. // 执行查询,然后处理响应结果
    60. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    61. // 根据状态和数据条数验证是否返回了数据
    62. if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().totalHits > 0) {
    63. SearchHits hits = searchResponse.getHits();
    64. for (SearchHit hit : hits) {
    65. // 将 JSON 转换成对象
    66. UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
    67. // 输出查询信息
    68. log.info(userInfo.toString());
    69. }
    70. }
    71. } catch (IOException e) {
    72. log.error("", e);
    73. }
    74. }
    75. }

    2、匹配查询(match)

    (1)、Restful 操作示例

    匹配查询全部数据与分页
    匹配查询符合条件的所有数据,并且设置以 salary 字段升序排序,并设置分页:
    1. GET mydlq-user/_search
    2. {
    3. "query": {
    4. "match_all": {}
    5. },
    6. "from": 0,
    7. "size": 10,
    8. "sort": [
    9. {
    10. "salary": {
    11. "order": "asc"
    12. }
    13. }
    14. ]
    15. }
    匹配查询数据
    匹配查询地址为 通州区 的数据:
    1. GET mydlq-user/_search
    2. {
    3. "query": {
    4. "match": {
    5. "address": "通州区"
    6. }
    7. }
    8. }
    词语匹配查询
    词语匹配进行查询,匹配 address 中为 北京市通州区 的员工信息:
    1. GET mydlq-user/_search
    2. {
    3. "query": {
    4. "match_phrase": {
    5. "address": "北京市通州区"
    6. }
    7. }
    8. }
    内容多字段查询
    查询在字段 address、remark 中存在 北京 内容的员工信息:
    1. GET mydlq-user/_search
    2. {
    3. "query": {
    4. "multi_match": {
    5. "query": "北京",
    6. "fields": ["address","remark"]
    7. }
    8. }
    9. }

    (2)、Java 代码示例

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

    3、模糊查询(fuzzy)

    (1)、Restful 操作示例

    模糊查询所有以 三 结尾的姓名
    1. GET mydlq-user/_search
    2. {
    3. "query": {
    4. "fuzzy": {
    5. "name": "三"
    6. }
    7. }
    8. }

    (2)、Java 代码示例

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

    4、范围查询(range)

    (1)、Restful 操作示例

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

    (2)、Java 代码示例

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

    5、通配符查询(wildcard)

    (1)、Restful 操作示例

    查询所有以 “三” 结尾的姓名:
    1. GET mydlq-user/_search
    2. {
    3. "query": {
    4. "wildcard": {
    5. "name.keyword": {
    6. "value": "*三"
    7. }
    8. }
    9. }
    10. }

    (2)、Java 代码示例

    1. import club.mydlq.elasticsearch.model.entity.UserInfo;
    2. import com.alibaba.fastjson.JSON;
    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.index.query.QueryBuilders;
    9. import org.elasticsearch.rest.RestStatus;
    10. import org.elasticsearch.search.SearchHit;
    11. import org.elasticsearch.search.SearchHits;
    12. import org.elasticsearch.search.builder.SearchSourceBuilder;
    13. import org.springframework.beans.factory.annotation.Autowired;
    14. import org.springframework.stereotype.Service;
    15. import java.io.IOException;
    16. @Slf4j
    17. @Service
    18. public class WildcardQueryService {
    19. @Autowired
    20. private RestHighLevelClient restHighLevelClient;
    21. /**
    22. * 查询所有以 “三” 结尾的姓名
    23. *
    24. * *:表示多个字符(0个或多个字符)
    25. * ?:表示单个字符
    26. */
    27. public Object wildcardQuery() {
    28. try {
    29. // 构建查询条件
    30. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    31. searchSourceBuilder.query(QueryBuilders.wildcardQuery("name.keyword", "*三"));
    32. // 创建查询请求对象,将查询对象配置到其中
    33. SearchRequest searchRequest = new SearchRequest("mydlq-user");
    34. searchRequest.source(searchSourceBuilder);
    35. // 执行查询,然后处理响应结果
    36. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    37. // 根据状态和数据条数验证是否返回了数据
    38. if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().totalHits > 0) {
    39. SearchHits hits = searchResponse.getHits();
    40. for (SearchHit hit : hits) {
    41. // 将 JSON 转换成对象
    42. UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
    43. // 输出查询信息
    44. log.info(userInfo.toString());
    45. }
    46. }
    47. } catch (IOException e) {
    48. log.error("", e);
    49. }
    50. }
    51. }

    6、布尔查询(bool)

    (1)、Restful 操作示例

    查询出生在 1990-1995 年期间,且地址在 北京市昌平区、北京市大兴区、北京市房山区 的员工信息:
    1. GET /mydlq-user/_search
    2. {
    3. "query": {
    4. "bool": {
    5. "filter": {
    6. "range": {
    7. "birthDate": {
    8. "format": "yyyy",
    9. "gte": 1990,
    10. "lte": 1995
    11. }
    12. }
    13. },
    14. "must": [
    15. {
    16. "terms": {
    17. "address.keyword": [
    18. "北京市昌平区",
    19. "北京市大兴区",
    20. "北京市房山区"
    21. ]
    22. }
    23. }
    24. ]
    25. }
    26. }
    27. }

    (2)、Java 代码示例

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

    八、聚合查询操作示例

    1、Metric 聚合分析

    (1)、Restful 操作示例

    统计员工总数、工资最高值、工资最低值、工资平均工资、工资总和:
    1. GET /mydlq-user/_search
    2. {
    3. "size": 0,
    4. "aggs": {
    5. "salary_stats": {
    6. "stats": {
    7. "field": "salary"
    8. }
    9. }
    10. }
    11. }
    统计员工工资最低值:
    1. GET /mydlq-user/_search
    2. {
    3. "size": 0,
    4. "aggs": {
    5. "salary_min": {
    6. "min": {
    7. "field": "salary"
    8. }
    9. }
    10. }
    11. }
    统计员工工资最高值:
    1. GET /mydlq-user/_search
    2. {
    3. "size": 0,
    4. "aggs": {
    5. "salary_max": {
    6. "max": {
    7. "field": "salary"
    8. }
    9. }
    10. }
    11. }
    统计员工工资平均值:
    1. GET /mydlq-user/_search
    2. {
    3. "size": 0,
    4. "aggs": {
    5. "salary_avg": {
    6. "avg": {
    7. "field": "salary"
    8. }
    9. }
    10. }
    11. }
    统计员工工资总值:
    1. GET /mydlq-user/_search
    2. {
    3. "size": 0,
    4. "aggs": {
    5. "salary_sum": {
    6. "sum": {
    7. "field": "salary"
    8. }
    9. }
    10. }
    11. }
    统计员工总数:
    1. GET /mydlq-user/_search
    2. {
    3. "size": 0,
    4. "aggs": {
    5. "employee_count": {
    6. "value_count": {
    7. "field": "salary"
    8. }
    9. }
    10. }
    11. }
    统计员工工资百分位:
    1. GET /mydlq-user/_search
    2. {
    3. "size": 0,
    4. "aggs": {
    5. "salary_percentiles": {
    6. "percentiles": {
    7. "field": "salary"
    8. }
    9. }
    10. }
    11. }

    (2)、Java 代码示例

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

    2、Bucket 聚合分析

    (1)、Restful 操作示例

    按岁数进行聚合分桶,统计各个岁数员工的人数:
    1. GET mydlq-user/_search
    2. {
    3. "size": 0,
    4. "aggs": {
    5. "age_bucket": {
    6. "terms": {
    7. "field": "age",
    8. "size": "10"
    9. }
    10. }
    11. }
    12. }
    按工资范围进行聚合分桶,统计工资在 3000-5000、5000-9000 和 9000 以上的员工信息:
    1. GET mydlq-user/_search
    2. {
    3. "aggs": {
    4. "salary_range_bucket": {
    5. "range": {
    6. "field": "salary",
    7. "ranges": [
    8. {
    9. "key": "低级员工",
    10. "to": 3000
    11. },{
    12. "key": "中级员工",
    13. "from": 5000,
    14. "to": 9000
    15. },{
    16. "key": "高级员工",
    17. "from": 9000
    18. }
    19. ]
    20. }
    21. }
    22. }
    23. }
    按照时间范围进行分桶,统计 1985-1990 年和 1990-1995 年出生的员工信息:
    1. GET mydlq-user/_search
    2. {
    3. "size": 10,
    4. "aggs": {
    5. "date_range_bucket": {
    6. "date_range": {
    7. "field": "birthDate",
    8. "format": "yyyy",
    9. "ranges": [
    10. {
    11. "key": "出生日期1985-1990的员工",
    12. "from": "1985",
    13. "to": "1990"
    14. },{
    15. "key": "出生日期1990-1995的员工",
    16. "from": "1990",
    17. "to": "1995"
    18. }
    19. ]
    20. }
    21. }
    22. }
    23. }
    按工资多少进行聚合分桶,设置统计的最小值为 0,最大值为 12000,区段间隔为 3000:
    1. GET mydlq-user/_search
    2. {
    3. "size": 0,
    4. "aggs": {
    5. "salary_histogram": {
    6. "histogram": {
    7. "field": "salary",
    8. "extended_bounds": {
    9. "min": 0,
    10. "max": 12000
    11. },
    12. "interval": 3000
    13. }
    14. }
    15. }
    16. }
    按出生日期进行分桶:
    1. GET mydlq-user/_search
    2. {
    3. "size": 0,
    4. "aggs": {
    5. "birthday_histogram": {
    6. "date_histogram": {
    7. "format": "yyyy",
    8. "field": "birthDate",
    9. "interval": "year"
    10. }
    11. }
    12. }
    13. }

    (2)、Java 代码示例

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

    3、Metric 与 Bucket 聚合分析

    (1)、Restful 操作示例

    按照员工岁数分桶、然后统计每个岁数员工工资最高值:
    1. GET mydlq-user/_search
    2. {
    3. "size": 0,
    4. "aggs": {
    5. "salary_bucket": {
    6. "terms": {
    7. "field": "age",
    8. "size": "10"
    9. },
    10. "aggs": {
    11. "salary_max_user": {
    12. "top_hits": {
    13. "size": 1,
    14. "sort": [
    15. {
    16. "salary": {
    17. "order": "desc"
    18. }
    19. }
    20. ]
    21. }
    22. }
    23. }
    24. }
    25. }
    26. }

    (2)、Java 代码示例

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