springboot集成elasticsearch实现文字和文档搜索

发布于:2024-12-18 ⋅ 阅读:(152) ⋅ 点赞:(0)

搭建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

  1. 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>
  1. 配置文件application.yml
spring:
  elasticsearch:
    rest:
      uris: 127.0.0.1:9200
    pipeline: eitc_text # 管道名称
  1. 配置文档全文搜索管道
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;
}

网站公告

今日签到

点亮在社区的每一天
去签到