es简单实现文章检索功能
使用的api是:Elasticsearch Java API client 8.0
官网:Package structure and namespace clients | Elasticsearch Java API Client [8.15] | Elastic
1.建立索引库
实现搜索功能字段:
- title:文章标题
- content:文章内容
- category:文章分类
PUT /search_test
{"mappings": {"properties": {"title":{"type": "text","analyzer": "ik_max_word","index": true},"content":{"type": "text","analyzer": "ik_max_word","index": true},"category":{"type": "keyword","index": true}}}
}
2.代码实现
返回实体:
@Data
public class ContentSearchVO {@Schema(description = "标题")private String title;@Schema(description = "高亮内容部分,用于首页展示")private String highLightContent;@Schema(description = "内容")private String content;}
请求实体:
@Data
public class SearchDTO {@Schema(description="搜索词")@NotBlank(message = "搜索词不能是空")private String text;@Schema(description="分类")private String category;private Integer pageNo;private Integer pageSize;}
2.1基于模板形式
@Overridepublic List<ContentSearchVO> search(SearchDTO dto) {String tags = dto.getCategory();if (tags != null){tags = String.format(SearchConstants.CATEGORY, tags) ;}else {tags = "";}//es内容搜索String query = String.format(SearchConstants.SEARCH_MAPPING, dto.getText(), dto.getText(), tags, dto.getPageNo(), dto.getPageSize());StringReader stringReader = new StringReader(query);SearchRequest searchRequest = SearchRequest.of(builder -> builder.index(SearchConstants.INDEX_NAME).withJson(stringReader));SearchResponse<Object> response = EsSearchUtil.search(searchRequest);List<ContentSearchVO> searchResult = EsSearchUtil.buildSearchResult(response);return searchResult;}
模板常量
public class SearchConstants {//索引库public static final String INDEX_NAME = "search_test";//类别模板public static final String CATEGORY = " ,\"term\": {\n" +" \"category\": \"%s\"\n" +" },\n";//搜索模板public static final String SEARCH_MAPPING = "{" +" \"query\": {\n" +" \"bool\": {\n" +" \"must\":[ " +" {\n" +" \"match\": {\n" +" \"title\": \"%s\"\n" +" }\n" +" },\n" +" {\n" +" \"match\": {\n" +" \"content\": \"%s\"\n" +" }\n" +" }\n" +"%s"+" ]}\n" +" },\n" +" \"from\": %s,\n" +" \"size\": %s,\n" +" \"highlight\": {\n" +" \"pre_tags\": \"<em>\",\n" +" \"post_tags\": \"</em>\",\n" +" \"fields\": {\n"+" \"title\": {},\n" +" \"content\": {}\n" +" }\n" +" }\n" +"}";}
EsSearchUtil
@Component
@Slf4j
public class EsSearchUtil {private static ElasticsearchClient client;@Autowiredpublic EsSearchUtil(ElasticsearchClient client) {EsSearchUtil.client = client;}/*** 数据检索*/public static SearchResponse<Object> search(SearchRequest searchRequest) {SearchResponse<Object> response;try {response = client.search(searchRequest, Object.class);log.debug("搜索返回结果:" + response.toString());} catch (IOException e) {log.error(e.toString());throw new RuntimeException("搜索服务出了点小差,请稍后再试", e);}return response;}/*** 构建搜索结果*/public static <T> List<T> buildSearchResult(SearchResponse<Object> resp, Class<T> clazz) {if (resp.hits() == null || resp.hits().hits() == null) {return Collections.emptyList();}List<Hit<Object>> hits = resp.hits().hits();List<T> list = new ArrayList<>();for(Hit<Object> hit:hits) {//格式转换T t = ConvertUtil.objToObj(hit.source(), clazz);list.add(t);}return list;}public static List<ContentSearchVO> buildSearchResult(SearchResponse<Object> resp){List<Hit<Object>> hits = resp.hits().hits();List<ContentSearchVO> list = new ArrayList<>();for(Hit<Object> hit:hits){ContentSearchVO contentSearchVO = ConvertUtil.objToObj(hit.source(), ContentSearchVO.class);Map<String, List<String>> highlight = hit.highlight();if (!CollectionUtils.isEmpty(highlight)) {//标题List<String> highLightTitle = highlight.get("title");//内容List<String> highLightContent = highlight.get("content");//无高亮,就用内容替代if (!CollectionUtils.isEmpty(highLightTitle)){StringBuilder highLightTitleStringBuilder = new StringBuilder();for (String titleSegment : highLightTitle) {highLightTitleStringBuilder.append(titleSegment);}contentSearchVO .setTitle(highLightTitleStringBuilder.toString());}if (!CollectionUtils.isEmpty(highLightContent)){StringBuilder highLightContentStringBuilder = new StringBuilder();for (String contentSegment : highLightContent) {highLightContentStringBuilder.append(contentSegment);}contentSearchVO .setHighLightContent(highLightContentStringBuilder.toString());}else {contentSearchVO .setHighLightContent(contentSearchVO.getContent());}}list.add(contentSearchVO);}return list;}/*** 分词解析* @param text 搜索词* @param index 索引库* @return 分词结果*/public static List<String> analyze (String text,String index) {AnalyzeRequest analyzeRequest = new AnalyzeRequest.Builder().index(index).text(text).analyzer("ik_max_word") // ik_smart.build();AnalyzeResponse analyzeResponse;try {analyzeResponse = client.indices().analyze(analyzeRequest);} catch (IOException e) {throw new RuntimeException(e);}List<AnalyzeToken> tokens = analyzeResponse.tokens();List<String> result = new ArrayList<>();// 分词结果for (AnalyzeToken token : tokens) {result.add(token.token());}return result;}
}
2.2 基于搜索api实现
@Service
@Slf4j
@RequiredArgsConstructor
public class ContentSearchServiceImpl implements ContentSearchService {private final SearchHotWordsService searchHotWordsService;@Value("${search.highlightWords:500}")private Integer highlightWords;@Value("${search.words:500}")private Integer words;@Value("${search.searchWords:200}")private Integer searchWords;@Overridepublic SearchVO search(SearchDTO dto) {SearchVO result = new SearchVO();//构建boolBoolQuery boolQuery = this.buildBoolQuery(dto);//构建高亮字段Highlight highlight = this.buildHighlight();//构建查询SearchRequest searchRequest = SearchRequest.of(s -> s.index(SearchConstants.INDEX_NAME).query(q -> q.bool(boolQuery)).from((dto.getPageNo() - 1) * dto.getPageSize()).size(dto.getPageSize()).highlight(highlight));SearchResponse<SearchDocument> response = EsSearchUtil.search(searchRequest,SearchDocument.class);List<ContentSearchVO> searchResult = this.buildSearchResult(response);if (!CollectionUtils.isEmpty(searchResult)){searchResult = this.sortSearchResult(searchResult);}result.setList(searchResult);return result;}/*** 构建bool* @param dto 查询参数* @return BoolQuery*/private BoolQuery buildBoolQuery(SearchDTO dto) {String tags = dto.getTags();//要查询的字段List<String> queryFields = List.of(SearchConstants.FILE_NAME, SearchConstants.CONVERTED_TEXT);//构建bool查询BoolQuery.Builder boolBuilder = new BoolQuery.Builder().should(s -> s.multiMatch(mu -> mu.fields(queryFields).query(dto.getText())));if (tags != null){List<FieldValue> v = new ArrayList<>();String[] split = tags.split(",");for (String s : split) {v.add(FieldValue.of(s));}TermsQuery termsQuery = TermsQuery.of(t -> t.field(SearchConstants.TAGS).terms(tm -> tm.value(v)));boolBuilder.must(m -> m.terms(termsQuery));}return boolBuilder.build();}/*** 构建高亮字段* @return Highlight*/private Highlight buildHighlight() {Map<String, HighlightField> map = new HashMap<>();map.put(SearchConstants.FILE_NAME, HighlightField.of(hf -> hf.preTags(SearchConstants.PRE_TAGS).postTags(SearchConstants.POST_TAGS)));map.put(SearchConstants.CONVERTED_TEXT, HighlightField.of(hf -> hf.preTags(SearchConstants.PRE_TAGS).postTags(SearchConstants.POST_TAGS).numberOfFragments(1).fragmentSize(highlightWords) //numberOfFragments(1),表示将字段分割为最多1个片段,并设置 fragmentSize(300),表示每个片段的大小为300个字符。));return Highlight.of(h -> h.type(HighlighterType.Unified).order(HighlighterOrder.Score).fields(map));}private List<ContentSearchVO> buildSearchResult(SearchResponse<SearchDocument> resp) {List<Hit<SearchDocument>> hits = resp.hits().hits();List<ContentSearchVO> list = new ArrayList<>();for(Hit<SearchDocument> hit:hits){SearchDocument searchDocument = JsonUtil.objToObj(hit.source(), SearchDocument.class);ContentSearchVO contentSearchVO = this.searchAdapter(searchDocument);String content = contentSearchVO.getContent();Map<String, List<String>> highlight = hit.highlight();if (!CollectionUtils.isEmpty(highlight)) {//高亮标题List<String> highLightTitle = highlight.get(SearchConstants.FILE_NAME);//高亮内容List<String> highLightContent = highlight.get(SearchConstants.CONVERTED_TEXT);//标题if (!CollectionUtils.isEmpty(highLightTitle)){StringBuilder highLightTitleStringBuilder = new StringBuilder();for (String titleSegment : highLightTitle) {highLightTitleStringBuilder.append(titleSegment);}contentSearchVO .setTitle(highLightTitleStringBuilder.toString());}//内容if (!CollectionUtils.isEmpty(highLightContent)){StringBuilder highLightContentStringBuilder = new StringBuilder();for (String titleSegment : highLightContent) {highLightContentStringBuilder.append(titleSegment);}contentSearchVO .setContent(highlightContent);}else {//无高亮字段---从头开始取一定数量的字String s = this.replaceSymbol(content).replace(SearchConstants.LINE, "");if (s.length() >= words){s = s.substring(0,words);}contentSearchVO .setContent(s);}}else {//无高亮字段---从头开始取一定数量的字String s = this.replaceSymbol(content).replace(SearchConstants.LINE, "");if (s.length() >= words){s = s.substring(0,words);}contentSearchVO .setContent(s);}list.add(contentSearchVO );}return list;}private ContentSearchVO searchAdapter(SearchDocument searchDocument) {ContentSearchVO contentSearchVO = new ContentSearchVO();contentSearchVO .setTitle(searchDocument.getFileName());contentSearchVO .setContent(searchDocument.getDocText());contentSearchVO .setFileId(searchDocument.getFileId());contentSearchVO .setTags(searchDocument.getTags());contentSearchVO .setTimestamp(searchDocument.getTimestamp());return contentSearchVO ;}private String replaceSymbol(String replaceStr) {return replaceStr.replaceAll("\t","").replaceAll(" "," ").replaceAll(" "," ").replaceAll("(\\n\\s*)+", " ") //多重换行只保留一个.replaceAll("\\s+"," ").replaceAll("-\\d+-", "") //去掉 页码;}}
相关文章:
es简单实现文章检索功能
使用的api是:Elasticsearch Java API client 8.0 官网:Package structure and namespace clients | Elasticsearch Java API Client [8.15] | Elastic 1.建立索引库 实现搜索功能字段: title:文章标题content:文章内…...

太速科技-607-基于FMC的12收和12发的光纤子卡
基于FMC的12收和12发的光纤子卡 一、板卡概述 本卡是一个FPGA夹层卡(FMC)模块,可提供高达2个CXP模块接口,提供12路收,12路发的光纤通道。每个通道支持10Gbps,通过Aurora协议,可以组成X4࿰…...

UEFI学习笔记(十):系统表与ACPI表的遍历
一、概述 在 UEFI 系统表中,有几个关键的表用于提供系统信息、服务和硬件抽象。这些表可以通过 EFI_SYSTEM_TABLE 访问,常见的 UEFI 系统表如下: 1、EFI_SYSTEM_TABLE (系统表) EFI_SYSTEM_TABLE 是一个指针,包含多个服务和系统…...

【深度学习基础模型】液态状态机(Liquid State Machines, LSM)详细理解并附实现代码。
【深度学习基础模型】液态状态机(Liquid State Machines, LSM)详细理解并附实现代码。 【深度学习基础模型】液态状态机(Liquid State Machines, LSM)详细理解并附实现代码。 文章目录 【深度学习基础模型】液态状态机࿰…...

深入理解链表(SList)操作
目录: 一、 链表介绍1.1、 为什么引入链表1.2、 链表的概念及结构1.3、 链表的分类 二、 无头单向非[循环链表](https://so.csdn.net/so/search?q循环链表&spm1001.2101.3001.7020)的实现2.1、 [单链表](https://so.csdn.net/so/search?q单链表&spm1001.2…...
03. prometheus 监控 Linux 主机
文章目录 一、prometheus 监控 Linux 主机二、防火墙打开端口1. 方式一:使用 iptables 添加白名单(推荐使用):2. 方式二:重载防火墙 一、prometheus 监控 Linux 主机 1. 官网下载 node_exporter 官网:htt…...

AI占据2024诺贝尔两大奖项,是否预示着未来AI即一切?
本次诺贝尔物理学和学奖的获得者都与AI息息相关,可谓是“AI领域的大丰收”。 2024年诺贝尔物理学奖揭晓:瑞典皇家科学院公布了2024年诺贝尔物理学奖的获得者。他们是美国的约翰霍普菲尔德(John J. Hopfield),以及加拿…...

[已解决] Install PyTorch 报错 —— OpenOccupancy 配环境
目录 关于 常见的初始化报错 环境推荐 torch, torchvision & torchaudio cudatoolkit 本地pip安装方法 关于 OpenOccupancy: 语义占用感知对于自动驾驶至关重要,因为自动驾驶汽车需要对3D城市结构进行细粒度感知。然而,现有的相关基准在城市场…...

6. PH47 代码框架硬件开发环境搭建
概述 PH47代码框架的硬件开发环境搭建同样简单, 建立基本的 PH47 框架学习或二次开发的硬件开发环境所需设备如下: BBP 飞控板及相关软硬件: BBP飞控板,或者至少一块Stm32F411核心板(WeAct Studio)Stm32程序烧录工具…...
package.json配置
package.json配置 描述配置文件配置脚本配置依赖配置发布配置系统配置第三方配置 描述配置 name : 项目名称,第三方包可以通过npm install 包名安装 "name":"react"version : 项目版本,项目版本号 "version" : "18.2…...

视频怎么转gif动图?5个简单转换方法快来学(详细教程)
相信大家在社交平台上会经常看到一些有趣的gif动图表情包,有些小伙伴就会问:这些GIF动图是如何制作的呢?一般GIF动图表情包可以用视频来制作,今天小编就来给大家分享几个视频转成GIF动图的方法,相信通过以下的几个方法…...

10月更新:优维EasyOps®需求解决更彻底,功能体验再升级
升 级 不 止 步 欢迎来到 需求至上,功能完善 的 \ EasyOps 7.5版本 / 👇 >> 联动架构视图:深度融合监控与资源拓扑 传统上,依赖监控态势感知系统固有的分层拓扑结构虽有其优势,但在处理复杂系统尤其是核心数…...
黑马javaWeb笔记重点备份1:三层架构、IOC、DI
来自:【黑马程序员JavaWeb开发教程,实现javaweb企业开发全流程(涵盖SpringMyBatisSpringMVCSpringBoot等)】 https://www.bilibili.com/video/BV1m84y1w7Tb/?p75&share_sourcecopy_web&vd_source9332b8fc5ea8d349a54c398…...

大坝渗流监测设备——渗压计
渗压计是一种用于监测大坝等水工建筑物渗流压力的重要设备,其准确性和可靠性对于保障大坝安全运行至关重要。南京峟思将为大家详细介绍渗压计的工作原理、安装方法及其在大坝渗流监测中的应用。 渗压计主要利用振弦频率的变化来测量渗透水压力。设备由透水部件、感应…...

Pikachu-Sql Inject-宽字节注入
基本概念 宽字节是相对于ascII这样单字节而言的;像 GB2312、GBK、GB18030、BIG5、Shift_JIS 等这些都是常说的宽字节,实际上只有两字节 GBK 是一种多字符的编码,通常来说,一个 gbk 编码汉字,占用2个字节。一个…...

如何制作低代码开发的视频教程?
如何制作低代码开发的视频教程? 随着数字化转型的加速,越来越多的企业和组织开始采用低代码开发平台来加速应用程序的构建。对于许多开发者和业务人员来说,学习如何使用这些平台可以显著提高工作效率。因此,创建一份清晰、实用且…...
Flink学习地址
--基础概念 概览 | Apache Flink --应用系列 如何学习Flink:糙快猛的大数据之路(图文并茂)_flink 学习-CSDN博客 --Python系列 pyflink实时接收kafka数据至hive_pyflink下kafka数据经过处理后插入hive-CSDN博客 Pyflink教程(一)&#…...
05_23 种设计模式之《建造者模式》
文章目录 一、建造者模式基础知识建造者模式的结构建造者模式的应用场景 一、建造者模式基础知识 建造者模式(Builder Pattern)是一种创建型设计模式,它提供了一种优雅的方式来创建复杂对象,同时隐藏其构建过程。这种模式允许你通…...

详细分析Spring Security OAuth2中的JwtAccessTokenConverter基本知识(附Demo)
目录 前言1. 基本知识2. Demo3. 实战 前言 java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)【Java项目】实战CRUD的功能整理(持续更新) 1. 基本知识 JwtAccessTokenConverter 是 Spring Security OAuth2 中的一…...

python习题2
1、输出一个年份,判断其是不是闰年 #输入一个年份,判断其是否是闰年 y eval(input()) if y%4 0 and y%100 ! 0:print("是") elif y%4000:print("是") else:print("不是") 2、模拟智能客服: 按1查询账户余额…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...

基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...

linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...

现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...

Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...

有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...