当前位置: 首页 > news >正文

es简单实现文章检索功能

使用的api是:Elasticsearch Java API client 8.0

官网:Package structure and namespace clients | Elasticsearch Java API Client [8.15] | Elastic

 

1.建立索引库

实现搜索功能字段:

  1. title:文章标题
  2. content:文章内容
  3. 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是&#xff1a;Elasticsearch Java API client 8.0 官网&#xff1a;Package structure and namespace clients | Elasticsearch Java API Client [8.15] | Elastic 1.建立索引库 实现搜索功能字段&#xff1a; title&#xff1a;文章标题content&#xff1a;文章内…...

太速科技-607-基于FMC的12收和12发的光纤子卡

基于FMC的12收和12发的光纤子卡 一、板卡概述 本卡是一个FPGA夹层卡&#xff08;FMC&#xff09;模块&#xff0c;可提供高达2个CXP模块接口&#xff0c;提供12路收&#xff0c;12路发的光纤通道。每个通道支持10Gbps,通过Aurora协议&#xff0c;可以组成X4&#xff0…...

UEFI学习笔记(十):系统表与ACPI表的遍历

一、概述 在 UEFI 系统表中&#xff0c;有几个关键的表用于提供系统信息、服务和硬件抽象。这些表可以通过 EFI_SYSTEM_TABLE 访问&#xff0c;常见的 UEFI 系统表如下&#xff1a; 1、EFI_SYSTEM_TABLE (系统表) EFI_SYSTEM_TABLE 是一个指针&#xff0c;包含多个服务和系统…...

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

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

深入理解链表(SList)操作

目录&#xff1a; 一、 链表介绍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. 方式一&#xff1a;使用 iptables 添加白名单&#xff08;推荐使用&#xff09;&#xff1a;2. 方式二&#xff1a;重载防火墙 一、prometheus 监控 Linux 主机 1. 官网下载 node_exporter 官网&#xff1a;htt…...

AI占据2024诺贝尔两大奖项,是否预示着未来AI即一切?

本次诺贝尔物理学和学奖的获得者都与AI息息相关&#xff0c;可谓是“AI领域的大丰收”。 2024年诺贝尔物理学奖揭晓&#xff1a;瑞典皇家科学院公布了2024年诺贝尔物理学奖的获得者。他们是美国的约翰霍普菲尔德&#xff08;John J. Hopfield&#xff09;&#xff0c;以及加拿…...

[已解决] Install PyTorch 报错 —— OpenOccupancy 配环境

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

6. PH47 代码框架硬件开发环境搭建

概述 PH47代码框架的硬件开发环境搭建同样简单&#xff0c; 建立基本的 PH47 框架学习或二次开发的硬件开发环境所需设备如下&#xff1a; BBP 飞控板及相关软硬件: BBP飞控板&#xff0c;或者至少一块Stm32F411核心板&#xff08;WeAct Studio&#xff09;Stm32程序烧录工具…...

package.json配置

package.json配置 描述配置文件配置脚本配置依赖配置发布配置系统配置第三方配置 描述配置 name : 项目名称&#xff0c;第三方包可以通过npm install 包名安装 "name":"react"version : 项目版本&#xff0c;项目版本号 "version" : "18.2…...

视频怎么转gif动图?5个简单转换方法快来学(详细教程)

相信大家在社交平台上会经常看到一些有趣的gif动图表情包&#xff0c;有些小伙伴就会问&#xff1a;这些GIF动图是如何制作的呢&#xff1f;一般GIF动图表情包可以用视频来制作&#xff0c;今天小编就来给大家分享几个视频转成GIF动图的方法&#xff0c;相信通过以下的几个方法…...

10月更新:优维EasyOps®需求解决更彻底,功能体验再升级

升 级 不 止 步 欢迎来到 需求至上&#xff0c;功能完善 的 \ EasyOps 7.5版本 / &#x1f447; >> 联动架构视图&#xff1a;深度融合监控与资源拓扑 传统上&#xff0c;依赖监控态势感知系统固有的分层拓扑结构虽有其优势&#xff0c;但在处理复杂系统尤其是核心数…...

黑马javaWeb笔记重点备份1:三层架构、IOC、DI

来自&#xff1a;【黑马程序员JavaWeb开发教程&#xff0c;实现javaweb企业开发全流程&#xff08;涵盖SpringMyBatisSpringMVCSpringBoot等&#xff09;】 https://www.bilibili.com/video/BV1m84y1w7Tb/?p75&share_sourcecopy_web&vd_source9332b8fc5ea8d349a54c398…...

大坝渗流监测设备——渗压计

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

Pikachu-Sql Inject-宽字节注入

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

如何制作低代码开发的视频教程?

如何制作低代码开发的视频教程&#xff1f; 随着数字化转型的加速&#xff0c;越来越多的企业和组织开始采用低代码开发平台来加速应用程序的构建。对于许多开发者和业务人员来说&#xff0c;学习如何使用这些平台可以显著提高工作效率。因此&#xff0c;创建一份清晰、实用且…...

Flink学习地址

--基础概念 概览 | Apache Flink --应用系列 如何学习Flink&#xff1a;糙快猛的大数据之路&#xff08;图文并茂&#xff09;_flink 学习-CSDN博客 --Python系列 pyflink实时接收kafka数据至hive_pyflink下kafka数据经过处理后插入hive-CSDN博客 Pyflink教程(一)&#…...

05_23 种设计模式之《建造者模式》

文章目录 一、建造者模式基础知识建造者模式的结构建造者模式的应用场景 一、建造者模式基础知识 建造者模式&#xff08;Builder Pattern&#xff09;是一种创建型设计模式&#xff0c;它提供了一种优雅的方式来创建复杂对象&#xff0c;同时隐藏其构建过程。这种模式允许你通…...

详细分析Spring Security OAuth2中的JwtAccessTokenConverter基本知识(附Demo)

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

python习题2

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

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

uniapp 小程序 学习(一)

利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 &#xff1a;开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置&#xff0c;将微信开发者工具放入到Hbuilder中&#xff0c; 打开后出现 如下 bug 解…...

【HarmonyOS 5】鸿蒙中Stage模型与FA模型详解

一、前言 在HarmonyOS 5的应用开发模型中&#xff0c;featureAbility是旧版FA模型&#xff08;Feature Ability&#xff09;的用法&#xff0c;Stage模型已采用全新的应用架构&#xff0c;推荐使用组件化的上下文获取方式&#xff0c;而非依赖featureAbility。 FA大概是API7之…...

node.js的初步学习

那什么是node.js呢&#xff1f; 和JavaScript又是什么关系呢&#xff1f; node.js 提供了 JavaScript的运行环境。当JavaScript作为后端开发语言来说&#xff0c; 需要在node.js的环境上进行当JavaScript作为前端开发语言来说&#xff0c;需要在浏览器的环境上进行 Node.js 可…...

Mac flutter环境搭建

一、下载flutter sdk 制作 Android 应用 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 1、查看mac电脑处理器选择sdk 2、解压 unzip ~/Downloads/flutter_macos_arm64_3.32.2-stable.zip \ -d ~/development/ 3、添加环境变量 命令行打开配置环境变量文件 ope…...

linux设备重启后时间与网络时间不同步怎么解决?

linux设备重启后时间与网络时间不同步怎么解决&#xff1f; 设备只要一重启&#xff0c;时间又错了/偏了&#xff0c;明明刚刚对时还是对的&#xff01; 这在物联网、嵌入式开发环境特别常见&#xff0c;尤其是开发板、树莓派、rk3588 这类设备。 解决方法&#xff1a; 加硬件…...