• 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

  • 相关阅读:
    Git ---游离状态下的commit 分支切换与找回,commit之后无法找到历史记录
    mybatis异常invalid comparison: java.util.Date and java.lang.String
    Spring的注解@Qualifier
    Spring @Bean注解的使用
    Java的IO系统
    linkin大话面向对象--GC和jar包
    linkin大话面向对象--闭包和回调
    linkin大话面向对象--内部类
    linkin大话面向对象--枚举
    linkin大话面向对象--接口
  • 原文地址:https://www.cnblogs.com/it-deepinmind/p/14273024.html
Copyright © 2020-2023  润新知