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查询账户余额…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...

回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...

PydanticAI快速入门示例
参考链接:https://ai.pydantic.dev/#why-use-pydanticai 示例代码 from pydantic_ai import Agent from pydantic_ai.models.openai import OpenAIModel from pydantic_ai.providers.openai import OpenAIProvider# 配置使用阿里云通义千问模型 model OpenAIMode…...
【深尚想】TPS54618CQRTERQ1汽车级同步降压转换器电源芯片全面解析
1. 元器件定义与技术特点 TPS54618CQRTERQ1 是德州仪器(TI)推出的一款 汽车级同步降压转换器(DC-DC开关稳压器),属于高性能电源管理芯片。核心特性包括: 输入电压范围:2.95V–6V,输…...
用 FFmpeg 实现 RTMP 推流直播
RTMP(Real-Time Messaging Protocol) 是直播行业中常用的传输协议。 一般来说,直播服务商会给你: ✅ 一个 RTMP 推流地址(你推视频上去) ✅ 一个 HLS 或 FLV 拉流地址(观众观看用)…...

【threejs】每天一个小案例讲解:创建基本的3D场景
代码仓 GitHub - TiffanyHoo/three_practices: Learning three.js together! 可自行clone,无需安装依赖,直接liver-server运行/直接打开chapter01中的html文件 运行效果图 知识要点 核心三要素 场景(Scene) 使用 THREE.Scene(…...