springboot整合Elasticsearch

前言

在Es7.15版本之后,es官方将它的高级客户端RestHighLevelClient标记为弃用状态。同时推出了全新的java API客户端Elasticsearch Java API Client,该客户端也将在Elasticsearch8.0及以后版本中成为官方推荐使用的客户端。

1、项目依赖

我们使用Elasticsearch client 的方式有两种,一种是我们可以引入springboot自带整合的starter 如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
	<version>${version}</version>
</dependency>

另一种,单独引入了elasticsearch8.x依赖

 <!--   elasticsearch依赖     -->
       <dependency>
           <groupId>co.elastic.clients</groupId>
           <artifactId>elasticsearch-java</artifactId>
           <version>8.1.0</version>
       </dependency>

2、springboot的集成实现

2.1、ElasticSearchConfig配置实现

如果我们使用的是springboot的start包,就可以在nacos增加下列配置:

spring:
  elasticsearch:
    uris: 172.16.3.52:9200

如果我们使用得是单独引入了elasticsearch8.x依赖

我们就在配置类中进行编写:\

@Configuration
public class ElasticSearchConfig {
@Value("${es.hosts}")
private String hosts;
@Value("${es.name:elastic}")
private String name;
@Value("${es.password:aimind}")
private String password;

@Bean
public ElasticsearchClient docqaElasticsearchClient() {
    final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
    credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(name, password));
    List<HttpHost> httpHosts = Lists.newArrayList();
    String[] split = hosts.split(",");
    for (int i = 0; i < split.length; i++) {
        httpHosts.add(HttpHost.create(split[i]));
    }
    HttpHost[] httpHosts1 = httpHosts.toArray(new HttpHost[0]);
    RestClient client = RestClient
            .builder(httpHosts1)
            .setHttpClientConfigCallback(httpAsyncClientBuilder ->
                    httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider).setKeepAliveStrategy((response, context) -> 180 * 1000))
            .build();

    ElasticsearchTransport transport = new RestClientTransport(client, new JacksonJsonpMapper());
    return new ElasticsearchClient(transport);
}
}

2.2、定义机器人知识得数据存储结构实体类

package com.rainbowred.commons.entities.robot;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.Date;

/**
 * @author dxl
 * @since 2024/2/19
 */

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
@TableName("robot_knowledge")
public class RobotKnowledge {

    /**
     * 主键
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 公司ID
     * @mbggenerated
     */
    private Long companyId;

    /**
     * 知识所属分类
     * @mbggenerated
     */
    private Long categoryId;

    /**
     * 知识所属分组
     * @mbggenerated
     */
    private Long groupId;

    /**
     * 关联类别,1是相关知识,2是菜单问题(菜单问题中有引导语)
     * @mbggenerated
     */
    private Long type;

    /**
     * 知识库问题
     * @mbggenerated
     */
    private String question;

    /**
     * 知识库答案
     *
     * @mbggenerated
     */
    private String answer;

    /**
     * 知识库答案-微信版
     * @mbggenerated
     */
    private String answerWechat;

    /**
     * 知识库是否启用 0:不启用 1:启用
     * @mbggenerated
     */
    private Byte status;

    /**
     * 相关知识id,用_分隔
     * @mbggenerated
     */
    private String relateKnowledgeIds;

    /**
     * 知识是否被索引 0:未被索引 1:被索引
     * @mbggenerated
     */
    private Byte indexed;

    /**
     *
     * 知识库问题hash值
     * @mbggenerated
     */
    private Integer hashcode;

    /**
     * 总命中次数
     * @mbggenerated
     */
    private Long totalHit;

    /**
     * 通过原始知识命中次数
     * @mbggenerated
     */
    private Long originHit;

    /**
     * 相关问题命中次数
     *
     * @mbggenerated
     */
    private Long relateQuesHit;

    /**
     * 相关知识命中次数
     *
     * @mbggenerated
     */
    private Long relateKnowledgeHit;

    /**
     * 点赞次数
     *
     * @mbggenerated
     */
    private Long agreeCount;

    /**
     * 踩次数
     *
     * @mbggenerated
     */
    private Long disagreeCount;

    /**
     * 知识库解决率
     *
     * @mbggenerated
     */
    private Float solveRate;

    /**
     * 截至日期,为空表示永久生效
     *
     * @mbggenerated
     */
    private Date expireDate;

    /**
     * 曝光次数
     *
     * @mbggenerated
     */
    private Long exposureNumber;

    /**
     * 点击次数
     *
     * @mbggenerated
     */
    private Long clickNumber;

    /**
     * 创建时间
     *
     * @mbggenerated
     */
    private Date createDatetime;

    /**
     * 创建知识的客服ID
     * @mbggenerated
     */
    private Long createUserid;

    /**
     * 修改时间
     *
     * @mbggenerated
     */
    private Date modifyDatetime;

    /**
     * 修改知识的客服ID
     *
     * @mbggenerated
     */
    private Long modifyUserid;

    /**
     * 该知识的最后命中时间
     *
     * @mbggenerated
     */
    private Date lastHitDatetime;

}

2.3、定义机器人知识的索引结构

package com.rainbowred.commons.elasticsearch.entity;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.io.Serializable;
import java.util.Date;

/**
 * @author dxl
 * @since 2024/2/19
 */

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@JsonIgnoreProperties(ignoreUnknown = true)
@Document(indexName = "robotknowledge_index", createIndex = false)
public class RobotKnowledgeIndex implements Serializable {

    private static final long serialVersionUID=1L;

    /**
     * 主键
     */
    @Field(type= FieldType.Integer)
    private Long id;

    /**
     * 公司ID
     * @mbggenerated
     */
    @Field(type= FieldType.Keyword)
    private Long companyId;

    /**
     * 知识所属分类
     * @mbggenerated
     */
    @Field(type= FieldType.Long)
    private Long categoryId;

    /**
     * 知识所属分组
     * @mbggenerated
     */
    @Field(type= FieldType.Long)
    private Long groupId;

    /**
     * 关联类别,1是相关知识,2是菜单问题(菜单问题中有引导语)
     * @mbggenerated
     */
    @Field(type= FieldType.Long)
    private Long type;

    /**
     * 知识库问题
     * @mbggenerated
     */
    @Field(type= FieldType.Text, analyzer = "ik_max_word")
    private String question;

    /**
     * 知识库答案
     *
     * @mbggenerated
     */
    @Field(type= FieldType.Keyword)
    private String answer;

    /**
     * 知识库答案-微信版
     * @mbggenerated
     */
    @Field(type= FieldType.Keyword)
    private String answerWechat;

    /**
     * 知识库是否启用 0:不启用 1:启用
     * @mbggenerated
     */
    @Field(type= FieldType.Byte)
    private Byte status;

    /**
     * 相关知识id,用_分隔
     * @mbggenerated
     */
    @Field(type= FieldType.Keyword)
    private String relateKnowledgeIds;

    /**
     * 知识是否被索引 0:未被索引 1:被索引
     * @mbggenerated
     */
    @Field(type= FieldType.Byte)
    private Byte indexed;

    /**
     *
     * 知识库问题hash值
     * @mbggenerated
     */
    @Field(type= FieldType.Integer)
    private Integer hashcode;

    /**
     * 总命中次数
     * @mbggenerated
     */
    @Field(type= FieldType.Long)
    private Long totalHit;

    /**
     * 通过原始知识命中次数
     * @mbggenerated
     */
    @Field(type= FieldType.Long)
    private Long originHit;

    /**
     * 相关问题命中次数
     *
     * @mbggenerated
     */
    @Field(type= FieldType.Long)
    private Long relateQuesHit;

    /**
     * 相关知识命中次数
     *
     * @mbggenerated
     */
    @Field(type= FieldType.Long)
    private Long relateKnowledgeHit;

    /**
     * 点赞次数
     *
     * @mbggenerated
     */
    @Field(type= FieldType.Long)
    private Long agreeCount;

    /**
     * 踩次数
     *
     * @mbggenerated
     */
    @Field(type= FieldType.Long)
    private Long disagreeCount;

    /**
     * 知识库解决率
     *
     * @mbggenerated
     */
    @Field(type= FieldType.Float)
    private Float solveRate;

    /**
     * 截至日期,为空表示永久生效
     *
     * @mbggenerated
     */
    @Field(type= FieldType.Date,format = DateFormat.date_hour_minute_second)
    private Date expireDate;

    /**
     * 曝光次数
     *
     * @mbggenerated
     */
    @Field(type= FieldType.Long)
    private Long exposureNumber;

    /**
     * 点击次数
     *
     * @mbggenerated
     */
    @Field(type= FieldType.Long)
    private Long clickNumber;

    /**
     * 创建时间
     *
     * @mbggenerated
     */
    @Field(type= FieldType.Date,format = DateFormat.date_hour_minute_second)
    private Date createDatetime;

    /**
     * 创建知识的客服ID
     * @mbggenerated
     */
    @Field(type= FieldType.Long)
    private Long createUserid;

    /**
     * 修改时间
     *
     * @mbggenerated
     */
    @Field(type= FieldType.Date,format = DateFormat.date_hour_minute_second)
    private Date modifyDatetime;

    /**
     * 修改知识的客服ID
     *
     * @mbggenerated
     */
    @Field(type= FieldType.Long)
    private Long modifyUserid;

    /**
     * 该知识的最后命中时间
     *
     * @mbggenerated
     */
    @Field(type= FieldType.Date,format = DateFormat.date_hour_minute_second)
    private Date lastHitDatetime;
}

2.4、编写机器人知识的IndexRepository

package com.rainbowred.commons.elasticsearch.repository;

import com.rainbowred.commons.elasticsearch.entity.RobotKnowledgeIndex;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

/**
 * @author dxl
 * @since 2024/2/19
 */
public interface RobotKnowledgeIndexRepository extends ElasticsearchRepository<RobotKnowledgeIndex,Long> {
}

注意:若报错在spring容器中找不到这个bean,则需要在启动类上加上以下注解:

package com.rainbowred.robot.service;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;


/**
 * @author dxl
 * @since 2024/1/30
 */

@SpringBootApplication(scanBasePackages = {"com.rainbowred.robot.service","com.rainbowred"})
@ComponentScan(basePackages = "com.rainbowred")
@EnableElasticsearchRepositories(basePackages = "com.rainbowred.commons.elasticsearch.repository")
public class RobotServiceApplication {

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

2.5、定义操作机器人索引的service

package com.rainbowred.commons.elasticsearch.service;

import com.rainbowred.commons.elasticsearch.entity.RobotKnowledgeIndex;
import com.rainbowred.commons.entities.robot.RobotKnowledge;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * @author dxl
 * @since 2024/2/19
 */
@Slf4j
@Service
public class RobotKnowledgeIndexService {
    @Resource
    private ElasticsearchTemplate template ;

    @Resource
    RobotKnowledgeService robotKnowledgeService;

    public void initIndex() {
        IndexOperations indexOps = template.indexOps(RobotKnowledgeIndex.class);
        if (indexOps.exists()) {
            boolean delFlag = indexOps.delete();
            log.info("robotKnowledge_index exists, delete: {}", delFlag);
        }

        // 现在首先创建索引,然后创建映射
        indexOps.create();
        indexOps.createMapping(RobotKnowledgeIndex.class);

        List<RobotKnowledge> robotKnowledgeList = robotKnowledgeService.queryAllRobotKnowledge();
        if (!robotKnowledgeList.isEmpty()) {
            List<RobotKnowledgeIndex> robotKnowledges = new ArrayList<>();
            // 应在这里遍历robotKnowledgeList,而不是robotKnowledges
            for (RobotKnowledge robotKnowledge : robotKnowledgeList) {
                RobotKnowledgeIndex robotKnowledgeIndex = new RobotKnowledgeIndex();
                BeanUtils.copyProperties(robotKnowledge, robotKnowledgeIndex);
                // 将新创建的robotKnowledgeIndex添加到列表中
                robotKnowledges.add(robotKnowledgeIndex);
            }
            // 确保robotKnowledges非空后才进行保存操作
            template.save(robotKnowledges);
        }

        // ID查询
        RobotKnowledgeIndex robotKnowledgeIndex = template.get("10", RobotKnowledgeIndex.class);
        log.info("robotKnowledge-index-10: {}", robotKnowledgeIndex);
    }

}

这个类中执行机器人知识索引初始化的方法,将机器人知识从数据库中查出并初始化存储到Elasticsearch 中

操作机器人知识数据的service:

package com.rainbowred.commons.elasticsearch.service;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rainbowred.commons.datasource.context.DataSourceContext;
import com.rainbowred.commons.entities.robot.RobotKnowledge;
import com.rainbowred.commons.mybatis.mapper.RobotKnowledgeMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author dxl
 * @since 2024/2/19
 */

@Service
public class RobotKnowledgeService {

    @Resource
    private RobotKnowledgeMapper robotKnowledgeMapper ;

    public List<RobotKnowledge> queryAllRobotKnowledge(){
        DataSourceContext.setGroupKey("yichat01");
        return robotKnowledgeMapper.selectList(new QueryWrapper<>());
    }

}

2.6、接口测试类

package com.rainbowred.robot.service.controller;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.TotalHits;
import com.rainbowred.commons.elasticsearch.entity.ContentsIndex;
import com.rainbowred.commons.elasticsearch.entity.RobotKnowledgeIndex;
import com.rainbowred.commons.elasticsearch.service.ContentsIndexService;
import com.rainbowred.commons.elasticsearch.service.RobotKnowledgeIndexService;
import com.rainbowred.commons.elasticsearch.service.RobotKnowledgeService;
import com.rainbowred.commons.tools.utils.JsonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author dxl
 * @since 2024/2/20
 */
@RestController
public class RobotKnowledgeController {

    @Autowired
    private ElasticsearchClient client ;

    @Autowired
    private RobotKnowledgeService robotKnowledgeService;

    @Autowired
    private RobotKnowledgeIndexService robotKnowledgeIndexService;

    @Autowired
    private ContentsIndexService contentsIndexService;


    @GetMapping(value = "/initRobotKnowledge")
    public String initRobotKnowledgeIndex() {
        robotKnowledgeIndexService.initIndex();
        return "robotKnowledgeIndex init success";
    }

    @GetMapping(value = "/initContent")
    public String initContentIndex(){
        contentsIndexService.initIndex();
        return "contentIndex init success";
    }


    @RequestMapping("/search/robotKnowledgeAnswers")
    public String searchRobotKnowledge(@RequestParam("question") String question) throws IOException {
        SearchResponse<RobotKnowledgeIndex> searchResponse = client.search(searchReq -> searchReq.index("robotknowledge_index")
                        .query(query -> query.match(field -> field
                        .field("question").query(question))), RobotKnowledgeIndex.class);

        return printResp(searchResponse);
    }

    @RequestMapping("/search/content")
    public String searchContent(@RequestParam("content") String content) throws IOException {
        SearchResponse<ContentsIndex> searchResponse = client.search(searchrReq -> searchrReq.index("contents_index")
                .query(query -> query.match(field -> field
                .field("content").query(content))),ContentsIndex.class);
        return printResp(searchResponse);
    }


    private <T>String printResp (SearchResponse<T> resp){
        TotalHits total = resp.hits().total();
        System.out.println("total:"+total);
        List<Hit<T>> hits = resp.hits().hits();
        List<T> indexList = new ArrayList<>();
        for (Hit<T> hit : hits){
            indexList.add(hit.source());
        }
        return JsonUtil.toJson(indexList);
    }

}

然后我们就可以通过接口查询到机器人知识的答案了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值