Spring Boot项目中使用ElasticSearch

Spring Boot项目中使用ElasticSearch

当项目中的数据库数据量过大,查询速度等都非常影响体验,提升速度方式有采用分库,分表,读写分离等方式。当然采用其他办法也可以减缓数据库压力。

Elasticsearch的优点

  1. 速度快
  2. 分布式,可扩展,高实时
  3. 可以分析数据
  4. 跨平台(基于RESTful web接口)

传统关系型数据库和es的对应关系

git代码demo路径指引

开发环境

  • JDK1.8
  • Spring Boot 2.4.2
  • Elasticsearch 7.6.2

1.依赖添加

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.6.2</version>
</dependency>

2.配置设置

spring.elasticsearch.rest.uris = 192.168.99.100:9200,192.168.99.100:9201,192.168.99.100:9202

3.es使用RestHighLevelClient操作

import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
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 org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.Objects;

/**
 * es 配置文件
 *
 * @author jun
 */
@Slf4j
@Configuration
public class ElasticSearchConfig {

    @Value("${spring.elasticsearch.rest.uris}")
    String[] ipAddress;

    private static final int ADDRESS_LENGTH = 2;
    private static final String HTTP_SCHEME = "http";

    @Bean
    public RestHighLevelClient restHighLevelClient() {
        return new RestHighLevelClient(
                RestClient.builder(
                        Arrays.stream(ipAddress)
                                .map(this::makeHttpHost)
                                .filter(Objects::nonNull)
                                .toArray(HttpHost[]::new)
                )
        );
    }

    private HttpHost makeHttpHost(String s) {
        assert !StringUtils.isEmpty(s);
        String[] address = s.split(":");
        if (address.length == ADDRESS_LENGTH) {
            String ip = address[0];
            int port = Integer.parseInt(address[1]);
            log.info(ip + ":" + port);
            return new HttpHost(ip, port, HTTP_SCHEME);
        } else {
            return null;
        }
    }

}

4.操作

import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
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.client.RequestOptions;
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.common.settings.Settings;
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.rest.RestStatus;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds;

/**
 * @author song
 * @date 2021-01-04
 * @desc
 */
@Component
public class ElasticSearchUtil {

    @Autowired
    @Qualifier("restHighLevelClient")
    private RestHighLevelClient client;

    @Resource
    private ApplicationContext ctx;

    /**
     * 创建索引
     *
     * @param index  索引
     * @return
     * @throws Exception
     */
    public boolean createIndex(String index) throws Exception {
        //创建索引
        CreateIndexRequest request = new CreateIndexRequest(index);

        // 2、设置索引的settings
        request.settings(Settings.builder()
                // 分片数
                 .put("index.number_of_shards", 1)
                // 副本数
                 .put("index.number_of_replicas", 1)
                // 默认分词器
                //.put("analysis.analyzer.default.tokenizer", "ik_max_word")
        );

        // 3、设置索引的mapping 默认文档类型_doc
        request.mapping(//"_doc",
                "{\n" +
                        "    \"properties\":{\n" +
                        "        \"create_time\":{\n" +
                        "            \"type\":\"date\"\n" +
                        "        },\n" +
                        "        \"id\":{\n" +
                        "            \"type\":\"long\"\n" +
                        "        },\n" +
                        "        \"user_id\":{\n" +
                        "            \"type\":\"long\"\n" +
                        "        }\n" +
                        "    }\n" +
                        "}",
                XContentType.JSON);

        // 5、 发送请求
        // 5.1 同步方式发送请求
        CreateIndexResponse createIndexResponse = client.indices()
                .create(request, RequestOptions.DEFAULT);

        // 6、处理响应
        boolean acknowledged = createIndexResponse.isAcknowledged();
        boolean shardsAcknowledged = createIndexResponse.isShardsAcknowledged();

        return acknowledged;

    }

    public BulkResponse request(BulkRequest request, RequestOptions aDefault) throws IOException {
        request.timeout(new TimeValue(6000, TimeUnit.SECONDS));
        return client.bulk(request, RequestOptions.DEFAULT);
    }


    /**
     * 查询数据
     *
     * @param dto 条件
     * @return
     * @throws IOException
     */
    public List<Map<String, Object>> getRecordData(MessageDTO dto, String index, String type) throws IOException {
        // init
        if (dto.getCurrent() == null) {
            dto.setCurrent(1);
        }
        if (dto.getSize() == null) {
            dto.setSize(10);
        }
        Integer pageSize = dto.getSize();
        Integer from = (dto.getCurrent() - 1) * pageSize;
        if (from <= 0) {
            from = 0;
        }

        // 1、创建search请求
        SearchRequest searchRequest = new SearchRequest(index);
        searchRequest.types(type);

        // 2、用SearchSourceBuilder来构造查询请求体 ,请仔细查看它的方法,构造各种查询的方法都在这。
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 集合查询
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.rangeQuery("create_time").gte(dto.getFromTime()).lte(dto.getEndTime()));

        sourceBuilder.query(queryBuilder);
        // 设置from确定结果索引以开始搜索的选项。预设为0。
        sourceBuilder.from(from);
        // 设置size用于确定要返回的搜索命中次数的选项。默认为10
        sourceBuilder.size(pageSize);
        // 设置一个可选的超时时间,以控制允许搜索的时间。
        sourceBuilder.timeout(new TimeValue(300, TimeUnit.SECONDS));

        // 指定排序
        sourceBuilder.sort(new FieldSortBuilder("id").order(SortOrder.DESC));

        sourceBuilder.trackTotalHits(true);

        // 将请求体加入到请求中
        searchRequest.source(sourceBuilder);


        // 3、发送请求
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);


        // 4、处理响应
        // 搜索结果状态信息
        RestStatus status = searchResponse.status();

        // 处理搜索命中文档结果
        SearchHits hits = searchResponse.getHits();
        TotalHits totalHits = hits.getTotalHits();

        SearchHit[] searchHits = hits.getHits();
        List<Map<String, Object>> mapList = new ArrayList<>();
        for (SearchHit hit : searchHits) {
            // 取_source字段值
            mapList.add(hit.getSourceAsMap());
        }
        long total = hits.getTotalHits().value;
        final List<Map<String, Object>> result = getMaps(mapList);

        return result;
    }


    /**
     * 查询数据数量
     *
     * @param dto 条件
     * @return
     * @throws IOException
     */
    public long queryRecordCount(MessageDTO dto, String index) {

        // 构建判断条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 集合查询
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.rangeQuery("create_time").gte(dto.getFromTime()).lte(dto.getEndTime()));

        searchSourceBuilder.query(queryBuilder);
        CountRequest countRequest = new CountRequest().indices(index).source(searchSourceBuilder);
        try {
            CountResponse count = client.count(countRequest, RequestOptions.DEFAULT);
            return count.getCount();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }
}
posted @ 2021-03-11 17:04  aN。  阅读(193)  评论(0编辑  收藏  举报