springboot工程中使用es实现搜索功能

ElasticSearch是一个基于Lucene的开源的、分布式实时搜索和分析引擎。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。

上一节我们以及安装好了elasticsearch以及kibana、IK分词器

现在让我们看看如何在springboot工程中使用吧。

安装依赖

我们使用的是7.x,注意7.x和8.x的差别较大,初学者可以先从7.x入手。

XML 复制代码
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.12.1</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>7.12.1</version>
        </dependency>

es配置

yml文件配置

XML 复制代码
elasticsearch:
  hostlist: 你的es服务ip #多个结点中间用逗号分隔
  article:
    index: article
    source_fields: id,title,content,summary,thumbnail,categoryId,isTop,status,viewCount,isComment,createTime
java 复制代码
@Configuration
public class ElasticsearchConfig {
    @Value("${elasticsearch.hostlist}")
    private String hostlist;

    @Bean
    public RestHighLevelClient restHighLevelClient() {
        //解析hostlist配置信息
        String[] split = hostlist.split(",");
        //创建HttpHost数组,其中存放es主机和端口的配置信息
        HttpHost[] httpHostArray = new HttpHost[split.length];
        for (int i = 0; i < split.length; i++) {
            String item = split[i];
            httpHostArray[i] = new HttpHost(item.split(":")[0], Integer.parseInt(item.split(":")[1]), "http");
        }
        //创建RestHighLevelClient客户端
        return new RestHighLevelClient(RestClient.builder(httpHostArray));
    }
}

索引

定义service接口

java 复制代码
public interface IndexService {
    /**
     * 添加文章索引
     * @param
     * @return
     */
    public Boolean addArticleIndex(String indexName, String id, Object object);
}

实现service接口

java 复制代码
@Service
@Slf4j
public class IndexServiceImpl implements IndexService {

    @Resource
    RestHighLevelClient client;


    @Override
    public Boolean addArticleIndex(String indexName, String id, Object object) {
        // 格式化日期
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = sdf.format(((Article) object).getCreateTime());

        // 将格式化后的日期字符串替换 JSON 字符串中的 createTime 字段
        JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(object));
        jsonObject.put("createTime", dateString);
        String jsonString = jsonObject.toJSONString();
//        String jsonString = JSON.toJSONString(object);
        IndexRequest indexRequest = new IndexRequest(indexName).id(id);
        //指定索引文档内容
        indexRequest.source(jsonString, XContentType.JSON);
        //索引响应对象
        IndexResponse indexResponse = null;
        try {
            indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("添加索引出错:{}", e.getMessage());
            e.printStackTrace();
            ZhiWenPlusException.cast("添加索引出错");
        }
        String name = indexResponse.getResult().name();
        System.out.println(name);
        return name.equalsIgnoreCase("created") || name.equalsIgnoreCase("updated");
    }
}

创建索引接口

java 复制代码
@RestController
@RequestMapping("search/index")
public class ArticleIndexController {

    @Value("${elasticsearch.article.index}")
    private String courseIndexStore;

    @Resource
    private IndexService indexService;

    @ApiOperation(value = "添加文章索引")
    @PostMapping("/article")
    public Boolean addIndex(@RequestBody Article article) {
        Long id = article.getId();
        if (id == null) {
            ZhiWenPlusException.cast("文章id为空");
        }
        Boolean result = indexService.addArticleIndex(courseIndexStore, String.valueOf(id), article);
        if (!result) {
            ZhiWenPlusException.cast("添加课程索引失败");
        }
        return result;
    }
}

搜索

根据关键字搜索文章信息,搜索方式为全文检索,关键字需要匹配文章的标题、简介,并且高亮显示。

定义service接口

java 复制代码
public interface ArticleSearchService {
    /**
     * 搜索文章信息
     * @param pageNo
     * @param pageSize
     * @param keyWords
     * @return
     */
    ResponseResult queryArticleIndex(Long pageNo, Long pageSize, String keyWords);
}

实现service接口

java 复制代码
@Service
@Slf4j
public class ArticleSearchServiceImpl implements ArticleSearchService {

    @Value("${elasticsearch.article.index}")
    private String courseIndexStore;
    @Value("${elasticsearch.article.source_fields}")
    private String sourceFields;

    @Resource
    RestHighLevelClient client;


    @Override
    public ResponseResult queryArticleIndex(Long pageNo, Long pageSize,String keyWords) {
        //设置索引
        SearchRequest searchRequest = new SearchRequest(courseIndexStore);

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //source源字段过虑
        String[] sourceFieldsArray = sourceFields.split(",");
        searchSourceBuilder.fetchSource(sourceFieldsArray, new String[]{});

        if (StringUtils.isNotEmpty(keyWords)) {
            //匹配关键字
            MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keyWords, "title", "summary");
            //设置匹配占比
            multiMatchQueryBuilder.minimumShouldMatch("70%");
            //提升另个字段的Boost值
            multiMatchQueryBuilder.field("title", 10);
            boolQueryBuilder.must(multiMatchQueryBuilder);
        }

        int start = (int) ((pageNo - 1) * pageSize);
        searchSourceBuilder.from(start);
        searchSourceBuilder.size(Math.toIntExact(pageSize));
        //布尔查询
        searchSourceBuilder.query(boolQueryBuilder);
        //高亮设置
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<font class='eslight'>");
        highlightBuilder.postTags("</font>");
        //设置高亮字段
        highlightBuilder.fields().add(new HighlightBuilder.Field("title"));
        searchSourceBuilder.highlighter(highlightBuilder);
        //请求搜索
        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = null;
        try {
            searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("课程搜索异常:{}", e.getMessage());
//            return new SearchPageResultDto<CourseIndex>(new ArrayList(), 0, 0, 0);
            ZhiWenPlusException.cast("课程搜索异常");
        }

        //结果集处理
        SearchHits hits = searchResponse.getHits();
        SearchHit[] searchHits = hits.getHits();
        //记录总数
        TotalHits totalHits = hits.getTotalHits();
        //数据列表
        List<Article> list = new ArrayList<>();

        for (SearchHit hit : searchHits) {

            String sourceAsString = hit.getSourceAsString();
            Article article = JSON.parseObject(sourceAsString, Article.class);

            //取出source
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();

            //课程id
            Long id = article.getId();
            //取出名称
            String title = article.getTitle();
            //取出高亮字段内容
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (highlightFields != null) {
                HighlightField nameField = highlightFields.get("title");
                if (nameField != null) {
                    Text[] fragments = nameField.getFragments();
                    StringBuffer stringBuffer = new StringBuffer();
                    for (Text str : fragments) {
                        stringBuffer.append(str.string());
                    }
                    title = stringBuffer.toString();

                }
            }
            article.setId(id);
            article.setTitle(title);

            list.add(article);

        }

        return new ResponseResult().ok(list);
    }
}

创建搜索接口

java 复制代码
@RestController
@RequestMapping("search/article")
public class ArticleSearchController {

    @Resource
    private ArticleSearchService articleSearchService;

    @GetMapping("/list")
    public ResponseResult searchArticle(Long pageNum, Long pageSize,String keywords) {
        ResponseResult result = articleSearchService.queryArticleIndex(pageNum, pageSize, keywords);
        return result;
    }
}
相关推荐
海兰1 天前
【第28篇】可观测性实战:LangFuse 方案详解
人工智能·spring boot·alibaba·spring ai
0xDevNull1 天前
Linux 中 Nginx 代理 Redis 的详细教程
redis·后端
GetcharZp1 天前
告别 Nginx 手动配置!这款 Go 语言开发的云原生网关,才是容器化时代的真香神器!
后端
RuoyiOffice1 天前
SpringBoot+Vue3 企业考勤如何处理法定假期?节假日方案、调休补班与工作日判断链路拆解
spring boot·后端·vue·anti-design-vue·ruoyioffice·假期·人力
xmjd msup1 天前
spring security 超详细使用教程(接入springboot、前后端分离)
java·spring boot·spring
Vane11 天前
从零开发一个AI插件,经历了什么?
人工智能·后端
952361 天前
SpringBoot统一功能处理
java·spring boot·后端
rleS IONS1 天前
SpringBoot中自定义Starter
java·spring boot·后端
DevilSeagull1 天前
MySQL(2) 客户端工具和建库
开发语言·数据库·后端·mysql·服务
TeDi TIVE1 天前
springboot和springframework版本依赖关系
java·spring boot·后端