知识点13--spring boot整合elasticsearch以及ES高亮

这篇具有很好参考价值的文章主要介绍了知识点13--spring boot整合elasticsearch以及ES高亮。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本章知识点沿用知识点12的项目,介绍如何使用spring boot整合ES,没有ES的去我主页各类型大数据集群搭建文档-->大数据原生集群本地测试环境搭建三中可以看到ES如何搭建

不管你有没有ES,最好是没有,因为一定要知道一点,一定要去官网查一下你当前用的spring boot data es的版本是不是和你自己ES服务器所匹配的,这一点简直是天坑,spring boot提供的es封装API对es的版本要求相当苛刻,对不上就用不了,很多人折在版本问题,奉劝大家一句,除非正式的项目开发上,团队会给你提供需要版本的jar,正式开发本身版本都是经过架构师仔细考虑并且版本方面问题都解决了,自己学习自己开发,你最好不要再jar上做坚持,直接去改你的es服务版本,也不要轻信网上说的改pom版本,会连带着出很多不必要的问题,官网如下https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#preface.requirements

比如,我在写这篇博文的时候spring boot沿用的前面知识点项目,spring boot版本是2.7.0,自动导入的spring boot data es版本是4.4.0,我的ES就要用7.17.3的,但是我本地先前用的es是6.6.2的,含泪重装。

同时不同的spring boot会受版本的影响,导致不同的操作ES方式会出现不同的问题,最常见的是操作方式过时,所以如果大家使用的版本和我的不一样,很可能导致无法操作

第一步:添加依赖

<!--es的配置 根据springboot的版本写入的-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

第二步:修改spring boot配置文件,添加如下的配置

spring.elasticsearch.uris=http://192.168.88.187:9200
spring.data.elasticsearch.repositories.enabled=true

第三步:对操作的实体Bean加注解

对你要在es操作的数据添加文档注释,indexName 是索引名称
@Document(indexName = "users")

对id字段,必须加上Id注解
@Id

指定字段的索引方式,index是否索引、store是否存储、字段的分词方式、搜索时关键字分词的方式、type指定该字段的值以什么样的数据类型来存储
@Field(index=true,store=true,analyzer="ik_max_word",searchAnalyzer="ik_max_word",type=FieldType.Text)

解释一下,需要分词的字段就是查询做条件的字段,比如我要操作的User类

package com.wy.scjg.bean;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;

@ToString
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@Document(indexName = "users")
public class User implements Serializable {
    /**
     * 主键ID
     */
    @TableId(value = "id", type = IdType.AUTO)
    @Id
    private Long id;

    /**
     * 姓名
     */
    @Field(index=true,store=true,analyzer="ik_max_word",searchAnalyzer="ik_max_word",type= FieldType.Text)
    private String name;

    /**
     * 年龄
     */
    private Integer age;

    /**
     * 邮箱
     */
    private String email;

    /**
     * 生日
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date brith;

    private Integer did;

    //表中不存在的字段
    @TableField(exist = false)
    private String dname;

    /**
     * 照片
     */
    private String zp;

    /**
     * 个人照,任意张
     */
    private String grz;
}

第四步:建立一个包,存放ES操作接口文件,并准备一个操作User数据的ES接口
spring.elasticsearch.uris,Spring boot,elasticsearch,spring boot,java
包和类名大家自定义

package com.wy.scjg.repository;

import com.wy.scjg.bean.User;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;

/**
 *  ElasticsearchRepository<数据Bean,一般都是String>
 * 继承后即可拥有基本的操作方法
 */
@Repository
public interface UserRepository extends ElasticsearchRepository<User,String> {

}

第五步:用测试类,向ES操作初始化User表的数据

package com.wy.scjg;

import com.wy.scjg.bean.User;
import com.wy.scjg.repository.UserRepository;
import com.wy.scjg.service.UserService;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class ESTest {

	//注入  UserRepository 对象操作ES
    @Autowired
    private UserRepository userRepository;

    @Autowired
    private UserService userService;

    @Test
    void imports(){
        List<User> list = userService.list();
        for(User user:list){
            userRepository.save(user);
        }
    }

}

运行代码后,查看es数据
spring.elasticsearch.uris,Spring boot,elasticsearch,spring boot,java
这个时候我们就可以将详情功能优化一下。

第六步:修改查询详情的Controller,将数据从ES查询

@Autowired
private UserRepository userRepository;

/**
 * 详情
 * @param request
 * @param id
 * @return
 */
@RequestMapping("/detail")
public String detail(HttpServletRequest request,Long id){
    Optional<User> byId = userRepository.findById(id.toString());

    //存储
    request.setAttribute("user",byId.get());
    return "/user_detail";
}

运行看效果
spring.elasticsearch.uris,Spring boot,elasticsearch,spring boot,java
注意:SpringBoot提供的超类ElasticsearchRepository,继承后就可以拥有基本的操作方法,如果要进行复杂点的查询,就需要我们自定义方法,自定义方法的方法名必须按照命名规则来进行命名

具体示例:更多的大家需要在网上自己找找了

//根据姓名查用户
List<User> findByName(String name);
	
//根据地址查询 findBy+字段名
List<User> findByAddress(String address);
	
//根据地址和姓名查询  findBy+多个字段名之间And分隔
List<User> findByAddressAndName(String address,String name);
	
//查询id小于某个值的数据  findBy+比大小的字段+LessThan
List<User> findByIdLessThan(int id);
	
//查询价格在多少-多少之间的   findBy+条件字段+Between
List<User> findByPriceBetween(double money,double money)

ES严格意义上并不是一个关系型数据库,它本身是一个空间换时间的索引库,以枚举分词查询条件的字段来弥补普通关系型数据库在like查询上的时间问题,通俗的将它提供了一种索引能力,所以它牺牲了其他普通关系型数据库哪像的性能,再加上国内使用ES通常都是IK分词器用来命中中文,因此ES一般只会被用来优化模糊匹配的查询需求,因为普通的数据库中只有like%能够触发索引。相较于其他的增删改来说ES虽然能做,但是它的效率没有普通关系数据库效率高,ES在存数据和修改数据都用的是save方法,不止要对某个字段分词,其他字段都会走一遍,效率很低。删除的话已有delete开头的方法,这些大家都可以自己试一试。

到此本篇知识点讲解结束,此外 本次整体讲解的spring boot项目已上传github


ES高亮

springboot封装程度很高,所以它把高亮功能封装在了ES客户端里面,因此想要使用springboot的ES高亮,除了导入data es之外还需要客户端

<!-- elasticsearch客户端-->
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>transport</artifactId>
</dependency>

配置文件无需多加,任然保持之前的就可以

spring.elasticsearch.uris=http://192.168.88.187:9200
spring.data.elasticsearch.repositories.enabled=true

随后在你的Service中就可以书写高亮代码,为了大家方便理解,首先给大家写一个单字段的高亮,高亮涉及很多不同包下的同名类,不要导错,防止大家分不清包,这里将整个UserService提供给大家

package com.wy.scjg.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wy.scjg.bean.User;
import com.wy.scjg.mapper.UserMapper;
import com.wy.scjg.service.UserService;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 实现类固定继承ServiceImpl类,目的是直接注入Dao层以及操作的实例类,并实现自定义的service接口
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

	//高亮注入
    @Resource
    private RestHighLevelClient highLevelClient;

	//这个和高亮无关,是前面的查询功能,不用看
    @Override
    public IPage<User> getUserList(Page page, User user) {
        return baseMapper.getUserList(page,user);
    }

	//高亮Service
    @Override
    public Map searchWC(Integer page, Integer limit, String kw) {
        //1、请求ES索引连接,构造参数是ES的索引库名
        SearchRequest request = new SearchRequest("users");
        //2、索源构造器,承载搜索条件
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //3、高亮构造器
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        //4、搜索结果
        SearchResponse searchResponse = null;
        //5、存放最终响应数据的Map 以及 具体数据的List
        Map searchResultMap = new HashMap<>();
        List list = new ArrayList<>();//保存结果

        //6、给索源构造器配置搜索条件:关键字模糊查询、分页、高亮
        if(kw != null && !"".equals(kw)){
            builder.query(QueryBuilders.matchQuery("name", kw));
        }
        //前端传递通常1开始,但是ES是0页开始的
        builder.from(page - 1);
        builder.size(limit);
        highlightBuilder.preTags("<font style='color: red'>");
        highlightBuilder.postTags("</font>");
        highlightBuilder.field("name");
        //同一字段中存在多个高亮值 设置都高亮
        highlightBuilder.requireFieldMatch(true);
        builder.highlighter(highlightBuilder);

        //7、给请求添加查询条件
        request.source(builder);
        try {
            //8、查询并处理结果
            searchResponse = highLevelClient.search(request, RequestOptions.DEFAULT);
            //保存分页信息:总条数total、总页数pages、结果当前页currPage
            long hitsTotalValue = searchResponse.getHits().getTotalHits().value;
            searchResultMap.put("total", hitsTotalValue);
            //页数(ceil后结果类型是double)
            searchResultMap.put("pages", (long) Math.ceil(hitsTotalValue / limit));
            searchResultMap.put("currPage", page);

            //解析高亮数据
            SearchHits hits = searchResponse.getHits();
            for(SearchHit hit: hits){
                //原始数据,不包含高亮的数据
                Map<String, Object> sourceMap = hit.getSourceAsMap();
                //高亮数据
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField highlightTitle = highlightFields.get("name");
                //替换
                if(highlightTitle != null){
                    Text[] fragments = highlightTitle.getFragments();
                    if(fragments != null && fragments.length > 0){
                        sourceMap.replace("name", fragments[0].toString());
                    }
                }
                list.add(sourceMap);//循环将数据添加入列表
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        searchResultMap.put("dataList",list);

        return searchResultMap;
    }

}

之后在Controller准备

//根据关键字搜索 参数:page当前页、limit每页多少数据、kw高亮值,produces可以没有它的目的只是设置返回值的类型和编码集
@ResponseBody
@RequestMapping(value = "/searchWC", produces = "application/json; charset=utf-8")
public Map searchWC(Integer page, Integer limit, String kw){
    return userService.searchWC(page, limit, kw);
}

随后启动项目,前端查看结果
spring.elasticsearch.uris,Spring boot,elasticsearch,spring boot,java

可是上面这种高亮只是用在简单的高亮场景,但是我们实际使用时往往有着不同的需求,而且不大可能只有一个字段高亮,同时大家编程完上面的简单高亮后会发现 RestHighLevelClient 是一个过时不推荐使用的高亮工具类,因此实际开发中更加实用的高亮如下

package com.wy.scjg.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wy.scjg.bean.User;
import com.wy.scjg.mapper.UserMapper;
import com.wy.scjg.service.UserService;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
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.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 实现类固定继承ServiceImpl类,目的是直接注入Dao层以及操作的实例类,并实现自定义的service接口
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

	//高亮功能被整合在了此对象中
    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Override
    public IPage<User> getUserList(Page page, User user) {
        return baseMapper.getUserList(page,user);
    }

	//高亮
    @Override
    public Map searchWC(Integer page, Integer limit, String kw) {
        //1 准备一个ES提供的索引查询构造器  这个类可以和SortBuilders配合指定更加丰富的排序
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
        //2 封装分页对象
        Pageable pageable = PageRequest.of(page - 1, limit);
        searchQueryBuilder.withPageable(pageable);
        //3 封装查询条件 这个类和QueryBuilders配合可以搭配出丰富的查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (kw!=null && !"".equals(kw)){
            boolQueryBuilder.should(QueryBuilders.multiMatchQuery(kw, "name", "age"));
        }
        //配置好后应当提交给索引查询构造器,但本案例中是为了让大家知道有这个东西就行
        //至于用这两个字段过滤的功能在高亮运行时就已经实现了,同时也体现出一个事情:
        //   高亮和ES的查询构造器不是必须共生,而是高亮是索引查询的一个功能
        //searchQueryBuilder.withFilter(boolQueryBuilder);

        //4 自定义一个ES的排序 指定SUM是匹配度总和 以及最小匹配度值
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(boolQueryBuilder).scoreMode(FunctionScoreQuery.ScoreMode.SUM).setMinScore(1);
        searchQueryBuilder.withQuery(functionScoreQueryBuilder);

        //5 封装配置的高亮字段
        HighlightBuilder.Field[] fields = new HighlightBuilder.Field[2];
        fields[0] = new HighlightBuilder.Field("name").preTags("<font color='red'>").postTags("</font>").requireFieldMatch(false);
        fields[1] = new HighlightBuilder.Field("age").preTags("<font color='red'>").postTags("</font>").requireFieldMatch(false);
        searchQueryBuilder.withHighlightFields(fields);

        //6 用索引条件构造器生成一个查询条件对象
        NativeSearchQuery searchQuery = searchQueryBuilder.build();

        //7 查询
        SearchHits<User> searchHits = elasticsearchRestTemplate.search(searchQuery, User.class);

        if(searchHits.getTotalHits()<=0){
            return null;
        }

        //8 处理结果
        List list = new ArrayList();
        Map searchResultMap = new HashMap<>();

        List<SearchHit<User>> result = searchHits.getSearchHits();
        result.forEach(r -> {
            //原始数据
            User user = r.getContent();
            //高亮数据
            Map<String, List<String>> highlightFields = r.getHighlightFields();
            //处理name字段
            List<String> names = highlightFields.get("name");
            if (names != null && names.size() > 0) {
                StringBuffer buffer = new StringBuffer();
                for (String s : names) {
                    buffer.append(s);
                }
                user.setName(buffer.toString());
            }
            //处理age字段
            List<String> ages = highlightFields.get("age");
            if (ages != null && ages.size() > 0) {
                StringBuffer buffer = new StringBuffer();
                //这里这个循环,大家不要有什么疑惑,就是springboot框架返回了一个集合,其实里面就一个高亮后的数据
                for (String s : ages) {
                    buffer.append(s);
                }
                //实体bean需要新增一个字段,因为原来的是数字,高亮后是个字符串
                user.setAge_HT(buffer.toString());
            }

            //装入数据集
            list.add(user);
        });
        searchResultMap.put("dataList",list);
        //保存分页信息:总条数total、总页数pages、结果当前页currPage
        long hitsTotalValue = searchHits.getTotalHits();
        searchResultMap.put("total", hitsTotalValue);
        searchResultMap.put("pages", (long) Math.ceil(hitsTotalValue / limit));
        searchResultMap.put("currPage", page);

        return searchResultMap;
    }

}

此时其他内容不变,访问接口拿数据,即可发现name、age字段已经高亮成功
spring.elasticsearch.uris,Spring boot,elasticsearch,spring boot,java
spring.elasticsearch.uris,Spring boot,elasticsearch,spring boot,java


最后我要说一个雷点,我们国内用的IK分词器是国人写的,它和ES在正常使用的时候,如果中文分词的字段中,某一条恰好存的没有中文,IK分词器是无法分词的,因此该值高亮时就不会有高亮效果,如我上图的高亮数据中,name字段值是高亮测试2,但如果存的是其他的,比方说2222zzz2这种没有一个中文的值,那么这条数据查询后,即使我的高亮关键字是2,name字段也是没有高亮效果的文章来源地址https://www.toymoban.com/news/detail-662723.html

到了这里,关于知识点13--spring boot整合elasticsearch以及ES高亮的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Elasticsearch集群搭建与相关知识点整理

    前言:大家好,我是小威,24届毕业生,在一家满意的公司实习。本篇文章参考网上的课程,介绍Elasticsearch集群的搭建,以及Elasticsearch集群相关知识点整理。 如果文章有什么需要改进的地方还请大佬不吝赐教 👏👏。 小威在此先感谢各位大佬啦~~🤞🤞 🏠个人主页:小威要

    2023年04月22日
    浏览(25)
  • C#知识点-13(进程、多线程、使用Socket实现服务器与客户端通信)

    进程 定义:每一个正在运行的应用程序,都是一个进程  进程不等于正在运行的应用程序。而是为应用程序的运行构建一个运行环境 多线程 这段代码在执行完成之前,程序会被卡死(不能操作程序,包括关闭窗口)。因为我们程序在做一些耗时操作的时候,如果主线程去执

    2024年02月22日
    浏览(42)
  • 飞天使-docker知识点13-查找docker run 启动时候命令与升级docker版本

    如果很久了,不记得之前docker run 命令 然后在升级docker 版本

    2024年01月21日
    浏览(29)
  • Spring入门系列:浅析知识点

    讲解Spring之前,我们首先梳理下Spring有哪些知识点可以进行入手源码分析,比如: Spring IOC依赖注入 Spring AOP切面编程 Spring Bean的声明周期底层原理 Spring 初始化底层原理 Spring Transaction事务底层原理 通过这些知识点,后续我们慢慢在深入Spring的使用及原理剖析,为了更好地理

    2023年04月10日
    浏览(24)
  • Spring IOC相关知识点

    :核心思想IOCAOP、作用(解耦,简化),简单描述框架组成 Spring框架是一款轻量级的开发框架,核心思想是IOC(控制反转)和AOP(面向切面编程),尾java应用程序开发提供组建管理服,用于组件之间的解耦,以及简化第三方javaEE中间件技术的应用(JMS,任务调度,缓

    2024年02月13日
    浏览(24)
  • Spring AOP知识点详解

    Spring AOP是 Spring最核心的能力,那到底什么是AOP呢,今天了不起带大家了解一下。 AOP(Aspect Oriented Programming): 面向切面编程 ,是OOP(面向对象编程)的一个延续,其和OOP一样,也是一种编程思想,不过AOP是一种横向开发模式。 OOP ,面向对象,允许开发者定义纵向的关系,但并适

    2024年02月16日
    浏览(37)
  • Spring MVC相关知识点

    首先,MVC模型是模型,视图,控制器的简写,其思想核心是通过将请求处理控制,业务逻辑,数据封装,数据显示等流程节点分离的思想来组织代码。 所以,MVC是一种设计模式,而SpringMVC是一款基于MVC设计模式思想实现的MVC框架,属于Spring技术栈的一部分。SpringMVC可以帮我们

    2024年02月13日
    浏览(49)
  • 206、SpringBoot 整合 RabbitMQ 的自动配置类 和 对应的属性处理类 的知识点

    ▲ Spring Boot 提供了一个 spring-boot-starter-amqp 的Starter来支持RabbitMQ,只要添加该Starter,它就会添加 spring-rabbit 依赖库(它有传递依赖了amqp-client.jar) ▲ 只要类加载路径下包含了 spring-rabbit 依赖库, Spring Boot 会自动配置 CachingConnectionFactory (CachingConnectionFactory:带缓存的连接工

    2024年02月07日
    浏览(34)
  • JavaEE学习的第三部分(重点在第四个知识点,SSM框架整合)

    Spring MVC的拦截器(Interceptor) 类似于Java Servlet的过滤器(Filter)。前面学习Java Web学过一点过滤器。 拦截器的作用 是拦截用户的请求,并做相应的处理 。例如在JSP提交表单数据给Controller,先是提交给拦截器的类。比如权限验证,是否登录,然后再交给Controller处理。 作法:需

    2024年02月12日
    浏览(23)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包