搭建elasticsearch服务器(使用的docker)
https://blog.csdn.net/weixin_44570041/article/details/144136251
windows搭建:详细安装步骤参考此资料 windows 安装 Elasticsearch
安装ik分词器插件和解析文档插件
将插件下载后放置在elasticsearch的plugins文件夹下
ik:https://release.infinilabs.com/analysis-ik/stable/
解析文档插件:https://www.elastic.co/guide/en/elasticsearch/plugins/7.6.2/index.html
springboot集成elasticsearch
- pom.xml
<!-- ElasticSearch -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<exclusions>
<exclusion>
<artifactId>elasticsearch-java</artifactId>
<groupId>co.elastic.clients</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.0.1</version>
<exclusions>
<exclusion>
<artifactId>jakarta.json-api</artifactId>
<groupId>jakarta.json</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.0.1</version>
</dependency>
- 配置文件application.yml
spring:
elasticsearch:
rest:
uris: 127.0.0.1:9200
pipeline: eitc_text # 管道名称
- 配置文档全文搜索管道
package com.eitc.common.config;
import org.elasticsearch.action.ingest.PutPipelineRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 配置文档全文搜索管道
*
* @author mtx
* @date: 2024/12/11 16:19
*/
@Configuration
public class ElasticSearchConfig {
@Resource
private RestHighLevelClient restHighLevelClient;
@Value("${spring.elasticsearch.pipeline}")
private String pipeline;
@Bean
public boolean putPipelineApi() throws IOException {
String source =
"{\"description\":\"Extract attachment information\",\"processors\":[{\"attachment\":" +
"{\"field\":\"content\",\"indexed_chars\":-1,\"ignore_missing\":true}},{\"remove\":{\"field\":\"content\"}}]}";
PutPipelineRequest request = new PutPipelineRequest(
pipeline,
new BytesArray(source.getBytes(StandardCharsets.UTF_8)),
XContentType.JSON
);
AcknowledgedResponse response = restHighLevelClient.ingest().putPipeline(request, RequestOptions.DEFAULT);
return response.isAcknowledged();
}
}
使用ElasticsearchRepository添加数据
接口
/**
* 添加用户数据到es
* @return
*/
@GetMapping("/addUserToEs")
public AjaxResult addUserToEs() {
User user = new User();
user.setUserName("郭晓查");
user.setId("1001");
userElasticRepository.save(user);
return AjaxResult.success();
}
/**
* 批量初始化搜索数据
* @return
*/
@GetMapping("/init")
public AjaxResult init() {
diagnosisManageElasticRepository.deleteAll();
List<User> list = userService.list();
diagnosisManageElasticRepository.saveAll(list);
return AjaxResult.success();
}
新建一个userElasticRepository继承ElasticsearchRepository
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
/**
* UserElasticRepository
* 使用类似于mybatisplus类似的接口创建索引
* @author mtx
* @date: 2024/12/03 8:54
*/
@Repository
public interface UserElasticRepository extends ElasticsearchRepository<User, Long> {
/**
* 通过用户名称分页搜索用户信息
*/
Page<User> searchUserByUserName(String userName, Pageable pageable);
}
user实体
@Data
@Document(indexName = "idx_user")
public class User {
@Id
private Long id;
@Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
private String userName;
}
通过上传文件到es中做搜索
@Value("${spring.elasticsearch.pipeline}")
private String pipeline;
/**
* 上传文件到es中做搜索
* @param file 支持word、excel、pdf、txt
* @return
* @throws IOException
*/
@PostMapping("/uploadFile")
public AjaxResult uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
if (file.isEmpty()) {
return error("文件为空");
}
InputStream inputFile = file.getInputStream();
byte[] buffer = new byte[(int) file.getSize()];
inputFile.read(buffer);
inputFile.close();
//将文件转成base64编码
String fileString = Base64Utils.encodeToString(buffer);
FileSearchElastic fileData = new FileSearchElastic();
fileData.setTitle(file.getOriginalFilename());
fileData.setContent(fileString);
// 设置索引名称
String indexName = "idx_file";
IndexRequest indexRequest = new IndexRequest(indexName).id(String.valueOf(System.currentTimeMillis()));
indexRequest.source(JSON.toJSONString(fileData), XContentType.JSON);
// 设置管道名称
indexRequest.setPipeline(pipeline);
IndexResponse index = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
return AjaxResult.success(index);
}
搜索接口
// 分页搜索es的用户信息
@GetMapping("/page")
public TableDataInfo page(@RequestParam(name = "username", required = false) String username) {
PageDomain pageDomain = TableSupport.buildPageRequest();
Integer pageNum = pageDomain.getPageNum();
Integer pageSize = pageDomain.getPageSize();
Pageable pageable = PageRequest.of(pageNum-1, pageSize);
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder
// 模糊搜索
.should(QueryBuilders.multiMatchQuery(userName,"username"));
//高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field(new HighlightBuilder.Field("userName"));
highlightBuilder.requireFieldMatch(false);//多个高亮关闭
highlightBuilder.preTags("<span style='color:red'>");
highlightBuilder.postTags("</span>");
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder()
.withHighlightBuilder(highlightBuilder)
.withSort(SortBuilders.scoreSort().order(SortOrder.DESC))
.withPageable(pageable);
if (StringUtils.isNotBlank(diagnosisParam)) {
builder.withQuery(boolQueryBuilder);
}
NativeSearchQuery query = builder.build();
// 返回实际命中数
query.setTrackTotalHits(true);
SearchHits<User> search = elasticsearchRestTemplate.search(query, User.class);
List<User> list = new ArrayList<>();
for (org.springframework.data.elasticsearch.core.SearchHit<User> searchHit : search.getSearchHits()) {
User user = searchHit.getContent();
if (CollectionUtils.isNotEmpty(searchHit.getHighlightField("username"))) {
user.setUserName(String.join("", searchHit.getHighlightField("username")));
}
list.add(user);
}
long totalHits = search.getTotalHits();
// 解决分页查询10000条数据后报错
if (totalHits > 10000) {
totalHits = 10000;
}
TableDataInfo rspData = new TableDataInfo();
rspData.setCode(HttpStatus.SUCCESS);
rspData.setMsg("成功");
rspData.setRows(list);
rspData.setTotal(totalHits);
return rspData;
}
// 分全文搜索文档内容
@GetMapping("/page")
@ApiOperation("分全文搜索文档内容")
public TableDataInfo page(@RequestParam(name = "content", required = false) String content) throws IOException, IllegalAccessException {
List<FileSearchElastic> list = new ArrayList<>();
SearchSourceBuilder builder = new SearchSourceBuilder();
// 返回实际命中数
builder.trackTotalHits(true);
//高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("attachment.content");
highlightBuilder.field("title");
highlightBuilder.requireFieldMatch(false);//多个高亮关闭
highlightBuilder.preTags("<span style='color:red'>");
highlightBuilder.postTags("</span>");
builder.highlighter(highlightBuilder);
SearchRequest searchRequest = new SearchRequest("idx_file");
if (StringUtils.isNotBlank(content)) {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.should(QueryBuilders.matchQuery("title", content))
.should(QueryBuilders.multiMatchQuery(content, "attachment.content"));
builder.query(boolQueryBuilder);
searchRequest.source(builder);
}
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
long totalHits = 0L;
if (search.getHits() != null) {
for (SearchHit documentFields : search.getHits().getHits()) {
Map<String, HighlightField> highlightFields = documentFields.getHighlightFields();
Map<String, Object> sourceAsMap = documentFields.getSourceAsMap();
sourceAsMap.putAll(BeanUtil.beanToMap(sourceAsMap.get("attachment")));
String content = getHighlightFieldInfo(highlightFields.get("attachment.content"));
if (StringUtils.isNotBlank(content)) {
sourceAsMap.put("content", content);
}
String title = getHighlightFieldInfo(highlightFields.get("title"));
if (StringUtils.isNotBlank(title)) {
sourceAsMap.put("title", title);
}
FileSearchElastic fileSearchElastic = new FileSearchElastic();
fileSearchElastic.setTitle(Objects.isNull(sourceAsMap.get("title")) ? "" : sourceAsMap.get("title").toString());
fileSearchElastic.setContent(Objects.isNull(sourceAsMap.get("content")) ? "" : sourceAsMap.get("content").toString());
list.add(fileSearchElastic);
}
totalHits = search.getHits().getTotalHits().value;
}
// 解决分页查询10000条数据后报错
if (totalHits > 10000) {
totalHits = 10000;
}
TableDataInfo rspData = new TableDataInfo();
rspData.setCode(HttpStatus.SUCCESS);
rspData.setMsg("成功");
rspData.setRows(list);
rspData.setTotal(totalHits);
return rspData;
}
private String getHighlightFieldInfo(HighlightField highlightField) {
if (Objects.isNull(highlightField)) {
return null;
}
Text[] fragments = highlightField.fragments();
StringBuilder nTitle = new StringBuilder();
for (Text fragment : fragments) {
nTitle.append(fragment);
}
return nTitle.toString();
}
// 实体
@Data
@Document(indexName = "idx_file")
public class FileSearchElastic {
@Id
private Long id;
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
@ApiModelProperty(value = "标题", name = "title")
private String title;
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
@ApiModelProperty(value = "内容", name = "内容")
private String content;
}