一、引入依赖

<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>7.8.0</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.8.0</version>
</dependency>

二、代码

前提:import信息
import com.alibaba.fastjson.JSONObject;
import com.atguigu.estest.model.User;
import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkItemResponse;
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.*;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.metrics.ParsedMax;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
1、添加索引结构到es
void addIndex() throws Exception {
    /**
     * GET /user
     * 作用:可以看到aliases、mappings、settings
     * 说明:我们下面使用的是直接定死的结构,而SpringBoot中使用的是注解的方式来创建索引结构,最终的原理都是下面的代码
     * {
     *     "user": {
     *         "aliases": {
     *             "user.aliases": {}
     *         },
     *         "mappings": {
     *             "properties": {
     *                 "age": {
     *                     "type": "integer"
     *                 },
     *                 "name": {
     *                     "type": "text",
     *                     "fields": {
     *                         "keyword": {
     *                             "type": "keyword"
     *                         }
     *                     }
     *                 },
     *                 "sex": {
     *                     "type": "keyword"
     *                 }
     *             }
     *         },
     *         "settings": {
     *             "index": {
     *                 "creation_date": "1649243890532",
     *                 "number_of_shards": "9",
     *                 "number_of_replicas": "2",
     *                 "uuid": "EPChtL_vQj2gHJbO5VTHqg",
     *                 "version": {
     *                     "created": "7060099"
     *                 },
     *                 "provided_name": "user"
     *             }
     *         }
     *     }
     * }
     */
    // 创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 定义索引名称
    CreateIndexRequest request = new CreateIndexRequest("user");
    // 添加aliases,对比上述结构来理解
    String aliaseStr = "{\"user.aliases\":{}}";
    Map aliases = JSONObject.parseObject(aliaseStr, Map.class);
    // 添加mappings,对比上述结构来理解
    String mappingStr = "{\"properties\":{\"name\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\"}}},\"sex\":{\"type\":\"keyword\"},\"age\":{\"type\":\"integer\"}}}";
    Map mappings = JSONObject.parseObject(mappingStr, Map.class);
    // 添加settings,对比上述结构来理解
    String settingStr = "{\"index\":{\"number_of_shards\":\"9\",\"number_of_replicas\":\"2\"}}";
    Map settings = JSONObject.parseObject(settingStr, Map.class);

    // 添加数据
    request.aliases(aliases);
    request.mapping(mappings);
    request.settings(settings);

    // 发送请求到ES
    CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
    // 处理响应结果
    System.out.println("添加索引是否成功:" + response.isAcknowledged());
    // 关闭ES客户端对象
    client.close();
}
2、获取索引信息
void getIndexInfo() throws Exception {
    // 创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 定义索引名称
    GetIndexRequest request = new GetIndexRequest("user");
    // 发送请求到ES
    GetIndexResponse response = client.indices().get(request, RequestOptions.DEFAULT);
    // 处理响应结果
    System.out.println("aliases:" + response.getAliases());
    System.out.println("mappings:" + response.getMappings());
    System.out.println("settings:" + response.getSettings());
    // 关闭ES客户端对象
    client.close();
}
3、判断索引是否存在于ES
void existIndex() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    // user是索引名称
    GetIndexRequest request = new GetIndexRequest("user");
    // 3、发送请求到ES
    boolean exist = client.indices().exists(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    System.out.println("索引存在于ES:" + exist);
    // 5、关闭ES客户端对象
    client.close();
}
4、删除索引
void deleteIndex() throws Exception {
    // 创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 定义索引名称
    DeleteIndexRequest request = new DeleteIndexRequest("user");
    // 发送请求到ES
    AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT);
    // 处理响应结果
    System.out.println("删除是否成功:" + response.isAcknowledged());
    // 关闭ES客户端对象
    client.close();
}
5、添加数据到索引中
void insertDataToIndex() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、创建请求对象
    User user = new User();
    user.setName("张三");
    user.setSex("男");
    user.setAge(22);
    // 定义请求对象
    IndexRequest request = new IndexRequest("user");
    // 设置文档id
    request.id("1000");
    // 将json格式字符串放在请求中
    request.source(JSONObject.toJSONString(user), XContentType.JSON);
    // 3、发送请求到ES
    IndexResponse response = client.index(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    System.out.println("数据插入结果:" + response.getResult());
    // 5、关闭ES客户端对象
    client.close();
}
6、局部更新索引中的数据
void updateDataFromIndex() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    User user = new User();
    user.setName("小美");
    user.setAge(20);
    UpdateRequest request = new UpdateRequest();
    request.index("user").id("1000");
    // 拓展:局部更新也可以这样写:request.doc(XContentType.JSON, "name", "小美", "age", 20);,其中"name"和"age"是User对象中的字段名称,而"小美"和20是对应的字段值
    request.doc(JSONObject.toJSONString(user), XContentType.JSON);
    // 同步更新,可以根据需要开启
//        request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
    // 3、发送请求到ES
    UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    System.out.println("数据更新结果:" + response.getResult());
    // 5、关闭ES客户端对象
    client.close();
}
7、根据文档id查询索引中的数据
void getDataById() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    GetRequest request = new GetRequest("user");
    request.id("1000");
    // 只查询特定字段,在下面fields字段值中填写索引属性名称,中间用,分隔;注意:如果需要查询所有字段,则不设置该项
//        String fields = "name";
//        if (fields != null){
//            request.fetchSourceContext(new FetchSourceContext(true,fields.split(","), Strings.EMPTY_ARRAY));
//        }
    // 3、发送请求到ES
    GetResponse response = client.get(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    System.out.println("查询结果:" + response.getSourceAsString());
    // 5、关闭ES客户端对象
    client.close();
}
8、根据文档id删除索引中的数据
void deleteDataById() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    DeleteRequest request = new DeleteRequest("user");
    request.id("1000");
    // 3、发送请求到ES
    DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    System.out.println("删除是否成功:" + response.getResult());
    // 5、关闭ES客户端对象
    client.close();
}
9、根据查询条件删除索引中的数据
void deleteDataByQuery() throws IOException {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    DeleteByQueryRequest request = new DeleteByQueryRequest("user");
    request.setQuery(QueryBuilders.matchAllQuery());
    // 3、发送请求到ES
    BulkByScrollResponse response = client.deleteByQuery(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    System.out.println("删除失败结果:" + response.getBulkFailures());
    // 5、关闭ES客户端对象
    client.close();
}
10、批量插入数据到索引中
void batchInsertDataToIndex() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    // 准备测试数据
    List<User> userList = new ArrayList<>(10);
    for (int i = 0; i < 10; i++) {
        User user = new User();
        user.setName(i % 2 == 0 ? "张三" + i : "小美" + i);
        user.setSex(i % 2 == 0 ? "男" : "女");
        user.setAge(22 + i);
        userList.add(user);
    }
    BulkRequest bulkRequest = new BulkRequest();
    // 准备批量插入的数据
    userList.forEach(user -> {
        // 设置请求对象
        IndexRequest request = new IndexRequest("user");
        // 文档id
        request.id("10000" + user.getAge());
        // 将json格式字符串放在请求中
        // 下面这种写法也可以写成:request.source(XContentType.JSON, "name", "张三", "age", "男", "age", 22);,其中"name"、"age"、 "age"是User对象中的字段名,而这些字段名称后面的值就是对应的值
        request.source(JSONObject.toJSONString(user), XContentType.JSON);
        // 将request添加到批量处理请求中
        bulkRequest.add(request);
    });
    // 3、发送请求到ES
    BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
    // 4、处理响应结果
    System.out.println("批量插入是否失败:" + response.hasFailures());
    // 4.1、插入详细信息
    for (BulkItemResponse itemResponse : response) {
        BulkItemResponse.Failure failure = itemResponse.getFailure();
        if (failure == null) {
            System.out.println("插入成功的文档id:" + itemResponse.getId());
        } else {
            System.out.println("插入失败的文档id:" + itemResponse.getId());
        }
    }
    // 5、关闭ES客户端对象
    client.close();
}
11、批量删除索引中的数据
void batchDeleteDataFromIndex() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    // 准备测试数据(只用到了age来生成文档id,但是为了和上面的批量插入应和,所以需要这样做)
    List<User> userList = new ArrayList<>(10);
    for (int i = 0; i < 10; i++) {
        User user = new User();
        user.setName(i % 2 == 0 ? "张三" + i : "小美" + i);
        user.setSex(i % 2 == 0 ? "男" : "女");
        user.setAge(22 + i);
        userList.add(user);
    }
    BulkRequest bulkRequest = new BulkRequest();
    // 准备批量插入的数据
    userList.forEach(user -> {
        // 设置请求对象
        DeleteRequest request = new DeleteRequest("user");
        // 文档id
        request.id("10000" + user.getAge());
        // 将request添加到批量处理请求中
        bulkRequest.add(request);
    });
    // 3、发送请求到ES
    BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
    // 4、处理响应结果
    System.out.println("批量删除是否失败:" + response.hasFailures());
    // 4.1、删除详细信息
    for (BulkItemResponse itemResponse : response) {
        BulkItemResponse.Failure failure = itemResponse.getFailure();
        if (failure == null) {
            System.out.println("删除成功的文档id:" + itemResponse.getId());
        } else {
            System.out.println("删除失败的文档id:" + itemResponse.getId());
        }
    }
    // 5、关闭ES客户端对象
    client.close();
}
12、高级查询之查询全部数据
void advancedQueryFromAllData() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    SearchRequest request = new SearchRequest();
    request.indices("user");
    // 指定检索条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    // 用来查询索引中全部的数据
    builder.query(QueryBuilders.matchAllQuery());
    request.source(builder);
    // 3、发送请求到ES
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    for (SearchHit hit : response.getHits().getHits()) {
        User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
        System.out.println("姓名:" + user.getName() + ";性别:" + user.getSex() + ";年龄:" + user.getAge());
    }
    // 5、关闭ES客户端对象
    client.close();
}
13、高级查询之term精准匹配
void advancedQueryByTerm() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    SearchRequest request = new SearchRequest();
    request.indices("user");
    // 指定检索条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    // 用来查询sex是男的数据
    builder.query(QueryBuilders.termQuery("sex", "男"));
    request.source(builder);
    // 3、发送请求到ES
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    for (SearchHit hit : response.getHits().getHits()) {
        User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
        System.out.println("姓名:" + user.getName() + ";性别:" + user.getSex() + ";年龄:" + user.getAge());
    }
    // 5、关闭ES客户端对象
    client.close();
}
14、高级查询之分页查询
void advancedQueryByPage() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    SearchRequest request = new SearchRequest();
    request.indices("user");
    // 指定检索条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    // 分页查询数据,本次测试只查询前5条
    builder.query(QueryBuilders.matchAllQuery());
    int currentPage = 1;
    int pageSize = 5;
    int from = (currentPage - 1) * pageSize;
    builder.from(from);
    builder.size(pageSize);
    request.source(builder);
    // 3、发送请求到ES
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    for (SearchHit hit : response.getHits().getHits()) {
        User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
        System.out.println("姓名:" + user.getName() + ";性别:" + user.getSex() + ";年龄:" + user.getAge());
    }
    // 5、关闭ES客户端对象
    client.close();
}
15、高级查询之排序查询
void advancedQueryBySort() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    SearchRequest request = new SearchRequest();
    request.indices("user");
    // 指定检索条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.matchAllQuery());
    // 根据年龄做降序排序
    builder.sort("age", SortOrder.DESC);
    request.source(builder);
    // 3、发送请求到ES
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    for (SearchHit hit : response.getHits().getHits()) {
        User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
        System.out.println("姓名:" + user.getName() + ";性别:" + user.getSex() + ";年龄:" + user.getAge());
    }
    // 5、关闭ES客户端对象
    client.close();
}
16、高级查询之source获取部分字段内容
void advancedQueryBySource() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    SearchRequest request = new SearchRequest();
    request.indices("user");
    // 指定检索条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.matchAllQuery());
    // 如果查询的属性很少,那就使用includes,而excludes设置为空数组
    // 如果排序的属性很少,那就使用excludes,而includes设置为空数组
    String[] includes = {"name", "age"};
    String[] excludes = {};
    builder.fetchSource(includes, excludes);
    request.source(builder);
    // 3、发送请求到ES
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    for (SearchHit hit : response.getHits().getHits()) {
        User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
        System.out.println("姓名:" + user.getName() + ";性别:" + user.getSex() + ";年龄:" + user.getAge());
    }
    // 5、关闭ES客户端对象
    client.close();
}
17、高级查询之should匹配
void advancedQueryByShould() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    SearchRequest request = new SearchRequest();
    request.indices("user");
    // 指定检索条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    boolQueryBuilder.should(QueryBuilders.matchQuery("age", 30));
    // 查询中boost默认是1,写成10可以增大score比分
    boolQueryBuilder.should(QueryBuilders.matchQuery("sex", "女").boost(10));
    builder.query(boolQueryBuilder);
    request.source(builder);
    // 3、发送请求到ES
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    for (SearchHit hit : response.getHits().getHits()) {
        User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
        System.out.println("姓名:" + user.getName() + ";性别:" + user.getSex() + ";年龄:" + user.getAge());
    }
    // 5、关闭ES客户端对象
    client.close();
}
18、高级查询之filter过滤查询
void advancedQueryByFilter() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    SearchRequest request = new SearchRequest();
    request.indices("user");
    // 指定检索条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    // 查询年龄大于等于26,小于等于29的结果
    boolQueryBuilder.filter(QueryBuilders.rangeQuery("age").gte(26).lte(29));
    builder.query(boolQueryBuilder);
    request.source(builder);
    // 3、发送请求到ES
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    for (SearchHit hit : response.getHits().getHits()) {
        User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
        System.out.println("姓名:" + user.getName() + ";性别:" + user.getSex() + ";年龄:" + user.getAge());
    }
    // 5、关闭ES客户端对象
    client.close();
}
19、高级查询之模糊查询
void advancedQueryByLike() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    SearchRequest request = new SearchRequest();
    request.indices("user");
    // 指定检索条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    // 和分词无关,这就是和mysql中like类似的做法
    // 查询名称中包含“张三”的数据,或者比“张三”多一个字符的数据,这是通过Fuzziness.ONE来控制的,比如“张三1”是可以出现的,但是“张三12”是无法出现的,这是因为他比张三多了两个字符;除了“Fuzziness.ONE”之外,还可以是“Fuzziness.TWO”等
    builder.query(QueryBuilders.fuzzyQuery("name.keyword", "张三").fuzziness(Fuzziness.ONE));
    request.source(builder);
    // 3、发送请求到ES
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    for (SearchHit hit : response.getHits().getHits()) {
        User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
        System.out.println("姓名:" + user.getName() + ";性别:" + user.getSex() + ";年龄:" + user.getAge());
    }
    // 5、关闭ES客户端对象
    client.close();
}
20、高级查询之高亮查询
void advancedQueryByHighLight() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    SearchRequest request = new SearchRequest();
    request.indices("user");
    // 指定检索条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    // 设置查询条件
    builder.query(QueryBuilders.matchPhraseQuery("name", "张三"));
    // 构建高亮查询对象
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    // 前置标签
    highlightBuilder.preTags("<b style='color:red'>");
    // 后置标签
    highlightBuilder.postTags("</b>");
    // 添加高亮的属性名称
    highlightBuilder.field("name");
    builder.highlighter(highlightBuilder);
    request.source(builder);
    // 3、发送请求到ES
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        Map<String, HighlightField> map = hit.getHighlightFields();
        HighlightField highlightField = map.get("name");
        System.out.println("高亮名称:" + highlightField.getFragments()[0].string());
    }
    // 5、关闭ES客户端对象
    client.close();
}
21、高级查询之最大值聚合查询
void advancedQueryByMaxValueAggregation() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    SearchRequest request = new SearchRequest();
    request.indices("user");
    // 指定检索条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    // 获取最大年龄
    AggregationBuilder aggregationBuilder = AggregationBuilders.max("maxAge").field("age");
    builder.aggregation(aggregationBuilder);
    // 设置size为0,不查询具体数据,只查询聚合结果
    builder.size(0);
    request.source(builder);
    // 3、发送请求到ES
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    Aggregations aggregations = response.getAggregations();
    ParsedMax maxAge = aggregations.get("maxAge");
    System.out.println("最大年龄:" + maxAge.getValue());
    // 5、关闭ES客户端对象
    client.close();
}
22、高级查询之分组聚合查询
void advancedQueryByGroupAggregation() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    SearchRequest request = new SearchRequest();
    request.indices("user");
    // 指定检索条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    // 按照性别分组,聚合操作要求和分词操作无关,由于sex在默认添加的时候是text类型,因为需要设置为keyword类型
    AggregationBuilder aggregationBuilder = AggregationBuilders.terms("termsSex").field("sex.keyword");
    builder.aggregation(aggregationBuilder);
    // 设置size为0,不查询具体数据,只查询聚合结果
    builder.size(0);
    request.source(builder);
    // 3、发送请求到ES
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    Aggregations aggregations = response.getAggregations();
    // 至于使用ParsedStringTerms、ParsedLongTerms、ParsedMax、ParsedNested、ParsedAvg……是由聚合要求和聚合字段类型确定的,比如本次要求是分组,并且聚合字段是sex,那就是String类型,所以使用ParsedStringTerms
    ParsedStringTerms termsSex = aggregations.get("termsSex");
    for (Terms.Bucket bucket : termsSex.getBuckets()) {
        System.out.println("性别:" + bucket.getKeyAsString() + ";数量:" + bucket.getDocCount());
    }
    // 5、关闭ES客户端对象
    client.close();
}
23、根据查询条件计算数据量
void count() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    CountRequest request = new CountRequest();
    request.indices("user");
    // 指定检索条件
    request.query(QueryBuilders.matchAllQuery());
    // 如果索引不存在,不会报错
    request.indicesOptions(IndicesOptions.fromOptions(true, true, false, false));
    // 3、发送请求到ES
    CountResponse response = client.count(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    System.out.println("数据总量:" + response.getCount());
    // 5、关闭ES客户端对象
    client.close();
}
24、根据查询条件滚动查询
void scrollQuery() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    // 假设用户想获取第10页数据,其中每页20条
    int totalPage = 10;
    int size = 20;
    SearchRequest searchRequest = new SearchRequest("user");
    // 指定检索条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    searchRequest.source(builder.query(QueryBuilders.matchAllQuery()).size(size));
    String scrollId = null;
    // 3、发送请求到ES
    SearchResponse scrollResponce = null;
    // 设置游标id存活时间
    Scroll scroll = new Scroll(TimeValue.timeValueMinutes(2));
    // 记录所有游标id
    List<String> scrollIds = new ArrayList<>();
    for (int i = 0; i < totalPage; i++) {
        try {
            // 首次检索
            if (i == 0) {
                //记录游标id
                searchRequest.scroll(scroll);
                // 首次查询需要指定索引名称和查询条件
                SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
                // 下一次搜索要用到该游标id
                scrollId = response.getScrollId();
                // 记录所有游标id
                scrollIds.add(scrollId);
            }
            // 非首次检索
            else {
                // 不需要在使用其他条件,也不需要指定索引名称,只需要使用执行游标id存活时间和上次游标id即可,毕竟信息都在上次游标id里面呢
                SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
                searchScrollRequest.scroll(scroll);
                scrollResponce = client.scroll(searchScrollRequest, RequestOptions.DEFAULT);
                // 下一次搜索要用到该游标id
                scrollId = scrollResponce.getScrollId();
                // 记录所有游标id
                scrollIds.add(scrollId);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //清除游标id
    ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
    clearScrollRequest.scrollIds(scrollIds);
    try {
        client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
    } catch (IOException e) {
        System.out.println("清除滚动查询游标id失败");
        e.printStackTrace();
    }
    // 4、处理响应结果
    System.out.println("滚动查询返回数据:" + scrollResponce);
    // 5、关闭ES客户端对象
    client.close();
}
25、根据索引名称和文档id查询单个文档是否存在于ES
void existById() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    // user是索引名称,1000是文档id
    GetRequest request = new GetRequest("user", "1000");
    // 不获取返回的_source的上下文
    request.fetchSourceContext(new FetchSourceContext(false));
    request.storedFields("_none_");
    // 3、发送请求到ES
    boolean exist = client.exists(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    System.out.println("文档存在于ES:" + exist);
    // 5、关闭ES客户端对象
    client.close();
}
26、根据索引名称和文档id查询多个文档是否存在于ES
void multiQueryById() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    MultiGetRequest request = new MultiGetRequest();
    // user是索引名称,1000和2000都是文档id
    request.add("user", "1000");
    request.add("user", "2000");
    // 3、发送请求到ES
    MultiGetResponse response = client.mget(request, RequestOptions.DEFAULT);
    // 4、处理响应结果
    List<String> existIdList = new ArrayList<>();
    for (MultiGetItemResponse itemResponse : response) {
        MultiGetResponse.Failure failure = itemResponse.getFailure();
        GetResponse getResponse = itemResponse.getResponse();
        if (failure == null) {
            boolean exists = getResponse.isExists();
            String id = getResponse.getId();
            if (exists) {
                existIdList.add(id);
            }
        } else {
            failure.getFailure().printStackTrace();
        }
    }
    System.out.println("数据存在于ES的文档id:" + existIdList);
    // 5、关闭ES客户端对象
    client.close();
}
27、打印集群名称和健康状况
void printClusterNameAndStatus() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、定义请求对象
    ClusterHealthRequest request = new ClusterHealthRequest();
    // 3、发送请求到ES
    ClusterHealthResponse response = client.cluster().health(request, RequestOptions.DEFAULT);
    // 4、获取健康状况
    ClusterHealthStatus status = response.getStatus();
    // 5、打印集群名称
    System.out.println("集群名称:" + response.getClusterName());
    // 6、打印集群状态
    System.out.println("集群健康状态:" + status.name());
    // 7、关闭ES客户端对象
    client.close();
}
28、打印索引信息
void printIndexInfo() throws Exception {
    // 1、创建ES客户端对象
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
    // 2、发送请求到ES
    Response response = client.getLowLevelClient().performRequest(new Request("GET", "/_cat/indices"));
    // 3、数据处理
    HttpEntity entity = response.getEntity();
    String responseStr = EntityUtils.toString(entity, StandardCharsets.UTF_8);
    // 4、数据分解
    String[] indexInfoArr = responseStr.split("\n");
    for (String indexInfo : indexInfoArr) {
        // 4.1、索引信息输出
        String[] infoArr = indexInfo.split("\\s+");
        String status = infoArr[0];
        String open = infoArr[1];
        String name = infoArr[2];
        String id = infoArr[3];
        String mainShardNum = infoArr[4];
        String viceShardNum = infoArr[5];
        String docNum = infoArr[6];
        String deletedDocNum = infoArr[7];
        String allShardSize = infoArr[8];
        String mainShardSize = infoArr[9];
        System.out.println("》》》》》》》》索引信息》》》》》》》》");
        System.out.println("名称:" + name);
        System.out.println("id:" + id);
        System.out.println("状态:" + status);
        System.out.println("是否开放:" + open);
        System.out.println("主分片数量:" + mainShardNum);
        System.out.println("副本分片数量:" + viceShardNum);
        System.out.println("Lucene文档数量:" + docNum);
        System.out.println("被删除文档数量:" + deletedDocNum);
        System.out.println("所有分片大小:" + allShardSize);
        System.out.println("主分片大小:" + mainShardSize);
    }
    // 6、关闭ES客户端对象
    client.close();
}

三、拓展

1、 indice可以设置多个
// 方法1
CountRequest request = new CountRequest("index1", "index2");


// 方法2
CountRequest request = new CountRequest();
request.indices("index1", "index2");
2、执行查询操作时,如果索引不存在,可以设置以下参数保证查询不报错
// request可以是CountRequest、SearchRequest对象
// 注意:如果不设置下列参数,在查询的时候将会出现索引不存在错误
request.indicesOptions(IndicesOptions.fromOptions(true, true, false, false));
3、创建ES客户端对象,包括:单节点(无密码)、单节点(有密码)、集群(无密码)、集群(有密码)场景
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class ESTestApplicationTests {

    // ES客户端对象
    private static RestHighLevelClient client;

    static {
        // 创建ES客户端对象
        // 1、单机版
        // 1.1、无密码
        client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));

        // 1.2有密码
//        RestClientBuilder builder = RestClient.builder(new HttpHost("192.168.56.10", 9200, "http"));
//        // 设置用户名和密码
//        String username = "elastic";
//        String password = "123456";
//        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
//        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
//        builder.setHttpClientConfigCallback(f -> f.setDefaultCredentialsProvider(credentialsProvider));
//        client = new RestHighLevelClient(builder);

        // 2、集群版
        // 1.2、无密码
//        client = new RestHighLevelClient(
//                RestClient.builder(
//                        new HttpHost("localhost", 9201, "http"),
//                        new HttpHost("localhost", 9202, "http"),
//                        new HttpHost("localhost", 9203, "http")
//                )
//        );

        // 2.2有密码
//        RestClientBuilder builder = RestClient.builder(
//                new HttpHost("192.168.56.10", 9201, "http"),
//                new HttpHost("192.168.56.10", 9202, "http"),
//                new HttpHost("192.168.56.10", 9203, "http")
//        );
//        // 设置用户名和密码
//        String username = "elastic";
//        String password = "123456";
//        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
//        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
//        builder.setHttpClientConfigCallback(f -> f.setDefaultCredentialsProvider(credentialsProvider));
//        client = new RestHighLevelClient(builder);
    }
}

四、实战

1、批量插入
public class ESSaveServiceImpl implements ESSaveService {

    @Resource
    private RestHighLevelClient client;

    @Override
    public Boolean productUp(List<SkuEsModel> list) throws IOException {
        // 创建批量处理请求
        BulkRequest bulkRequest = new BulkRequest();
        // 将需要处理的请求添加到批量处理请求中
        list.forEach(model -> {
            // 索引名称为product
            IndexRequest request = new IndexRequest(ESConstant.PRODUCT_INDEX);
            // 文档id,要求为String类型
            // The id of the indexed document. If not set, will be automatically generated.
            request.id(model.getSkuId().toString());
            // 将需要传输的数据转换为json格式字符串
            String jsonString = JSONObject.toJSONString(model);
            // 将json格式字符串放在请求中
            request.source(jsonString, XContentType.JSON);
            // 将request添加到批量处理请求中
            bulkRequest.add(request);
        });
        // 发送请求到es进行批量处理
        BulkResponse bulk = client.bulk(bulkRequest, ElasticsearchConfig.COMMON_OPTIONS);

        // 返回批量处理是否成功
        return bulk.hasFailures();
    }
}
1.1、对应索引结构
PUT gulimall_product
{
  "mappings": {
    "properties": {
      "skuId": {
        "type": "long"
      },
      "spuId": {
        "type": "keyword"
      },
      "skuTitle": {
        "type": "text",
        "analyzer": "ik_smart"
      },
      "skuPrice": {
        "type": "double"
      },
      "skuImg": {
        "type": "keyword"
      },
      "saleCount": {
        "type": "long"
      },
      "hasStock": {
        "type": "boolean"
      },
      "hotScore": {
        "type": "long"
      },
      "brandId": {
        "type": "long"
      },
      "catalogId": {
        "type": "long"
      },
      "brandName": {
        "type": "keyword"
      },
      "brandImg": {
        "type": "keyword"
      },
      "catalogName": {
        "type": "keyword"
      },
      "attrs": {
        "type": "nested",
        "properties": {
          "attrId": {
            "type": "long"
          },
          "attrName": {
            "type": "keyword"
          },
          "attrValue": {
            "type": "keyword"
          }
        }
      }
    }
  }
}
2、聚合查询
public class ESSearchServiceImpl implements ESSearchService {

    @Resource
    private RestHighLevelClient client;

    @Resource
    private ProductFeignService productFeignService;

    @Override
    public SearchResult search(SearchParam param) {
        // 返回数据
        SearchResult result = new SearchResult();
        // 1、创建检索请求
        SearchRequest searchRequest = buildSearchRequest(param);

        try {
            // 2、执行检索,获取响应数据
            SearchResponse response = client.search(searchRequest, ElasticsearchConfig.COMMON_OPTIONS);

            // 3、分析响应数据,将响应数据封装成我们想要的格式
            result = buildSearchResult(response, param);

        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 构建检索请求
     *
     * @return
     */
    private SearchRequest buildSearchRequest(SearchParam param) {
        // 指定Query DSL,即检索条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();


        // **************************构建查询条件start***********************
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        if (StringUtils.isNotEmpty(param.getKeyword())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle", param.getKeyword()));
        }

        if (ObjectUtils.allNotNull(param.getCatalog3Id())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId", param.getCatalog3Id()));
        }

        if (ObjectUtils.allNotNull(param.getBrandId()) && param.getBrandId().size() > 0) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId", param.getBrandId()));
        }

        if (ObjectUtils.allNotNull(param.getAttrs()) && param.getAttrs().size() > 0) {
            param.getAttrs().forEach(attr -> {
                BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                // attrs=1_安卓:苹果:塞班&attrs=2_5寸:6寸
                String[] s1 = attr.split("_");
                String[] s2 = s1[1].split(":");
                boolQuery.must(QueryBuilders.termQuery("attrs.attrId", s1[0]));
                boolQuery.must(QueryBuilders.termsQuery("attrs.attrValue", s2));
                // ScoreMode.None表示不参与评分
                boolQueryBuilder.filter(QueryBuilders.nestedQuery("attrs", boolQuery, ScoreMode.None));
            });
        }

        if (ObjectUtils.allNotNull(param.getHasStock())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock", param.getHasStock() == 1));
        }

        if (StringUtils.isNotEmpty(param.getSkuPrice())) {
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");
            String[] s = param.getSkuPrice().split("_");
            if (s.length == 2 && StringUtils.isNotEmpty(s[0])) {
                rangeQuery.gte(s[0]).lte(s[1]);
            } else if (s.length == 2 && param.getSkuPrice().startsWith("_")) {
                // 由于"_8000"分割之后会出现["", "8000"]这种情况,所以我们这样做
                rangeQuery.lte(s[1]);
            } else if (s.length == 1 && param.getSkuPrice().endsWith("_")) {
                rangeQuery.gte(s[0]);
            }
            boolQueryBuilder.filter(rangeQuery);
        }

        sourceBuilder.query(boolQueryBuilder);
        // **************************构建查询条件end***********************

        // **************************构建排序条件start***********************
        /*
         * 排序条件
         * sort=saleCount_asc/desc---》销量
         * sort=skuPrice_asc/desc---》价格
         * sort=hotScore_asc/desc---》热度评分
         */
        if (StringUtils.isNotEmpty(param.getSort())) {
            String[] s = param.getSort().split("_");
            SortOrder order = "asc".equalsIgnoreCase(s[1]) ? SortOrder.ASC : SortOrder.DESC;
            sourceBuilder.sort(s[0], order);
        }
        // **************************构建排序条件end***********************

        // **************************构建分页条件start***********************
        int start = (param.getPageNum() - 1) * ESConstant.PAGE_SIZE;
        sourceBuilder.from(start).size(ESConstant.PAGE_SIZE);
        // **************************构建分页条件end***********************

        // **************************构建高亮条件start***********************
        if (StringUtils.isNotEmpty(param.getKeyword())) {
            HighlightBuilder builder = new HighlightBuilder();
            builder.preTags("<b style='color:red'>");
            builder.postTags("</b>");
            builder.field("skuTitle");
            sourceBuilder.highlighter(builder);
        }
        // **************************构建高亮条件end***********************

        // **************************构建聚合条件start***********************
        // 创建聚合
        TermsAggregationBuilder brandAgg = AggregationBuilders.terms("brand_agg")
                .field("brandId").size(32);
        // 添加子聚合
        brandAgg.subAggregation(AggregationBuilders.terms("brand_name_agg")
                .field("brandName").size(1));
        brandAgg.subAggregation(AggregationBuilders.terms("brand_img_agg")
                .field("brandImg").size(1));
        // 创建聚合
        TermsAggregationBuilder catalogAgg = AggregationBuilders.terms("catalog_agg")
                .field("catalogId").size(14);
        // 添加子聚合
        catalogAgg.subAggregation(AggregationBuilders.terms("catalog_name_agg")
                .field("catalogName").size(1));
        // 创建聚合
        NestedAggregationBuilder attrAgg = AggregationBuilders.nested("attr_agg", "attrs");
        // 添加子聚合
        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attr_id_agg")
                .field("attrs.attrId").size(14);
        // 添加孙子聚合
        attrIdAgg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
        attrIdAgg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(12));
        attrAgg.subAggregation(attrIdAgg);
        // 将聚合添加到请求中
        sourceBuilder.aggregation(brandAgg);
        sourceBuilder.aggregation(catalogAgg);
        sourceBuilder.aggregation(attrAgg);
        // **************************构建聚合条件end***********************


        // 创建检索请求:首先要指定被检索的索引,然后添加检索条件
        SearchRequest searchRequest = new SearchRequest(new String[]{ESConstant.PRODUCT_INDEX}, sourceBuilder);

        return searchRequest;
    }

    /**
     * 构建响应结果
     *
     * @return
     */
    private SearchResult buildSearchResult(SearchResponse response, SearchParam param) {
        // 总返回结果
        SearchResult result = new SearchResult();

        // 获取es返回的结果
        SearchHits hits = response.getHits();

        // 1、所有返回的商品信息
        List<SkuEsModel> products = new ArrayList<>();
        // 获得检索信息
        SearchHit[] searchHits = hits.getHits();
        if (searchHits != null && searchHits.length > 0) {
            // 将hits中的hits中的值放在对象中
            for (SearchHit hit : searchHits) {
                String str = hit.getSourceAsString();
                // 普通类型可以直接这样写,复杂类型需要用new TypeReference<类型>(){}来完成,例如List<XXX>就是复杂类型
                SkuEsModel skuEsModel = JSONObject.parseObject(str, SkuEsModel.class);
                // 如果有全文检索关键字,那就需要使用高亮显示
                if (StringUtils.isNotEmpty(param.getKeyword())) {
                    // 以下结果是debug返回值response,分析控制台中的response中的internalResponse---》hits---》hits得出来的
                    Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                    HighlightField highlightField = highlightFields.get("skuTitle");
                    String skuTitle = highlightField.getFragments()[0].string();
                    skuEsModel.setSkuTitle(skuTitle);
                }
                products.add(skuEsModel);
            }
        }
        // 将商品信息放在总返回结果中
        result.setProducts(products);

        // 获得聚合信息
        // 注意:聚合中用到的ParsedLongTerms、ParsedStringTerms、ParsedNested类型来自于debug返回值response,通过查看控制台response中的internalResponse---》aggregations得出返回值类型
        Aggregations aggregations = response.getAggregations();

        // 2、检索出来的所有品牌信息
        List<SearchResult.BrandVo> brands = new ArrayList<>();
        // 分析聚合信息
        ParsedLongTerms brandAgg = aggregations.get("brand_agg");
        // 判断聚合信息是否为空
        if (ObjectUtils.allNotNull(brandAgg) && brandAgg.getBuckets().size() > 0) {
            for (Terms.Bucket bucket : brandAgg.getBuckets()) {
                // 返回对象
                SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
                // 放品牌id
                String brandId = bucket.getKeyAsString();
                brandVo.setBrandId(Long.parseLong(brandId));
                // 放品牌图片
                ParsedStringTerms brandImgAgg = bucket.getAggregations().get("brand_img_agg");
                brandVo.setBrandImg(brandImgAgg.getBuckets().get(0).getKeyAsString());
                // 放品牌名称
                ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brand_name_agg");
                brandVo.setBrandName(brandNameAgg.getBuckets().get(0).getKeyAsString());
                // 将对象放入返回值
                brands.add(brandVo);
            }
        }
        // 将聚合信息添加到总返回结果中
        result.setBrands(brands);

        // 3、检索出来的所有分类信息
        List<SearchResult.CatalogVo> catalogs = new ArrayList<>();
        // 分析聚合信息
        ParsedLongTerms catalogAgg = aggregations.get("catalog_agg");
        // 判断聚合信息是否为空
        if (ObjectUtils.allNotNull(catalogAgg) && catalogAgg.getBuckets().size() > 0) {
            for (Terms.Bucket bucket : catalogAgg.getBuckets()) {
                // 返回对象
                SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
                // 放分类id
                String catalogId = bucket.getKeyAsString();
                catalogVo.setCatalogId(Long.parseLong(catalogId));
                // 放分类名称
                ParsedStringTerms catalogNameAgg = bucket.getAggregations().get("catalog_name_agg");
                catalogVo.setCatalogName(catalogNameAgg.getBuckets().get(0).getKeyAsString());
                // 将对象放入返回值
                catalogs.add(catalogVo);
            }
        }
        // 将聚合信息添加到总返回结果中
        result.setCatalogs(catalogs);

        // 4、所有涉及到的可以检索的属性信息
        List<SearchResult.AttrVo> attrs = new ArrayList<>();
        // 分析聚合信息
        ParsedNested attrAgg = aggregations.get("attr_agg");
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attr_id_agg");
        // 判断聚合信息是否为空
        if (ObjectUtils.allNotNull(attrIdAgg) && attrIdAgg.getBuckets().size() > 0) {
            for (Terms.Bucket bucket : attrIdAgg.getBuckets()) {
                // 返回对象
                SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
                // 放分类id
                String attrId = bucket.getKeyAsString();
                attrVo.setAttrId(Long.parseLong(attrId));
                // 放分类名称
                ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attr_name_agg");
                attrVo.setAttrName(attrNameAgg.getBuckets().get(0).getKeyAsString());
                // 放分类值
                ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attr_value_agg");
                // 创建分类值列表
                List<String> attrValue = new ArrayList<>();
                for (Terms.Bucket bucket1 : attrValueAgg.getBuckets()) {
                    attrValue.add(bucket1.getKeyAsString());
                }
                // 放分类值列表
                attrVo.setAttrValue(attrValue);
                // 将对象放入返回值
                attrs.add(attrVo);
            }
        }
        // 将聚合信息添加到总返回结果中
        result.setAttrs(attrs);

        // 5、总记录数
        long total = hits.getTotalHits().value;
        result.setTotal(total);

        // 6、总页数
        int totalPage = (int) total % ESConstant.PAGE_SIZE > 0 ? ((int) total / ESConstant.PAGE_SIZE + 1) : (int) total / ESConstant.PAGE_SIZE;
        result.setTotalPage(totalPage);

        // 7、当前页
        result.setPageNum(param.getPageNum());

        // 8、面包屑导航
        // 处理属性面包屑导航
        if (param.getAttrs() != null && param.getAttrs().size() > 0) {
            List<SearchResult.NavVo> navVos = param.getAttrs().stream().map(attr -> {
                SearchResult.NavVo navVo = new SearchResult.NavVo();
                // attr就类似于attrs=1_安卓:苹果:塞班中的1_安卓:苹果:塞班
                String[] s = attr.split("_");
                // 将所有属性id依次放在属性id集合中,前端隐藏属性选择表的时候可以用
                result.getAttrIds().add(Long.parseLong(s[0]));
                // 把navValue按照v1,v2……这种方式放在navVo返回值中
                String[] values = s[1].split(":");
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < values.length; i++) {
                    sb.append(values[i]);
                    if (i < values.length - 1) {
                        sb.append(",");
                    }
                }
                navVo.setNavValue(sb.toString());
                // 把navName放在navVo返回值中
                R r = productFeignService.attrInfo(Long.parseLong(s[0]));
                if (r.getCode() == Constant.SUCCESS_CODE) {
                    AttrRespVo attrRespVo = r.getData("attr", new TypeReference<AttrRespVo>() {
                    });
                    navVo.setNavName(attrRespVo.getAttrName());
                } else {
                    navVo.setNavName(s[0]);
                }
                // 把link放在navVo返回值中
                String link = getLinkByAttrs(param.get_requestURI(), param.get_queryString(), "attrs", attr);
                navVo.setLink(link);
                // 返回相应对象
                return navVo;
            }).collect(Collectors.toList());
            // 将面包屑导航集合放在返回结果中
            result.setNavVos(navVos);
        }
        // 处理商标面包屑导航
        if (param.getBrandId() != null && param.getBrandId().size() > 0) {
            // 获取面包屑导航集合
            List<SearchResult.NavVo> navVos = result.getNavVos();
            SearchResult.NavVo navVo = new SearchResult.NavVo();
            navVo.setNavName("品牌");
            // 从远程获取数据
            R r = productFeignService.brandInfo(param.getBrandId());
            // 判断获取远程数据是否成功
            if (r.getCode() == Constant.SUCCESS_CODE) {
                // 获取远程数据
                List<BrandVo> brandVos = r.getData("brand", new TypeReference<List<BrandVo>>() {
                });
                // 用来存储商标名称,毕竟在设计之初可以多选,最后名称类似于"华为,Apple"
                StringBuilder sb = new StringBuilder();
                // 去掉所有商标信息的地址
                String link = null;
                // 所有商标id集合
                List<Long> brandIds = new ArrayList<>();
                for (int i = 0; i < brandVos.size(); i++) {
                    sb.append(brandVos.get(i).getName());
                    if (i < brandVos.size() - 1) {
                        sb.append(",");
                    }
                    brandIds.add(brandVos.get(i).getBrandId());
                }
                link = getLinkByBrandId(param.get_requestURI(), param.get_queryString(), "brandId", brandIds);
                // 将经过遍历的值添加进去
                navVo.setLink(link);
                navVo.setNavValue(sb.toString());
            } else {
                // 获取远程数据出错,进行处理
                navVo.setNavValue("首页");
                navVo.setLink("http://gulimall.com");
            }
            // 将商标信息添加到面包屑导航中
            navVos.add(navVo);
        }
        // 最终返回给前端的数据
        return result;
    }

    // 删除相关属性,然后获取链接
    private String getLinkByAttrs(String requestUri, String queryString, String key, String value) {
        String encode = null;
        try {
            // 前端地址会把空格变成%20,而后端使用URLEncoder.encode()之后会把空格变成+,所以我们需要使用replaceAll替换操作,还有执行URLEncoder.encode()之后会将/转义,但是我不需要转义
            // 注意:只能改变自己去适应前端变化,不能改变前端的地址信息,否则前端不能访问了怎么办?
            encode = URLEncoder.encode(value, "UTF-8").replaceAll("\\+", "%20").replaceAll("%2F","/");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        // 可能该属性是中间值或者最后一个值(前面还有值)
        queryString = queryString.replace("&" + key + "=" + encode, "");
        // 可能该属性是第一个值,后面还有值
        queryString = queryString.replace(key + "=" + encode + "&", "");
        // 可能该属性是第一个值,后面没有值
        queryString = queryString.replace(key + "=" + encode, "");

        if (StringUtils.isNotEmpty(queryString)) {
            return "http://search.gulimall.com" + requestUri + "?" + queryString;
        } else {
            return "http://search.gulimall.com" + requestUri;
        }
    }

    // 删除所有品牌id,然后获取链接
    private String getLinkByBrandId(String requestUri, String queryString, String key, List<Long> values) {

        // 把所有品牌id参数都去掉
        for (Long value : values) {
            // 可能该属性是中间值或者最后一个值(前面还有值)
            queryString = queryString.replace("&" + key + "=" + value, "");
            // 可能该属性是第一个值,后面还有值
            queryString = queryString.replace(key + "=" + value + "&", "");
            // 可能该属性是第一个值,后面没有值
            queryString = queryString.replace(key + "=" + value, "");
        }

        if (StringUtils.isNotEmpty(queryString)) {
            return "http://search.gulimall.com" + requestUri + "?" + queryString;
        } else {
            return "http://search.gulimall.com" + requestUri;
        }
    }
}
2.1、对应索引结构

请看 四、实战》1、批量插入》1.1、对应索引结构

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐