14.Spring2.7.x 整合 Elasticsearch7.17

发布时间:2023年12月18日

Elasticsearch:一个分布式的、Restful 风格的搜索引擎;支持对各种类型的数据的索引;搜索速度快,可以提供实时的搜索服务;便于水平扩展,每秒可以处理 PB 级海量数据


目录

1.Spring 整合 Elasticsearch

1.1 实体类与 ES 建立关系?

1.2 实现接口

1.3 测试类


1.Spring 整合 Elasticsearch

  • 引入依赖:spring-boot-starter-data-elasticsearch
  • 配置 Elasticsearch:cluster-name、cluster-notes
  • Spring Data Elaticsearch:ElaticsearchRestTemplate、ElaticsearchRepository

依赖:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

配置:

# ElasticsearchProperties
spring.data.elasticsearch.cluster-name=xiaowen
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300

解决冲突:Elasticsearch 底层是基于 Netty;Redis 底层也是基于 Netty,两者启动 natty 的时候由冲突

在?Application 类(核心入口配置类,最先被加载的)添加:

  • 添加 @PostConstruct 注解:管理 Bean 的初始化方法,由注解所修饰的方法会在构造器调用完成之后被执行
  • 添加方法:解决 netty 启动冲突问题(Netty4Utils.setAvailableProcessors())
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import javax.annotation.PostConstruct;

@SpringBootApplication
public class DemoApplication {

	//添加 @PostConstruct 注解:管理 Bean 的初始化方法,由注解所修饰的方法会在构造器调用完成之后被执行
	@PostConstruct
	public void init() {
		// 解决netty启动冲突问题
		// see Netty4Utils.setAvailableProcessors()
		System.setProperty("es.set.netty.runtime.available.processors", "false");
	}

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

1.1 实体类与 ES 建立关系?

编写与需求相关的代码:把数据库中存的帖子再存到 ES 服务器中,在 ES 服务器中搜索帖子

帖子表存到 ES 中的索引,每个字段对应的类型、怎么搜索都是要进行配置,但是配置不需要写 xml 文件,通过注解实现,注解写在实体类中(针对帖子操作,写在 discussPost 类中)

  • 在此类中添加注解 @Document(indexName = " ", type = " ", shards =? , replicas =? ):Spring 整合 ES,底层访问 ES 会自动将实体数据和 ES 服务器的索引映射:indexName:索引;type:类型;shards:分片;replicas:副本(ElasticSearch7.x已经不推荐使用type了,并且将在8.0彻底移除)
  • 主键通常会加 @Id、普通字段添加 @Field(type = FiledType. )
  • 重点是 title 和 context:搜索帖子主要搜索标题和内容(类型为 type = FieldType.Text),存储的时候尽可能多的拆分关键词,增加搜索范围(使用 analyzer = "ik_max_word");搜索的时候,需要尽可能满足需求,不需要拆分太多次(使用searchAnalyzer = "ik_smart")
/**
 * 与 ES 中的索引对应
 */

//Spring 整合 ES,底层访问 ES 会自动将实体数据和 ES 服务器的索引映射
@Document(indexName = "discusspost")
public class DiscussPost {

    @Id
    private int id;

    @Field(type = FieldType.Integer)
    private int userId;

    //搜索帖子主要搜索标题和内容
    //存储的时候尽可能多的拆分关键词,增加搜索范围(使用 analyzer = "ik_max_word")
    // 搜索的时候,需要尽可能满足需求,不需要拆分太多次(使用searchAnalyzer = "ik_smart")
    @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
    private String title;

    @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
    private String content;

    @Field(type = FieldType.Integer)
    private int type;

    @Field(type = FieldType.Integer)
    private int status;//帖子状态

    @Field(type = FieldType.Date)
    private Date createTime;//创建时间

    @Field(type = FieldType.Integer)
    private int commentCount;//评论数量

    @Field(type = FieldType.Double)
    private double score;//分数
}

上述这个实体类就和es之间的数据建立了联系。

1.2 实现接口

在dao下新建elasticsearch,实现 DiscussPostRepository 接口,继承时声明泛型,主键类型

  • 添加注解 @Repository:这个接口是数据访问层代码,ES可以看作特殊的数据库(@Mapper 是 MyBatis 专用注解)
package com.example.demo.dao.elasticsearch;

import com.example.demo.entity.DiscussPost;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface DiscussPostRepository extends ElasticsearchRepository<DiscussPost, Integer> {

}

1.3 测试类

package com.example.demo;

import com.example.demo.dao.DiscussPostMapper;
import com.example.demo.dao.elasticsearch.DiscussPostRepository;
import com.example.demo.entity.DiscussPost;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = DemoApplication.class)
public class ElasticsearchTests {

    @Autowired
    private DiscussPostMapper discussMapper;

    @Autowired
    private DiscussPostRepository discussRepository;

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    //查询
    @Test
    public void testInsert() {
        discussRepository.save(discussMapper.selectDiscussPostById(241));
        discussRepository.save(discussMapper.selectDiscussPostById(242));
        discussRepository.save(discussMapper.selectDiscussPostById(243));
    }

    //插入多条数据
    @Test
    public void testInsertList() {
        discussRepository.saveAll(discussMapper.selectDiscussPosts(101, 0, 100));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(102, 0, 100));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(103, 0, 100));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(111, 0, 100));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(112, 0, 100));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(131, 0, 100));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(132, 0, 100));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(133, 0, 100));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(134, 0, 100));
    }

    //修改
    @Test
    public void testUpdate() {
        DiscussPost post = discussMapper.selectDiscussPostById(231);
        post.setContent("我是新人,使劲灌水.");
        discussRepository.save(post);
    }

    //删除
    @Test
    public void testDelete() {
        // discussRepository.deleteById(231);
        discussRepository.deleteAll();
    }

    //搜索
    @Test
    public void testSearchByRepository() {
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.multiMatchQuery("互联网寒冬", "title", "content"))
                .withSorts(SortBuilders.fieldSort("type").order(SortOrder.DESC),
                        SortBuilders.fieldSort("score").order(SortOrder.DESC),
                        SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
                .withPageable(PageRequest.of(0, 10))
                .withHighlightFields( //高亮显示
                        new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),
                        new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
                ).build();


        // elasticTemplate.queryForPage(searchQuery, class, SearchResultMapper)
        // 底层获取得到了高亮显示的值, 但是没有返回.

        Iterable<DiscussPost> page = discussRepository.findAll();

        for (DiscussPost post : page) {
            System.out.println(post);
        }
    }

    // 查询数据并将关键字高亮显示
    @Test
    public void testSearchByTemplate() {
        // withQuery()用于构造搜索条件
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.multiMatchQuery("互联网寒冬", "title", "content"))
                .withSorts(SortBuilders.fieldSort("type").order(SortOrder.DESC),
                        SortBuilders.fieldSort("score").order(SortOrder.DESC),
                        SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
                .withPageable(PageRequest.of(0, 10))
                .withHighlightFields(
                        new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),
                        new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
                ).build();
        //查询
        SearchHits<DiscussPost> search = elasticsearchRestTemplate.search(searchQuery, DiscussPost.class);
        long count = elasticsearchRestTemplate.count(searchQuery, DiscussPost.class);
        System.out.println("共查询到: "+ count +"条数据");

        //得到查询返回的内容
        List<SearchHit<DiscussPost>> searchHits = search.getSearchHits();
        // 设置一个最后需要返回的实体类集合
        List<DiscussPost> discussPosts = new ArrayList<>();
        // 遍历返回的内容,进行处理
        for (SearchHit<DiscussPost> hit : searchHits) {
            // 获取高亮的内容
            Map<String, List<String>> highlightFields = hit.getHighlightFields();
            // 将高亮的内容添加到content中(匹配到的如果是多段,就将第一段高亮显示)
            // 没有匹配到关键字就显示原来的title和content
            hit.getContent().setTitle(highlightFields.get("title")==null ? 
                    hit.getContent().getTitle():highlightFields.get("title").get(0));
            hit.getContent().setContent(highlightFields.get("content")==null ? 
                    hit.getContent().getContent():highlightFields.get("content").get(0));
            // 放到实体类中
            discussPosts.add(hit.getContent());
        }
        for (DiscussPost post : discussPosts) {
            System.out.println(post);
        }
    }
}
文章来源:https://blog.csdn.net/m0_72161237/article/details/134981044
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。