SpringBoot 操作 ElasticSearch 详解

SpringBoot 操作 ElasticSearch 详解

文章目录

  !版权声明:本博客内容均为原创,每篇博文作为知识积累,写博不易,转载请注明出处。


参考地址:

  • 百度百科
  • 博文示例项目 Github 地址:https://github.com/my-dlq/blog-example/tree/master/springboot/springboot-elasticsearch-example

系统环境:

  • SpringBoot 版本:2.2.4
  • ElasticSearch 版本:6.5.3

一、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):

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

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

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

示例项目地址:https://github.com/my-dlq/blog-example/tree/master/springboot/springboot-elasticsearch-example

1、Maven 引入相关依赖

  • lombok: lombok 工具依赖。
  • fastjson: 用于将 JSON 转换对象的依赖。
  • spring-boot-starter-web: SpringBoot 的 Web 依赖。
  • elasticsearch-rest-high-level-client: 用于操作 ES 的 Java 客户端。
  • elasticsearch: ElasticSearch 依赖。在操作 ES Server 时候我们需要保证 ES 客户端和其保持一致。但是,在使用 elasticsearch-rest-high-level-client 时候,发现其依赖的 elasticsearch 依赖包并不和自身指定的版本相符,导致出现兼容问题。所以,这里我们手动引入该依赖来替换 elasticsearch-rest-high-level-client 依赖中的默认的 elasticsearch 依赖。
 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
17    <properties>
18        <java.version>1.8</java.version>
19    </properties>
20    <dependencies>
21        <!--web-->
22        <dependency>
23            <groupId>org.springframework.boot</groupId>
24            <artifactId>spring-boot-starter-web</artifactId>
25        </dependency>
26        <!--lombok-->
27        <dependency>
28            <groupId>org.projectlombok</groupId>
29            <artifactId>lombok</artifactId>
30            <optional>true</optional>
31        </dependency>
32        <!--fastjson-->
33        <dependency>
34            <groupId>com.alibaba</groupId>
35            <artifactId>fastjson</artifactId>
36            <version>1.2.61</version>
37        </dependency>
38        <!--elasticsearch-->
39        <dependency>
40            <groupId>org.elasticsearch.client</groupId>
41            <artifactId>elasticsearch-rest-high-level-client</artifactId>
42            <version>6.5.4</version>
43        </dependency>
44        <dependency>
45            <groupId>org.elasticsearch</groupId>
46            <artifactId>elasticsearch</artifactId>
47            <version>6.5.4</version>
48        </dependency>
49    </dependencies>
50
51    <build>
52        <plugins>
53            <plugin>
54                <groupId>org.springframework.boot</groupId>
55                <artifactId>spring-boot-maven-plugin</artifactId>
56            </plugin>
57        </plugins>
58    </build>
59
60</project>

2、ElasticSearch 连接配置

(1)、application.yml 配置文件

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

 1#base
 2server:
 3  port: 8080
 4#spring
 5spring:
 6  application:
 7    name: springboot-elasticsearch-example
 8#elasticsearch
 9elasticsearch:
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 中的配置信息:

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

四、索引操作示例

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

1、Restful 操作示例

创建索引

创建名为 mydlq-user 的索引与对应 Mapping。

 1PUT /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 索引。

1DELETE /mydlq-user

2、Java 代码示例

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

五、文档操作示例

1、Restful 操作示例

增加文档信息

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

 1POST /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 的文档信息。

1GET /mydlq-user/doc/1

更新文档信息

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

 1PUT /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 的文档信息。

1DELETE /mydlq-user/doc/1

2、Java 代码示例

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

六、插入初始化数据

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

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、查询数据

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

1GET 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 字段是专门用于不分词查询的子字段。

 1GET mydlq-user/_search
 2{
 3  "query": {
 4    "term": {
 5      "address.keyword": {
 6        "value": "北京市通州区"
 7      }
 8    }
 9  }
10}

精确查询-多内容查询

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

 1GET mydlq-user/_search
 2{
 3  "query": {
 4    "terms": {
 5      "address.keyword": [
 6        "北京市丰台区",
 7        "北京市昌平区",
 8        "北京市大兴区"
 9      ]
10    }
11  }
12}

(2)、Java 代码示例

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

2、匹配查询(match)

(1)、Restful 操作示例

匹配查询全部数据与分页

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

 1GET 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}

匹配查询数据

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

1GET mydlq-user/_search
2{
3  "query": {
4    "match": {
5      "address": "通州区"
6    }
7  }
8}

词语匹配查询

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

1GET mydlq-user/_search
2{
3  "query": {
4    "match_phrase": {
5      "address": "北京市通州区"
6    }
7  }
8}

内容多字段查询

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

1GET mydlq-user/_search
2{
3  "query": {
4    "multi_match": {
5      "query": "北京",
6      "fields": ["address","remark"]
7    }
8  }
9}

(2)、Java 代码示例

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

3、模糊查询(fuzzy)

(1)、Restful 操作示例

模糊查询所有以 结尾的姓名

1GET mydlq-user/_search
2{
3  "query": {
4    "fuzzy": {
5      "name": "三"
6    }
7  }
8}

(2)、Java 代码示例

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

4、范围查询(range)

(1)、Restful 操作示例

查询岁数 ≥ 30 岁的员工数据:

 1GET /mydlq-user/_search
 2{
 3  "query": {
 4    "range": {
 5      "age": {
 6        "gte": 30
 7      }
 8    }
 9  }
10}

查询生日距离现在 30 年间的员工数据:

 1GET mydlq-user/_search
 2{
 3  "query": {
 4    "range": {
 5      "birthDate": {
 6        "gte": "now-30y"
 7      }
 8    }
 9  }
10}

(2)、Java 代码示例

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

5、通配符查询(wildcard)

(1)、Restful 操作示例

查询所有以 “三” 结尾的姓名:

 1GET mydlq-user/_search
 2{
 3  "query": {
 4    "wildcard": {
 5      "name.keyword": {
 6        "value": "*三"
 7      }
 8    }
 9  }
10}

(2)、Java 代码示例

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

6、布尔查询(bool)

(1)、Restful 操作示例

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

 1GET /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 代码示例

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

八、聚合查询操作示例

1、Metric 聚合分析

(1)、Restful 操作示例

统计员工总数、工资最高值、工资最低值、工资平均工资、工资总和:

 1GET /mydlq-user/_search
 2{
 3  "size": 0,
 4  "aggs": {
 5    "salary_stats": {
 6      "stats": {
 7        "field": "salary"
 8      }
 9    }
10  }
11}

统计员工工资最低值:

 1GET /mydlq-user/_search
 2{
 3  "size": 0,
 4  "aggs": {
 5    "salary_min": {
 6      "min": {
 7        "field": "salary"
 8      }
 9    }
10  }
11}

统计员工工资最高值:

 1GET /mydlq-user/_search
 2{
 3  "size": 0,
 4  "aggs": {
 5    "salary_max": {
 6      "max": {
 7        "field": "salary"
 8      }
 9    }
10  }
11}

统计员工工资平均值:

 1GET /mydlq-user/_search
 2{
 3  "size": 0,
 4  "aggs": {
 5    "salary_avg": {
 6      "avg": {
 7        "field": "salary"
 8      }
 9    }
10  }
11}

统计员工工资总值:

 1GET /mydlq-user/_search
 2{
 3  "size": 0,
 4  "aggs": {
 5    "salary_sum": {
 6      "sum": {
 7        "field": "salary"
 8      }
 9    }
10  }
11}

统计员工总数:

 1GET /mydlq-user/_search
 2{
 3  "size": 0,
 4  "aggs": {
 5    "employee_count": {
 6      "value_count": {
 7        "field": "salary"
 8      }
 9    }
10  }
11}

统计员工工资百分位:

 1GET /mydlq-user/_search
 2{
 3  "size": 0,
 4  "aggs": {
 5    "salary_percentiles": {
 6      "percentiles": {
 7        "field": "salary"
 8      }
 9    }
10  }
11}

(2)、Java 代码示例

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

2、Bucket 聚合分析

(1)、Restful 操作示例

按岁数进行聚合分桶,统计各个岁数员工的人数:

 1GET 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 以上的员工信息:

 1GET 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 年出生的员工信息:

 1GET 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:

 1GET 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}

按出生日期进行分桶:

 1GET 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 代码示例

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

3、Metric 与 Bucket 聚合分析

(1)、Restful 操作示例

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

 1GET 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 代码示例

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

---END---


  !版权声明:本博客内容均为原创,每篇博文作为知识积累,写博不易,转载请注明出处。