Springboot 集成 Es 的相关操作

注:文章皆为个人纪录,可用性请以最终结果为准,若有错还请大佬们指出,谢谢!

此文章重在spring boot中对6.3.2版本Es的各种操作,简单易学,暂不做基础概念梳理。

每个操作都已测试,请放心食用~~


步骤一:引入相关依赖

注:此处引入  elasticsearch-rest-high-level-client  的Java客户端,并且与Es相关所有工具的版本需保持一致

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.es</groupId>
    <artifactId>example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>example</name>
    <description>Demo project for Elastic Search</description>
    <properties>
        <java.version>1.8</java.version>
        <elasticsearch.version>6.3.2</elasticsearch.version>
        <elasticsearch.clent.version>6.3.2</elasticsearch.clent.version>
        <fastjson.version>1.2.61</fastjson.version>
        <hutool.version>5.1.0</hutool.version>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.version}</version>
        </dependency>

        <!-- elasticsearch 依赖 -->
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>

        <!-- elasticsearch Java操作客户端依赖 -->
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>${elasticsearch.clent.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

步骤二:yml 配置

配好Es相关信息

server:
  port: 7777

spring:
  application:
    name: EsStudyTest

elasticsearch:
  schema: http # 协议
  address: 127.0.0.1:9200 # 连接地址(Es的 ip : port)
  connectTimeout: 5000 # 连接超时时间
  socketTimeout: 5000 # socket 连接超时时间
  connectionRequestTimeout: 5000 # 获取连接的超时时间
  maxConnectNum: 100 # 最大连接数
  maxConnectPerRoute: 100 # 最大路由连接数

步骤三:创建Es的配置类

获取 RestHighLevelClient  的入口

package com.es.example.config;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;

/**
 * ElasticSearch 连接配置
 */
@Configuration
public class EsConfig {
    /** 协议 */
    @Value("${elasticsearch.schema}")
    private String schema;

    /** 集群地址,如果有多个则用英文都好相隔 */
    @Value("${elasticsearch.address}")
    private String address;

    /** 连接超时时间 */
    @Value("${elasticsearch.connectTimeout}")
    private int connectTimeout;

    /** socket 连接超时时间 */
    @Value("${elasticsearch.socketTimeout}")
    private int socketTimeout;

    /** 获取连接的超时时间 */
    @Value("${elasticsearch.connectionRequestTimeout}")
    private int connectionRequestTimeout;

    /** 最大连接数 */
    @Value("${elasticsearch.maxConnectNum}")
    private int maxConnectNum;

    /** 最大路由连接数 */
    @Value("${elasticsearch.maxConnectPerRoute}")
    private int maxConnectPerRoute;

    @Bean
    public RestHighLevelClient restHighLevelClient() {
        // 组装集群地址
        List<HttpHost> hostList = new ArrayList<>();
        String[] hostArray = address.split(",");
        for (String addr : hostArray) {
            String host = addr.split(":")[0];
            String port = addr.split(":")[1];
            hostList.add(new HttpHost(host, Integer.parseInt(port), schema));
        }
        HttpHost[] httpHost = hostList.toArray(new HttpHost[]{});

        // 构建连接对象
        RestClientBuilder builder = RestClient.builder(httpHost);

        // 异步连接延时配置
        builder.setRequestConfigCallback(requestConfigBuilder -> {
            requestConfigBuilder.setConnectTimeout(connectTimeout);
            requestConfigBuilder.setSocketTimeout(socketTimeout);
            requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeout);
            return requestConfigBuilder;
        });

        // 异步连接数配置
        builder.setHttpClientConfigCallback(httpClientBuilder -> {
            httpClientBuilder.setMaxConnTotal(maxConnectNum);
            httpClientBuilder.setMaxConnPerRoute(maxConnectPerRoute);
            return httpClientBuilder;
        });

        return new RestHighLevelClient(builder);
    }
}

步骤四:创建自定义注解

用于在实体上说明Es的索引与type

package com.es.example.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义注解  声明Es的索引与type
 */
@Target({ElementType.TYPE}) // 声明注解使用类型
@Retention(RetentionPolicy.RUNTIME) // 声明注解生命周期为运行时
public @interface EsDocument {
    /**
     * 索引
     */
    String index();

    /**
     * type
     */
    String type();
}

步骤五:创建实体

package com.es.example.entity;

import com.es.example.annotations.EsDocument;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;

import java.util.Date;

@Data
@Builder
@EsDocument(index = "test20210910", type = "doc") // 自定义注解,声明Es的索引与type
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo {
    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private Integer age = 0;

    /**
     * 生日
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private String birthday;

    /**
     * 工资
     */
    private Integer salary = 0;

    /**
     * 地址
     */
    private String address;

    /**
     * 备注
     */
    private String remark;

    /**
     * 测试数据
     */
    private String sayWords;

    /**
     * 创建时间
     */
    private Date createTime;
}

步骤六:开始花式操作

说明

此步骤中的方法相对全面,且每一步都有注释,以及逻辑之间我都会做明显分隔,便于理解,

前部分做索引与文档的增删改操作,后半部分做查询操作,有明显分割提示,所有操作都在一个类中,成员变量共享,

但是我没有做每个方法的具体响应示例,重在"授人以渔"。

后面的一系列查询操作思路核心就五步:

第一步:创建查询条件;

第二步:创建查询源构建器,并将查询条件配置其中;

第三步:创建请求;

第四步:调用 RestHighLevelClient 客户端去获取请求响应体;

第五步:解析响应体

6.1 创建索引

package com.es.example.service.impl;
 
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.es.example.annotations.EsDocument;
import com.es.example.entity.UserInfo;
import com.es.example.service.EsIndexOperationService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.stats.ParsedStats;
import org.elasticsearch.search.aggregations.metrics.tophits.ParsedTopHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;
 
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
 
/**
 * Es索引相关操作
 */
@Service
@Slf4j
public class EsIndexOperationServiceImpl implements EsIndexOperationService {
    /**
     * 测试所用  ES文档id
     */
    private static final String TEST_ES_DOCUMENT_ID = "R0Xp3HsByDspl6UWckXA";
 
    /**
     * 查询条件的字段
     */
    private static final String TEST_ES_DOCUMENT_QUERY_KEY = "address";
 
 
    
    @Resource
    private RestHighLevelClient restHighLevelClient;
 
    /**
     * 创建索引
     *
     * @return 创建成功与否
     */
    @Override
    public String createIndex() {
        try {
            // 创建mapping
            XContentBuilder mapping = getCreateIndexMapping();
 
            // 创建索引配置信息
            Settings settings = Settings.builder()
                    .put("index.number_of_shards", 1)
                    .put("index.number_of_replicas", 0)
                    .build();
 
            // 创建索引请求对象,然后设置索引类型(ES 7.0 将不在存在索引类型)和 mapping 与 index 配置
            CreateIndexRequest request = new CreateIndexRequest("test20210910", settings);
            request.mapping("doc", mapping);
 
            // 客户端执行创建索引的请求
            CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request);
 
            // 判断是否创建成功
            boolean isCreated = createIndexResponse.isAcknowledged();
            log.info("是否创建成功:[{}]", isCreated);
 
            return isCreated ? "创建成功" : "创建失败";
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /**
     * 获取创建索引的mapping
     *
     * @return mapping
     * @throws IOException IO异常
     */
    private XContentBuilder getCreateIndexMapping() throws IOException {
        // 对比ES原生创建索引Dsl语句,每一个startObject()与endObject()就相当于Dsl语句中的一组大括号 {}
        return XContentFactory.jsonBuilder()
                        .startObject()
                        .field("dynamic", true)
                        .startObject("properties")
                        .startObject("name")
                        .field("type", "text")
                        .startObject("fields")
                        .startObject("keyword")
                        .field("type", "keyword")
                        .endObject()
                        .endObject()
                        .endObject()
 
                        .startObject("address")
                        .field("type", "text")
                        .startObject("fields")
                        .startObject("keyword")
                        .field("type", "keyword")
                        .endObject()
                        .endObject()
                        .endObject()
 
                        .startObject("remark")
                        .field("type", "text")
                        .startObject("fields")
                        .startObject("keyword")
                        .field("type", "keyword")
                        .endObject()
                        .endObject()
                        .endObject()
 
                        .startObject("sayWords")
                        .field("type", "text")
                        .endObject()
 
                        .startObject("age")
                        .field("type", "integer")
                        .endObject()
 
                        .startObject("salary")
                        .field("type", "integer")
                        .endObject()
 
                        .startObject("birthDate")
                        .field("type", "date")
                        .field("format", "yyyy-MM-dd")
                        .endObject()
 
                        .startObject("createTime")
                        .field("type", "date")
                        .endObject()
                        .endObject()
                        .endObject();
    }

6.2 删除索引 

/**
     * 删除索引
     *
     * @return 是否删除成功
     */
    @Override
    public String deleteIndex() {
        try {
            // 创建请求
            DeleteIndexRequest request = new DeleteIndexRequest("test20210910");
 
            // 处理请求
            DeleteIndexResponse response = restHighLevelClient.indices().delete(request);
 
            // 拿到结果
            boolean isDelete = response.isAcknowledged();
            log.info("是否删除成功:[{}]", isDelete);
 
            return isDelete ? "删除成功" : "删除失败";
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

6.3 新增一条文档 

/**
     * 新增一条文档
     *
     * @return 是否新增成功
     */
    @Override
    public String addADocument() {
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class); // 获取注解
        IndexRequest request = new IndexRequest(annotation.index(), annotation.type());
 
        // 创建员工信息
        UserInfo userInfo = UserInfo.builder()
                .name("王五")
                .age(9)
                .salary(1100)
                .address("天津市")
                .remark("来自北京市的王先生")
                .sayWords("啊阿萨飒飒")
                .createTime(new Date())
                .birthday("1991-01-01").build();
 
        /*
        将对象转为JSON数组
        不转为JSON格式并且不声明的话会报错java.lang.IllegalArgumentException: The number of object passed must be even but was [1])
        意为 request.source(userInfo) 方法不知道userInfo是什么类型的数据
        */
        byte[] json = JSON.toJSONBytes(userInfo);
 
        // 设置文档内容
        request.source(json, XContentType.JSON); // 声明类型
        try {
            // 执行请求
            IndexResponse response = restHighLevelClient.index(request);
            log.info("新增一条文档的结果状态:[{}]", response.status());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "新增一条文档成功";
    }

6.4 批量新增文档 

/**
     * 批量新增文档
     *
     * @return 是否新增成功
     */
    @Override
    public String addBulkDocument() {
        // 创建数据集
        List<UserInfo> userInfoList = new ArrayList<>();
        for (int i = 0; i <= 10; i++) {
            UserInfo userInfo = UserInfo.builder()
                    .name("用户" + i)
                    .age(i)
                    .salary(i * 100 + 100)
                    .address("用户" + i + "住在" + i + "号街区")
                    .remark("这是第" + i + "条文档信息的批量插入")
                    .sayWords("大大慢慢那得看" + i)
                    .createTime(new Date())
                    .birthday("199" + i + "-01-01").build();
            userInfoList.add(userInfo);
        }
 
        // 获取注解
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
 
        // 创建批量插入的请求
        BulkRequest bulkRequest = new BulkRequest();
        for (UserInfo userInfo : userInfoList) {
            IndexRequest request = new IndexRequest(annotation.index(), annotation.type());
            /*
            * 将对象转为JSON数组
              不转为JSON格式并且不声明的话会报错java.lang.IllegalArgumentException: The number of object passed must be even but was [1])
              意为 request.source(userInfo) 方法不知道userInfo是什么类型的数据
            *  */
            byte[] json = JSON.toJSONBytes(userInfo);
            request.source(json, XContentType.JSON); // 声明类型
            bulkRequest.add(request);
        }
 
        // 执行批量请求
        BulkResponse response = null;
        try {
            response = restHighLevelClient.bulk(bulkRequest);
            log.info("新增一条文档的结果状态:[{}]", response.status().getStatus());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return response != null && response.status().getStatus() == 200 ? "批量新增文档成功" : "批量新增文档失败";
    }

6.5 更新文档 

/**
     * 更新文档
     *
     * @return 是否更新成功
     */
    @Override
    public String updateDocument() {
        // 设置更新数据
        UserInfo update = UserInfo.builder().address("湖南省常德市").build();
        byte[] json = JSON.toJSONBytes(update);
 
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        UpdateRequest request = new UpdateRequest(annotation.index(), annotation.type(), TEST_ES_DOCUMENT_ID);
        request.doc(json, XContentType.JSON);
 
        // 处理请求
        UpdateResponse response = null;
        try {
            response = restHighLevelClient.update(request);
            log.info("更新一条文档的结果状态:[{}]", response.status().getStatus());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return response != null && response.status().getStatus() == 200 ? "更新成功" : "更新失败";
    }

6.6 删除文档

/**
     * 删除文档
     *
     * @return 是否删除成功
     */
    @Override
    public String deleteDocument() {
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        DeleteRequest request = new DeleteRequest(annotation.index(), annotation.type(), TEST_ES_DOCUMENT_ID);
 
        DeleteResponse response = null;
        try {
            response = restHighLevelClient.delete(request);
            log.info("删除一条文档的结果状态:[{}]", response.status().getStatus());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return response != null && response.status().getStatus() == 200 ? "删除成功" : "删除失败";
    }

以下为各种查询操作 

6.7 根据id获取文档 

/**
     * 根据id获取文档
     *
     * @return 文档结果
     */
    @Override
    public UserInfo getDocument() {
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        GetRequest request = new GetRequest(annotation.index(), annotation.type(), TEST_ES_DOCUMENT_ID);
        GetResponse response = null;
        try {
            response = restHighLevelClient.get(request);
            log.info("根据id查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (response != null) {
            UserInfo userInfo = JSON.parseObject(response.getSourceAsBytes(), UserInfo.class);
            log.info("获取查询结果:" + userInfo);
            return BeanUtil.isEmpty(userInfo) ? null : userInfo;
        }
        return null;
    }

6.8 精确查询 

查询条件不会进行分词,但是查询内容可能会分词,导致查询不到

/**
     * 精确查询(查询条件不会进行分词,但是查询内容可能会分词,导致查询不到)
     *
     * @return 查询结果
     */
    @Override
    public List<UserInfo> termQuery() {
        // 构建查询条件(注意:termQuery 支持多种格式查询,如 boolean、int、double、string 等,这里使用的是 string 的查询)
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.termQuery(TEST_ES_DOCUMENT_QUERY_KEY + ".keyword", "用户2住在2号街区")); // 加".keyword"就是整句匹配
        /*
        * QueryBuilders 有很多查询方式,详情请查看源码
        * termsQuery 相当于 Mysql 的 in
        * searchSourceBuilder.query(QueryBuilders.termsQuery("address.keyword", "用户1住在1号街区", , "用户2住在2号街区"));
        *
        * 也可以不加 .keyword 进行模糊匹配
        * searchSourceBuilder.query(QueryBuilders.termsQuery("sayWords", "街", "常"));
        *  */
 
        // 拿到索引信息
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
 
        // 创建查询请求,将查询对象配置到其中
        SearchRequest searchRequest = new SearchRequest(annotation.index());
        searchRequest.source(searchSourceBuilder);
 
        // 处理查询请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(searchRequest);
            log.info("精确查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        // 处理响应
        if (response != null) {
            if (RestStatus.OK.equals(response.status()) && response.getHits().totalHits > 0) {
                List<UserInfo> result = new ArrayList<>();
                SearchHits hits = response.getHits();
                for (SearchHit hit : hits) {
                    // 将JSON转换为对象
                    UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
                    result.add(userInfo);
                }
                log.info("获取查询结果:" + result);
                return CollectionUtil.isEmpty(result) ? null : result;
            }
        }
        return null;
    }

6.9  匹配查询符合条件的所有数据,并设置分页

/**
     * 匹配查询符合条件的所有数据,并设置分页
     *
     * @return 查询结果
     */
    @Override
    public List<UserInfo> matchAllQuery() {
        // 创建匹配全部的查询条件
        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        /*
        * 创建匹配筛选的查询条件
        * MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(TEST_ES_DOCUMENT_QUERY_KEY, "*街区");
        *
        * 创建匹配短句的查询条件
        * MatchPhraseQueryBuilder matchPhraseQueryBuilder = QueryBuilders.matchPhraseQuery(TEST_ES_DOCUMENT_QUERY_KEY, "用户");
        *
        * 创建根据内容匹配多个字段的查询条件
        * MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("北京", TEST_ES_DOCUMENT_QUERY_KEY, "remark");
        *  */
 
        // 创建查询源构造器
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(matchAllQueryBuilder);
 
        // 设置分页
        searchSourceBuilder.from(0);
        searchSourceBuilder.size(10);
 
        // 设置排序
        searchSourceBuilder.sort("salary", SortOrder.DESC);
 
        // 创建请求,并将查询条件配置到其中
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        SearchRequest request = new SearchRequest(annotation.index());
        request.source(searchSourceBuilder);
 
        // 处理查询请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request);
            log.info("匹配所有条件查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        // 处理响应
        if (response != null) {
            if (RestStatus.OK.equals(response.status()) && response.getHits().totalHits > 0) {
                List<UserInfo> result = new ArrayList<>();
                for (SearchHit hit : response.getHits()) {
                    // 将JSON转化为对象
                    UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
                    result.add(userInfo);
                }
                log.info("获取查询结果:" + result);
                return CollectionUtil.isEmpty(result) ? null : result;
            }
        }
        return null;
    }

6.10 模糊查询 (慎用)

/**
     * 模糊查询
     *
     * 慎用!!
     *
     * @return 查询结果
     */
    @Override
    public List<UserInfo> fuzzyQuery() {
        // 设置筛选条件,并设置模糊性(越大匹配度越低,注意:模糊度没设置合适可能会出现不想要的结果甚至无结果) 慎用!!
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.fuzzyQuery(TEST_ES_DOCUMENT_QUERY_KEY, "街区").fuzziness(Fuzziness.ONE));
 
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        SearchRequest searchRequest = new SearchRequest(annotation.index());
        searchRequest.source(searchSourceBuilder);
 
        // 处理请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(searchRequest);
            log.info("模糊查询查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        // 处理响应
        if (response != null) {
            if (RestStatus.OK.equals(response.status()) && response.getHits().totalHits > 0) {
                List<UserInfo> result = new ArrayList<>();
                for (SearchHit hit : response.getHits()) {
                    UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
                    result.add(userInfo);
                }
                log.info("获取查询结果:" + result);
                return result;
            }
        }
        return null;
    }

6.11 范围查询 

/**
     * 范围查询
     *
     * @return 查询结果
     */
    @Override
    public List<UserInfo> rangeQuery() {
        // 设置查询条件,includeLower(是否包含下边界)、includeUpper(是否包含上边界)
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.rangeQuery("salary").gte(500).lte(1000).includeLower(true).includeUpper(true));
        /*
        如果查询的字段是日期类型
        searchSourceBuilder.query(QueryBuilders.rangeQuery("birthday").from("1991-01-01").to("1999-01-01").includeLower(true).includeUpper(true));
        */
 
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        SearchRequest request = new SearchRequest(annotation.index());
        request.source(searchSourceBuilder);
 
        // 处理请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request);
            log.info("范围查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        // 处理响应
        if (response != null) {
            if (RestStatus.OK.equals(response.status()) && response.getHits().totalHits > 0) {
                List<UserInfo> result = new ArrayList<>();
                for (SearchHit hit : response.getHits()) {
                    UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
                    result.add(userInfo);
                }
                log.info("获取查询结果:" + result);
                return result;
            }
        }
        return null;
    }

6.12 通配符查询

*:表示多个字符(0个或多个字符)  ?:表示单个字符 

/**
     * 通配符查询( *:表示多个字符(0个或多个字符)  ?:表示单个字符)
     *
     * @return 查询结果
     */
    @Override
    public List<UserInfo> wildcardQuery() {
        // 创建查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.wildcardQuery(TEST_ES_DOCUMENT_QUERY_KEY + ".keyword", "*街区"));
 
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        SearchRequest request = new SearchRequest(annotation.index());
        request.source(searchSourceBuilder);
 
        // 处理请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request);
            log.info("通配符查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        // 处理响应
        if (response != null) {
            if (RestStatus.OK.equals(response.status()) && response.getHits().totalHits > 0) {
                List<UserInfo> result = new ArrayList<>();
                for (SearchHit hit : response.getHits()) {
                    UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
                    result.add(userInfo);
                }
                log.info("获取查询结果:" + result);
                return result;
            }
        }
        return null;
    }

6.13 布尔查询(重点!) 

/**
     * 布尔查询(重点!)
     *
     * @return 查询结果
     */
    @Override
    public List<UserInfo> boolQuery() {
        // 创建bool查询构建器
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
 
        // 创建查询条件(若有多个条件,就让后面接着加)
        boolQueryBuilder.must(QueryBuilders.termsQuery(TEST_ES_DOCUMENT_QUERY_KEY + ".keyword", "用户2住在2号街区", "用户3住在3号街区", "用户8住在8号街区"))
                .filter().add(QueryBuilders.rangeQuery("birthday").format("yyyy").gte("1990").lte("1995").includeLower(true).includeUpper(true));
 
        // 创建查询源并将构建器配置其中
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(boolQueryBuilder);
 
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        SearchRequest request = new SearchRequest(annotation.index());
        request.source(searchSourceBuilder);
 
        // 处理请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request);
            log.info("布尔查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        // 处理响应
        if (response != null) {
            if (RestStatus.OK.equals(response.status()) && response.getHits().totalHits > 0) {
                List<UserInfo> result = new ArrayList<>();
                for (SearchHit hit : response.getHits()) {
                    UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
                    result.add(userInfo);
                }
                log.info("获取查询结果:" + result);
                return result;
            }
        }
        return null;
    }

6.14 聚合统计查询(重点!) 

/**
     * 聚合统计查询(重点!)
     *
     * @return 查询结果如下:
     * {
     *     "salary_stats": {
     *         "name": "salary_stats",
     *         "count": 26,
     *         "min": 100.0,
     *         "max": 1100.0,
     *         "sum": 12100.0,
     *         "avg": 465.38461538461536,
     *         "type": "stats",
     *         "sumAsString": "12100.0",
     *         "maxAsString": "1100.0",
     *         "avgAsString": "465.38461538461536",
     *         "minAsString": "100.0",
     *         "metaData": null,
     *         "fragment": true
     *     }
     * }
     */
    @Override
    public Object aggregationStats() {
        /*
        声明聚合的类型与字段
            统计数据(可拿到总数、平均数、最大值、最小值等数据)  --      stats("字段_stats").field("字段")
            总数                                               --      count("字段_count").field("字段")
            平均数                                             --       avg("字段_avg").field("字段")
        以此类推...
        */
        AggregationBuilder aggr = AggregationBuilders.stats("salary_stats").field("salary");
 
        // 设置查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.aggregation(aggr);
 
        // 设置查询结果不返回,只返回聚合的结果
        searchSourceBuilder.size(0);
 
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        SearchRequest request = new SearchRequest(annotation.index());
        request.source(searchSourceBuilder);
 
        // 处理请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request);
            log.info("聚合统计查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
 
        // 处理响应
        if (response != null) {
            // 获取响应中的聚合信息
            Aggregations aggregations = response.getAggregations();
 
            if (RestStatus.OK.equals(response.status()) && aggregations != null) {
                /*
                解析数据类型
                    ParsedStats             --      聚合信息
                    ParsedValueCount        --      总数
                    ParsedAvg               --      平均数
                以此类推
                */
                ParsedStats stats = aggregations.get("salary_stats");
                log.info("-------------------------------------------");
                log.info("聚合信息:");
                log.info("count:{}", stats.getCount());
                log.info("avg:{}", stats.getAvg());
                log.info("max:{}", stats.getMax());
                log.info("min:{}", stats.getMin());
                log.info("sum:{}", stats.getSum());
                log.info("-------------------------------------------");
 
                return aggregations.getAsMap();
            }
        }
        return null;
    }

6.15 聚合分桶查询(重点!) 

/**
     * 聚合分桶查询(重点!)
     *
     * @return 查询结果: 桶名 与 对应的总数
     */
    @Override
    public Object aggregationBucket() {
        /*
         < 声明聚合的类型与字段 >
         按岁数进行聚合分桶
         TermsAggregationBuilder aggr = AggregationBuilders.terms("age_bucket").field("age");
         按工资范围进行聚合分桶
         AggregationBuilder aggr = AggregationBuilders.range("salary_range_bucket")
                    .field("salary")
                    .addUnboundedTo("低级员工", 3000)
                    .addRange("中级员工", 5000, 9000)
                    .addUnboundedFrom("高级员工", 9000);
          按照时间范围进行分桶
          AggregationBuilder aggr = AggregationBuilders.dateRange("date_range_bucket")
                    .field("birthday")
                    .format("yyyy")
                    .addRange("1985-1990", "1985", "1990")
                    .addRange("1990-1995", "1990", "1995");
           按工资多少进行聚合分桶
           AggregationBuilder aggr = AggregationBuilders.histogram("salary_histogram")
                    .field("salary")
                    .extendedBounds(0, 12000)
                    .interval(3000);
           按出生日期进行分桶
           AggregationBuilder aggr = AggregationBuilders.dateHistogram("birthday_histogram")
                    .field("birthday")
                    .interval(1)
                    .dateHistogramInterval(DateHistogramInterval.YEAR)
                    .format("yyyy");
        */
        TermsAggregationBuilder aggr = AggregationBuilders.terms("age_bucket").field("age");
 
        // 创建查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.aggregation(aggr);
        searchSourceBuilder.size(10);
 
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        SearchRequest request = new SearchRequest(annotation.index());
        request.source(searchSourceBuilder);
 
        // 处理请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request);
            log.info("聚合分桶查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        // 处理响应
        if (response != null) {
            if (RestStatus.OK.equals(response.status())) {
                // 获取响应中的聚合信息
                Aggregations aggregations = response.getAggregations();
 
                // 分桶
                Terms byCompanyAggregation  = aggregations.get("age_bucket"); // 不同的聚合类型修改 声明聚合的类型与字段 中AggregationBuilders的不同名称即可
                List<? extends Terms.Bucket> buckets = byCompanyAggregation.getBuckets();
 
                // 输出各个桶的内容
                log.info("-------------------------------------------");
                log.info("聚合信息:");
                for (Terms.Bucket bucket : buckets) {
                    log.info("桶名:{} | 总数:{}", bucket.getKeyAsString(), bucket.getDocCount());
                }
                log.info("-------------------------------------------");
 
                return aggregations.getAsMap();
            }
        }
        return null;
    }

6.16 聚合查询场景分析

按岁数分桶、然后统计各个岁数收入最高的用户信息 

/**
     * 聚合查询场景分析
     * (按岁数分桶、然后统计各个岁数收入最高的用户信息)
     *
     * @return 查询结果:
     *  桶名:1
     *  值:{"address":"湖南省常德市","age":1,"birthday":"1991-01-01","createTime":1631498892472,"name":"用户1","remark":"这是第1条文档信息的批量插入","salary":200}
     *
     *  以此类推...
     */
    @Override
    public Object aggregationAnalysis() {
        // 构建聚合参数
        AggregationBuilder testTop = AggregationBuilders.topHits("salary_max_user")
                .size(1)
                .sort("salary", SortOrder.DESC);
        AggregationBuilder salaryBucket = AggregationBuilders.terms("salary_bucket")
                .field("age")
                .size(10);
        salaryBucket.subAggregation(testTop);
 
        // 创建查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.size(0);
        searchSourceBuilder.aggregation(salaryBucket);
 
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        SearchRequest request = new SearchRequest(annotation.index());
        request.source(searchSourceBuilder);
 
        // 处理请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request);
            log.info("聚合查询场景分析的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        // 处理响应
        if (response != null) {
            // 获取响应中的聚合信息
            Aggregations aggregations = response.getAggregations();
 
            if (RestStatus.OK.equals(response.status())) {
                // 分桶
                Terms byCompanyAggregation = aggregations.get("salary_bucket");
                List<? extends Terms.Bucket> buckets = byCompanyAggregation.getBuckets();
                // 输出各个桶的内容
                log.info("-------------------------------------------");
                log.info("聚合信息:");
                for (Terms.Bucket bucket : buckets) {
                    log.info("桶名:{}", bucket.getKeyAsString());
                    ParsedTopHits topHits = bucket.getAggregations().get("salary_max_user");
                    for (SearchHit hit:topHits.getHits()){
                        log.info(hit.getSourceAsString());
                    }
                }
                log.info("-------------------------------------------");
 
                return aggregations.getAsMap();
            }
        }
        return response;
    }
 
 
}

第六步中所有代码(方便一次性粘贴)

package com.es.example.service.impl;
 
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.es.example.annotations.EsDocument;
import com.es.example.entity.UserInfo;
import com.es.example.service.EsIndexOperationService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.stats.ParsedStats;
import org.elasticsearch.search.aggregations.metrics.tophits.ParsedTopHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;
 
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
 
/**
 * Es索引相关操作
 */
@Service
@Slf4j
public class EsIndexOperationServiceImpl implements EsIndexOperationService {
    /**
     * 测试所用  ES文档id
     */
    private static final String TEST_ES_DOCUMENT_ID = "R0Xp3HsByDspl6UWckXA";
 
    /**
     * 查询条件的字段
     */
    private static final String TEST_ES_DOCUMENT_QUERY_KEY = "address";
 
 
    
    @Resource
    private RestHighLevelClient restHighLevelClient;
 
    /**
     * 创建索引
     *
     * @return 创建成功与否
     */
    @Override
    public String createIndex() {
        try {
            // 创建mapping
            XContentBuilder mapping = getCreateIndexMapping();
 
            // 创建索引配置信息
            Settings settings = Settings.builder()
                    .put("index.number_of_shards", 1)
                    .put("index.number_of_replicas", 0)
                    .build();
 
            // 创建索引请求对象,然后设置索引类型(ES 7.0 将不在存在索引类型)和 mapping 与 index 配置
            CreateIndexRequest request = new CreateIndexRequest("test20210910", settings);
            request.mapping("doc", mapping);
 
            // 客户端执行创建索引的请求
            CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request);
 
            // 判断是否创建成功
            boolean isCreated = createIndexResponse.isAcknowledged();
            log.info("是否创建成功:[{}]", isCreated);
 
            return isCreated ? "创建成功" : "创建失败";
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /**
     * 获取创建索引的mapping
     *
     * @return mapping
     * @throws IOException IO异常
     */
    private XContentBuilder getCreateIndexMapping() throws IOException {
        // 对比ES原生创建索引Dsl语句,每一个startObject()与endObject()就相当于Dsl语句中的一组大括号 {}
        return XContentFactory.jsonBuilder()
                        .startObject()
                        .field("dynamic", true)
                        .startObject("properties")
                        .startObject("name")
                        .field("type", "text")
                        .startObject("fields")
                        .startObject("keyword")
                        .field("type", "keyword")
                        .endObject()
                        .endObject()
                        .endObject()
 
                        .startObject("address")
                        .field("type", "text")
                        .startObject("fields")
                        .startObject("keyword")
                        .field("type", "keyword")
                        .endObject()
                        .endObject()
                        .endObject()
 
                        .startObject("remark")
                        .field("type", "text")
                        .startObject("fields")
                        .startObject("keyword")
                        .field("type", "keyword")
                        .endObject()
                        .endObject()
                        .endObject()
 
                        .startObject("sayWords")
                        .field("type", "text")
                        .endObject()
 
                        .startObject("age")
                        .field("type", "integer")
                        .endObject()
 
                        .startObject("salary")
                        .field("type", "integer")
                        .endObject()
 
                        .startObject("birthDate")
                        .field("type", "date")
                        .field("format", "yyyy-MM-dd")
                        .endObject()
 
                        .startObject("createTime")
                        .field("type", "date")
                        .endObject()
                        .endObject()
                        .endObject();
    }
 
    /**
     * 删除索引
     *
     * @return 是否删除成功
     */
    @Override
    public String deleteIndex() {
        try {
            // 创建请求
            DeleteIndexRequest request = new DeleteIndexRequest("test20210910");
 
            // 处理请求
            DeleteIndexResponse response = restHighLevelClient.indices().delete(request);
 
            // 拿到结果
            boolean isDelete = response.isAcknowledged();
            log.info("是否删除成功:[{}]", isDelete);
 
            return isDelete ? "删除成功" : "删除失败";
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /**
     * 新增一条文档
     *
     * @return 是否新增成功
     */
    @Override
    public String addADocument() {
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class); // 获取注解
        IndexRequest request = new IndexRequest(annotation.index(), annotation.type());
 
        // 创建员工信息
        UserInfo userInfo = UserInfo.builder()
                .name("王五")
                .age(9)
                .salary(1100)
                .address("天津市")
                .remark("来自北京市的王先生")
                .sayWords("啊阿萨飒飒")
                .createTime(new Date())
                .birthday("1991-01-01").build();
 
        /*
        将对象转为JSON数组
        不转为JSON格式并且不声明的话会报错java.lang.IllegalArgumentException: The number of object passed must be even but was [1])
        意为 request.source(userInfo) 方法不知道userInfo是什么类型的数据
        */
        byte[] json = JSON.toJSONBytes(userInfo);
 
        // 设置文档内容
        request.source(json, XContentType.JSON); // 声明类型
        try {
            // 执行请求
            IndexResponse response = restHighLevelClient.index(request);
            log.info("新增一条文档的结果状态:[{}]", response.status());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "新增一条文档成功";
    }
 
    /**
     * 批量新增文档
     *
     * @return 是否新增成功
     */
    @Override
    public String addBulkDocument() {
        // 创建数据集
        List<UserInfo> userInfoList = new ArrayList<>();
        for (int i = 0; i <= 10; i++) {
            UserInfo userInfo = UserInfo.builder()
                    .name("用户" + i)
                    .age(i)
                    .salary(i * 100 + 100)
                    .address("用户" + i + "住在" + i + "号街区")
                    .remark("这是第" + i + "条文档信息的批量插入")
                    .sayWords("大大慢慢那得看" + i)
                    .createTime(new Date())
                    .birthday("199" + i + "-01-01").build();
            userInfoList.add(userInfo);
        }
 
        // 获取注解
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
 
        // 创建批量插入的请求
        BulkRequest bulkRequest = new BulkRequest();
        for (UserInfo userInfo : userInfoList) {
            IndexRequest request = new IndexRequest(annotation.index(), annotation.type());
            /*
            * 将对象转为JSON数组
              不转为JSON格式并且不声明的话会报错java.lang.IllegalArgumentException: The number of object passed must be even but was [1])
              意为 request.source(userInfo) 方法不知道userInfo是什么类型的数据
            *  */
            byte[] json = JSON.toJSONBytes(userInfo);
            request.source(json, XContentType.JSON); // 声明类型
            bulkRequest.add(request);
        }
 
        // 执行批量请求
        BulkResponse response = null;
        try {
            response = restHighLevelClient.bulk(bulkRequest);
            log.info("新增一条文档的结果状态:[{}]", response.status().getStatus());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return response != null && response.status().getStatus() == 200 ? "批量新增文档成功" : "批量新增文档失败";
    }
 
    /**
     * 更新文档
     *
     * @return 是否更新成功
     */
    @Override
    public String updateDocument() {
        // 设置更新数据
        UserInfo update = UserInfo.builder().address("湖南省常德市").build();
        byte[] json = JSON.toJSONBytes(update);
 
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        UpdateRequest request = new UpdateRequest(annotation.index(), annotation.type(), TEST_ES_DOCUMENT_ID);
        request.doc(json, XContentType.JSON);
 
        // 处理请求
        UpdateResponse response = null;
        try {
            response = restHighLevelClient.update(request);
            log.info("更新一条文档的结果状态:[{}]", response.status().getStatus());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return response != null && response.status().getStatus() == 200 ? "更新成功" : "更新失败";
    }
 
    /**
     * 删除文档
     *
     * @return 是否删除成功
     */
    @Override
    public String deleteDocument() {
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        DeleteRequest request = new DeleteRequest(annotation.index(), annotation.type(), TEST_ES_DOCUMENT_ID);
 
        DeleteResponse response = null;
        try {
            response = restHighLevelClient.delete(request);
            log.info("删除一条文档的结果状态:[{}]", response.status().getStatus());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return response != null && response.status().getStatus() == 200 ? "删除成功" : "删除失败";
    }
 
 
    /* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 以下为查询操作示例 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
 
    /**
     * 根据id获取文档
     *
     * @return 文档结果
     */
    @Override
    public UserInfo getDocument() {
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        GetRequest request = new GetRequest(annotation.index(), annotation.type(), TEST_ES_DOCUMENT_ID);
        GetResponse response = null;
        try {
            response = restHighLevelClient.get(request);
            log.info("根据id查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (response != null) {
            UserInfo userInfo = JSON.parseObject(response.getSourceAsBytes(), UserInfo.class);
            log.info("获取查询结果:" + userInfo);
            return BeanUtil.isEmpty(userInfo) ? null : userInfo;
        }
        return null;
    }
 
    /**
     * 精确查询(查询条件不会进行分词,但是查询内容可能会分词,导致查询不到)
     *
     * @return 查询结果
     */
    @Override
    public List<UserInfo> termQuery() {
        // 构建查询条件(注意:termQuery 支持多种格式查询,如 boolean、int、double、string 等,这里使用的是 string 的查询)
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.termQuery(TEST_ES_DOCUMENT_QUERY_KEY + ".keyword", "用户2住在2号街区")); // 加".keyword"就是整句匹配
        /*
        * QueryBuilders 有很多查询方式,详情请查看源码
        * termsQuery 相当于 Mysql 的 in
        * searchSourceBuilder.query(QueryBuilders.termsQuery("address.keyword", "用户1住在1号街区", , "用户2住在2号街区"));
        *
        * 也可以不加 .keyword 进行模糊匹配
        * searchSourceBuilder.query(QueryBuilders.termsQuery("sayWords", "街", "常"));
        *  */
 
        // 拿到索引信息
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
 
        // 创建查询请求,将查询对象配置到其中
        SearchRequest searchRequest = new SearchRequest(annotation.index());
        searchRequest.source(searchSourceBuilder);
 
        // 处理查询请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(searchRequest);
            log.info("精确查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        // 处理响应
        if (response != null) {
            if (RestStatus.OK.equals(response.status()) && response.getHits().totalHits > 0) {
                List<UserInfo> result = new ArrayList<>();
                SearchHits hits = response.getHits();
                for (SearchHit hit : hits) {
                    // 将JSON转换为对象
                    UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
                    result.add(userInfo);
                }
                log.info("获取查询结果:" + result);
                return CollectionUtil.isEmpty(result) ? null : result;
            }
        }
        return null;
    }
 
    /**
     * 匹配查询符合条件的所有数据,并设置分页
     *
     * @return 查询结果
     */
    @Override
    public List<UserInfo> matchAllQuery() {
        // 创建匹配全部的查询条件
        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        /*
        * 创建匹配筛选的查询条件
        * MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(TEST_ES_DOCUMENT_QUERY_KEY, "*街区");
        *
        * 创建匹配短句的查询条件
        * MatchPhraseQueryBuilder matchPhraseQueryBuilder = QueryBuilders.matchPhraseQuery(TEST_ES_DOCUMENT_QUERY_KEY, "用户");
        *
        * 创建根据内容匹配多个字段的查询条件
        * MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("北京", TEST_ES_DOCUMENT_QUERY_KEY, "remark");
        *  */
 
        // 创建查询源构造器
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(matchAllQueryBuilder);
 
        // 设置分页
        searchSourceBuilder.from(0);
        searchSourceBuilder.size(10);
 
        // 设置排序
        searchSourceBuilder.sort("salary", SortOrder.DESC);
 
        // 创建请求,并将查询条件配置到其中
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        SearchRequest request = new SearchRequest(annotation.index());
        request.source(searchSourceBuilder);
 
        // 处理查询请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request);
            log.info("匹配所有条件查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        // 处理响应
        if (response != null) {
            if (RestStatus.OK.equals(response.status()) && response.getHits().totalHits > 0) {
                List<UserInfo> result = new ArrayList<>();
                for (SearchHit hit : response.getHits()) {
                    // 将JSON转化为对象
                    UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
                    result.add(userInfo);
                }
                log.info("获取查询结果:" + result);
                return CollectionUtil.isEmpty(result) ? null : result;
            }
        }
        return null;
    }
 
    /**
     * 模糊查询
     *
     * 慎用!!
     *
     * @return 查询结果
     */
    @Override
    public List<UserInfo> fuzzyQuery() {
        // 设置筛选条件,并设置模糊性(越大匹配度越低,注意:模糊度没设置合适可能会出现不想要的结果甚至无结果) 慎用!!
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.fuzzyQuery(TEST_ES_DOCUMENT_QUERY_KEY, "街区").fuzziness(Fuzziness.ONE));
 
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        SearchRequest searchRequest = new SearchRequest(annotation.index());
        searchRequest.source(searchSourceBuilder);
 
        // 处理请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(searchRequest);
            log.info("模糊查询查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        // 处理响应
        if (response != null) {
            if (RestStatus.OK.equals(response.status()) && response.getHits().totalHits > 0) {
                List<UserInfo> result = new ArrayList<>();
                for (SearchHit hit : response.getHits()) {
                    UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
                    result.add(userInfo);
                }
                log.info("获取查询结果:" + result);
                return result;
            }
        }
        return null;
    }
 
    /**
     * 范围查询
     *
     * @return 查询结果
     */
    @Override
    public List<UserInfo> rangeQuery() {
        // 设置查询条件,includeLower(是否包含下边界)、includeUpper(是否包含上边界)
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.rangeQuery("salary").gte(500).lte(1000).includeLower(true).includeUpper(true));
        /*
        如果查询的字段是日期类型
        searchSourceBuilder.query(QueryBuilders.rangeQuery("birthday").from("1991-01-01").to("1999-01-01").includeLower(true).includeUpper(true));
        */
 
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        SearchRequest request = new SearchRequest(annotation.index());
        request.source(searchSourceBuilder);
 
        // 处理请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request);
            log.info("范围查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        // 处理响应
        if (response != null) {
            if (RestStatus.OK.equals(response.status()) && response.getHits().totalHits > 0) {
                List<UserInfo> result = new ArrayList<>();
                for (SearchHit hit : response.getHits()) {
                    UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
                    result.add(userInfo);
                }
                log.info("获取查询结果:" + result);
                return result;
            }
        }
        return null;
    }
 
    /**
     * 通配符查询( *:表示多个字符(0个或多个字符)  ?:表示单个字符)
     *
     * @return 查询结果
     */
    @Override
    public List<UserInfo> wildcardQuery() {
        // 创建查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.wildcardQuery(TEST_ES_DOCUMENT_QUERY_KEY + ".keyword", "*街区"));
 
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        SearchRequest request = new SearchRequest(annotation.index());
        request.source(searchSourceBuilder);
 
        // 处理请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request);
            log.info("通配符查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        // 处理响应
        if (response != null) {
            if (RestStatus.OK.equals(response.status()) && response.getHits().totalHits > 0) {
                List<UserInfo> result = new ArrayList<>();
                for (SearchHit hit : response.getHits()) {
                    UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
                    result.add(userInfo);
                }
                log.info("获取查询结果:" + result);
                return result;
            }
        }
        return null;
    }
 
    /**
     * 布尔查询(重点!)
     *
     * @return 查询结果
     */
    @Override
    public List<UserInfo> boolQuery() {
        // 创建bool查询构建器
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
 
        // 创建查询条件(若有多个条件,就让后面接着加)
        boolQueryBuilder.must(QueryBuilders.termsQuery(TEST_ES_DOCUMENT_QUERY_KEY + ".keyword", "用户2住在2号街区", "用户3住在3号街区", "用户8住在8号街区"))
                .filter().add(QueryBuilders.rangeQuery("birthday").format("yyyy").gte("1990").lte("1995").includeLower(true).includeUpper(true));
 
        // 创建查询源并将构建器配置其中
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(boolQueryBuilder);
 
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        SearchRequest request = new SearchRequest(annotation.index());
        request.source(searchSourceBuilder);
 
        // 处理请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request);
            log.info("布尔查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        // 处理响应
        if (response != null) {
            if (RestStatus.OK.equals(response.status()) && response.getHits().totalHits > 0) {
                List<UserInfo> result = new ArrayList<>();
                for (SearchHit hit : response.getHits()) {
                    UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
                    result.add(userInfo);
                }
                log.info("获取查询结果:" + result);
                return result;
            }
        }
        return null;
    }
 
    /**
     * 聚合统计查询(重点!)
     *
     * @return 查询结果如下:
     * {
     *     "salary_stats": {
     *         "name": "salary_stats",
     *         "count": 26,
     *         "min": 100.0,
     *         "max": 1100.0,
     *         "sum": 12100.0,
     *         "avg": 465.38461538461536,
     *         "type": "stats",
     *         "sumAsString": "12100.0",
     *         "maxAsString": "1100.0",
     *         "avgAsString": "465.38461538461536",
     *         "minAsString": "100.0",
     *         "metaData": null,
     *         "fragment": true
     *     }
     * }
     */
    @Override
    public Object aggregationStats() {
        /*
        声明聚合的类型与字段
            统计数据(可拿到总数、平均数、最大值、最小值等数据)  --      stats("字段_stats").field("字段")
            总数                                               --      count("字段_count").field("字段")
            平均数                                             --       avg("字段_avg").field("字段")
        以此类推...
        */
        AggregationBuilder aggr = AggregationBuilders.stats("salary_stats").field("salary");
 
        // 设置查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.aggregation(aggr);
 
        // 设置查询结果不返回,只返回聚合的结果
        searchSourceBuilder.size(0);
 
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        SearchRequest request = new SearchRequest(annotation.index());
        request.source(searchSourceBuilder);
 
        // 处理请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request);
            log.info("聚合统计查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
 
        // 处理响应
        if (response != null) {
            // 获取响应中的聚合信息
            Aggregations aggregations = response.getAggregations();
 
            if (RestStatus.OK.equals(response.status()) && aggregations != null) {
                /*
                解析数据类型
                    ParsedStats             --      聚合信息
                    ParsedValueCount        --      总数
                    ParsedAvg               --      平均数
                以此类推
                */
                ParsedStats stats = aggregations.get("salary_stats");
                log.info("-------------------------------------------");
                log.info("聚合信息:");
                log.info("count:{}", stats.getCount());
                log.info("avg:{}", stats.getAvg());
                log.info("max:{}", stats.getMax());
                log.info("min:{}", stats.getMin());
                log.info("sum:{}", stats.getSum());
                log.info("-------------------------------------------");
 
                return aggregations.getAsMap();
            }
        }
        return null;
    }
 
    /**
     * 聚合分桶查询(重点!)
     *
     * @return 查询结果: 桶名 与 对应的总数
     */
    @Override
    public Object aggregationBucket() {
        /*
         < 声明聚合的类型与字段 >
         按岁数进行聚合分桶
         TermsAggregationBuilder aggr = AggregationBuilders.terms("age_bucket").field("age");
         按工资范围进行聚合分桶
         AggregationBuilder aggr = AggregationBuilders.range("salary_range_bucket")
                    .field("salary")
                    .addUnboundedTo("低级员工", 3000)
                    .addRange("中级员工", 5000, 9000)
                    .addUnboundedFrom("高级员工", 9000);
          按照时间范围进行分桶
          AggregationBuilder aggr = AggregationBuilders.dateRange("date_range_bucket")
                    .field("birthday")
                    .format("yyyy")
                    .addRange("1985-1990", "1985", "1990")
                    .addRange("1990-1995", "1990", "1995");
           按工资多少进行聚合分桶
           AggregationBuilder aggr = AggregationBuilders.histogram("salary_histogram")
                    .field("salary")
                    .extendedBounds(0, 12000)
                    .interval(3000);
           按出生日期进行分桶
           AggregationBuilder aggr = AggregationBuilders.dateHistogram("birthday_histogram")
                    .field("birthday")
                    .interval(1)
                    .dateHistogramInterval(DateHistogramInterval.YEAR)
                    .format("yyyy");
        */
        TermsAggregationBuilder aggr = AggregationBuilders.terms("age_bucket").field("age");
 
        // 创建查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.aggregation(aggr);
        searchSourceBuilder.size(10);
 
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        SearchRequest request = new SearchRequest(annotation.index());
        request.source(searchSourceBuilder);
 
        // 处理请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request);
            log.info("聚合分桶查询的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        // 处理响应
        if (response != null) {
            if (RestStatus.OK.equals(response.status())) {
                // 获取响应中的聚合信息
                Aggregations aggregations = response.getAggregations();
 
                // 分桶
                Terms byCompanyAggregation  = aggregations.get("age_bucket"); // 不同的聚合类型修改 声明聚合的类型与字段 中AggregationBuilders的不同名称即可
                List<? extends Terms.Bucket> buckets = byCompanyAggregation.getBuckets();
 
                // 输出各个桶的内容
                log.info("-------------------------------------------");
                log.info("聚合信息:");
                for (Terms.Bucket bucket : buckets) {
                    log.info("桶名:{} | 总数:{}", bucket.getKeyAsString(), bucket.getDocCount());
                }
                log.info("-------------------------------------------");
 
                return aggregations.getAsMap();
            }
        }
        return null;
    }
 
    /**
     * 聚合查询场景分析
     * (按岁数分桶、然后统计各个岁数收入最高的用户信息)
     *
     * @return 查询结果:
     *  桶名:1
     *  值:{"address":"湖南省常德市","age":1,"birthday":"1991-01-01","createTime":1631498892472,"name":"用户1","remark":"这是第1条文档信息的批量插入","salary":200}
     *
     *  以此类推...
     */
    @Override
    public Object aggregationAnalysis() {
        // 构建聚合参数
        AggregationBuilder testTop = AggregationBuilders.topHits("salary_max_user")
                .size(1)
                .sort("salary", SortOrder.DESC);
        AggregationBuilder salaryBucket = AggregationBuilders.terms("salary_bucket")
                .field("age")
                .size(10);
        salaryBucket.subAggregation(testTop);
 
        // 创建查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.size(0);
        searchSourceBuilder.aggregation(salaryBucket);
 
        // 创建请求
        EsDocument annotation = UserInfo.class.getAnnotation(EsDocument.class);
        SearchRequest request = new SearchRequest(annotation.index());
        request.source(searchSourceBuilder);
 
        // 处理请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request);
            log.info("聚合查询场景分析的响应体:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        // 处理响应
        if (response != null) {
            // 获取响应中的聚合信息
            Aggregations aggregations = response.getAggregations();
 
            if (RestStatus.OK.equals(response.status())) {
                // 分桶
                Terms byCompanyAggregation = aggregations.get("salary_bucket");
                List<? extends Terms.Bucket> buckets = byCompanyAggregation.getBuckets();
                // 输出各个桶的内容
                log.info("-------------------------------------------");
                log.info("聚合信息:");
                for (Terms.Bucket bucket : buckets) {
                    log.info("桶名:{}", bucket.getKeyAsString());
                    ParsedTopHits topHits = bucket.getAggregations().get("salary_max_user");
                    for (SearchHit hit:topHits.getHits()){
                        log.info(hit.getSourceAsString());
                    }
                }
                log.info("-------------------------------------------");
 
                return aggregations.getAsMap();
            }
        }
        return response;
    }
 
 
}

说明

        本文章是在学习了 <全栈开发者社区> 与 <狂神说> 的Es相关讲解后的实践与补充,想学习更加完整的知识可去膜拜大神!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值