文章目录
前言
在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);
}
}
然后我们就可以通过接口查询到机器人知识的答案了