Spring Boot 2.3.2 集成elasticsearch 7.6.2實戰


前言

此前雖然有嘗試過集成elasticsearch,不過技術棧並非spring boot。本次嘗試在springboot項目中集成elasticsearch,不過由於spring boot、es、rest的版本問題,折騰了好久,寫這篇文章的目的一是分享一下,也是為了紀念當時的摸爬滾打。
注:集成過程中有參考部分網友的文章,在此表示感謝。

版本和環境

JDK: 1.8
spring boot: 2.3.2
elasticsearch: 7.6.2
IDE: 宇宙第一的IDEA

Elasticsearch、IK分詞器的安裝和配置

我安裝的elasticsearch版本為7.6.2,網上教程很多,這里不再贅述
分詞器:IK
測試:

http://127.0.0.1:9200/alarm_reason_index/_search

 

{
    "query": {
        "match": {
            "planTitle": "那時候"
        }
    },
    "sort": [
        "_score",
        {
            "timestamp": {
                "order": "desc",
                "unmapped_type": "long"
            }
        }
    ],
    "from": 0,
    "size": 4
}

結果

{
    "took": 5,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 5,
            "relation": "eq"
        },
        "max_score": null,
        "hits": [
            {
                "_index": "alarm_reason_index",
                "_type": "_doc",
                "_id": "1315844356900499451",
                "_score": 4.377079,
                "_source": {
                    "_class": "com.knowswift.stnl.bean.plan.po.EmergencyPlanES",
                    "emergencyPlanId": "1315844356900499451",
                    "planCode": "37090476851",
                    "planTitle": "那時的卡卡是",
                    "planLevel": "一級",
                    "createTime": "2020-10-24 10:20:18",
                    "timestamp": 1603506018
                },
                "sort": [
                    4.377079,
                    1603506018
                ]
            }
            ...
        ]
    }
}

集成過程

1. pom文件

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

其中

  1. spring-boot-starter-data-elasticsearch 需要2.3以上;
  2. org.elasticsearch和org.elasticsearch.client的版本需要和elasticsearch一致。

2. application.yml

spring:
  elasticsearch:
    rest:
      uris: 127.0.0.1:9200

3. 實體類

@Data
@Document(indexName = "pl_index")
public class PlES {
    @Id
    private String id;
    @Field(analyzer = "ik_smart")
    private String code;
    @Field(analyzer = "ik_max_word")
    private String title;
    @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "uuuu-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    @Field
    private Long timestamp;
    
    public void setCreateTime(LocalDateTime createTime) {
        this.createTime = createTime;
        timestamp = createTime.toEpochSecond(ZoneOffset.of("+8"));
    }
}

4.ElasticRepository

用於調用ElasticsearchRepository的方法

public interface PlElasticRepository extends ElasticsearchRepository<PlES, String> {
}

5.業務層接口IElasticService

基礎的接口方法

public interface IElasticService<T, ID> {


    boolean createIndex(Class<T> tClass);

    boolean deleteIndex(String index);

    void save(T entity);

    void saveBatch(List<T> list);

    List<T> findAll();

    Page<T> query(String key);

    void deleteById(ID id);

    @Resource
    ElasticsearchRepository getRepository();
}

6.業務層實現類

public class ElasticServiceImpl<M extends ElasticsearchRepository<T, ID>, T, ID> implements IElasticService<T, ID> {

    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Resource
    RestHighLevelClient restHighLevelClient;


    @Resource
    private M getRepository;

    @SneakyThrows
    @Override
    public boolean createIndex(Class<T> clazz) {
        Document document = clazz.getAnnotation(Document.class);
        GetIndexRequest request = new GetIndexRequest(document.indexName());
        boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
        if (exists){
            return true;
        }
        CreateIndexRequest createIndexRequest = new CreateIndexRequest(document.indexName());
        restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
        return true;
    }

    @Override
    public boolean deleteIndex(String index) {
        return elasticsearchRestTemplate.deleteIndex(index);
    }

    @Override
    public void save(T entity) {
        T save = getRepository.save(entity);
    }

    @Override
    public void saveBatch(List<T> list) {
        Iterable<T> ts = getRepository.saveAll(list);
    }

    @Override
    public List<T> findAll() {
//        FieldSortBuilder timestamp = SortBuilders.fieldSort("timestamp").order(SortOrder.DESC);
        return (List<T>) getRepository.findAll(Sort.by("timestamp").descending());
    }

    @Override
    public Page<T> query(String key) {
        return null;
    }

    @Override
    public M getRepository() {
        return this.getRepository;
    }

    public void deleteById(ID id) {
        getRepository.deleteById(id);
    }
}

6. 業務實現方法

@Service
public class PlElasticService extends ElasticServiceImpl<PlElasticRepository, PlES, String> {
    
    @SneakyThrows
    public SearchHits list(String key, int pageNo, int pageSize) {
        SearchRequest request = new SearchRequest();
        SearchSourceBuilder builder = new SearchSourceBuilder();
        if (StringUtils.isNotBlank(key)) {
            builder.query(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("title", key)))
                    .sort(SortBuilders.scoreSort());
        } else {
            builder.query(QueryBuilders.boolQuery().must(QueryBuilders.matchAllQuery())).sort(SortBuilders.scoreSort());;
        }
        // 排序
        FieldSortBuilder order = SortBuilders.fieldSort("timestamp").unmappedType("long").order(SortOrder.DESC);
        // 分頁 應注意,pageNo在此處並非是頁碼,而是當前頁的第一行,也就是SQL語句的 offset
        builder.from(pageNo).size(pageSize).sort(order);
        builder.timeout(new TimeValue(2, TimeUnit.SECONDS));
        // 高亮
        builder.highlighter(new HighlightBuilder().field("title").preTags("<span style=\"color:red\">").postTags("</span>"));
        request.source(builder);
        SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
//        long value = hits.getTotalHits().value;
        SearchHits searchHits = response.getHits();
        SearchHit[] hits1 = searchHits.getHits();
        for (SearchHit documentFields : hits1) {
            Map<String, HighlightField> map = documentFields.getHighlightFields();
            HighlightField title = map.get("title");
            if (title == null) {
                continue;
            }
            Text[] fragments = title.fragments();
            if (fragments.length == 0) {
                continue;
            }
            // 將es結果集轉成實體類
            String sourceAsString = documentFields.getSourceAsString();
            PlES plES = JSONObject.parseObject(sourceAsString, PlES.class);
            // 高亮替換
            plES.setTitle(fragments[0].toString());
        }
        return searchHits;
    }
}

至此,一個簡單的分頁查詢方法就已經完成。

存在問題

所使用的elasticsearch客戶端是 restHighLevelClient,在長時間不請求之后,再次請求會出現異常:遠程主機強迫關閉了一個現有的連接,再次請求又正常。
暫時了解到的,這可能是elasticsearch存在——會殺死長時間空閑連接——的問題,或者是我不知道如何解決的問題。
目前我使用的解決方法是添加一個定時器,持續請求,保證連接是活躍的

@Scheduled(cron = "0 0/2 * * * *")
    public void keepESAlive() {
        try {
            restHighLevelClient.info(RequestOptions.DEFAULT);
        } catch (IOException ignored) {

        }
    }

如果有更好的解決辦法,請在評論分享。

END

轉載於:https://blog.csdn.net/Hades_iphone/article/details/109459191


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM