基于数据库的全文检索实现

对于内容摘要,信件内容进行全文检索
基于SpringBoot 2.5.6+Postgresql+jpa+hibernate实现

依赖

<spring-boot.version>2.5.6</spring-boot.version>
<hibernate-types-52.version>2.14.0</hibernate-types-52.version><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- hibernate支持配置 -->
<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-core</artifactId>
</dependency>
<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-entitymanager</artifactId>
</dependency>
<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-ehcache</artifactId>
</dependency>
<dependency><groupId>com.vladmihalcea</groupId><artifactId>hibernate-types-52</artifactId><version>${hibernate-types-52.version}</version>
</dependency>
<!-- hibernate支持配置 --><dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId>
</dependency><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>

业务逻辑

登记保存之后,处理完成业务逻辑,发送全文检索事件

//附加类型对应的附件ids
Map<String, List<String>> attCategoryToAttIds = new HashMap<String, List<String>>();
attCategoryToAttIds.put(cmpRecord.getFileCategory(), files==null?null:files.stream().map(d->d.getId()).collect(Collectors.toList()));
//处理监听事件所需要的数据
Map<String,Object>eventData = Utils.buildMap("recordId", cmpRecord.getId(),"newRecord", true,"attCategoryToAttIds", attCategoryToAttIds);
//创建全文检索事件
DomainEvent de = new DefaultDomainEvent(cmpRecord.getId() + "_Handle_CmpRecord_FullTextSearch", operateInfo, ExecutePoint.CURR_THREAD,eventData, new Date(), "Handle_CmpRecord_FullTextSearch");
//发布事件
DomainEventPublisherFactory.getRegisteredPublisher().publishEvent(de);

处理业务发送全文检索事件

@Service
@Transactional
@SuppressWarnings("unchecked")
public class HandleCmpRecordFullTextSearchListener implements IDomainEventListener {@Autowiredprivate CmpRecordRepository cmpRecordRepository;@Autowiredprivate DataChangeLogEventRepository dataChangeLogEventRepository;@Overridepublic void onEvent(DomainEvent event) {AccessTokenUser operator=event.getOperator();Date operateTime=event.obtainEventTime();Map<String,Object> otherData=(Map<String,Object>)event.getEventData();String recordId = (String) otherData.get("recordId");boolean newRecord=(boolean)otherData.get("newRecord");String comment = (String) otherData.get("comment");//办理记录的备注if(StringUtils.isBlank(recordId)) {throw new RuntimeException("未指定信访记录id");}//获取登记信息CmpRecord cmdRecord = cmpRecordRepository.getCmpRecordById(recordId);//指定关联关系RelateProjValObj cmpRdProj=new RelateProjValObj(recordId,RelateProjConstants.PROJTYPE_CMP_RECORD); //这是关联那个业务List<RelateProjValObj> mainProjs=Arrays.asList(cmpRdProj);DomainEvent de=null;//登记信息是无效的 则删除已存在的和这个件相关的if(cmdRecord==null||!cmdRecord.isValidEntity()) {//删除全文检索信息de=new FullTextSearchOperateEvent(recordId+"_FullTextSearch_Remove", null, operator, operateTime,mainProjs, null);DomainEventPublisherFactory.getRegisteredPublisher().publishEvent(de);return;}//全文检索 类型前缀 String contentTypepPefix=RelateProjConstants.PROJTYPE_CMP_RECORD;//在当前线程中执行,保证事务一致性ExecutePoint executePoint=ExecutePoint.CURR_THREAD;/***********************************************关键词检索-内容摘要***********************************************///全文检索的类型 区分内容摘要 附件内容List<String> contentTypes=Arrays.asList(contentTypepPefix+"_contentAbstract");String contentAbstract =cmdRecord.getBaseInfo().getContentAbstract();//内容摘要if(StringUtils.isBlank(contentAbstract)) contentAbstract="";if(StringUtils.isNotBlank(comment)) {if(StringUtils.isNotBlank(contentAbstract)) contentAbstract=contentAbstract + ",";contentAbstract=contentAbstract+comment;}de=new FullTextSearchOperateEvent(recordId+"_FullTextSearch_Update", executePoint, operator, operateTime,mainProjs, contentTypes, contentAbstract, null);DomainEventPublisherFactory.getRegisteredPublisher().publishEvent(de);/***********************************************关键词检索-信件内容***********************************************/contentTypes=Arrays.asList(contentTypepPefix+"_content");String content =cmdRecord.getBaseInfo().getContent();//信件内容de=new FullTextSearchOperateEvent(recordId+"_FullTextSearch_Update", executePoint, operator, operateTime,mainProjs, contentTypes, content, null);DomainEventPublisherFactory.getRegisteredPublisher().publishEvent(de);/***********************************************关键词检索-附件(原信等)***********************************************///如果附件也需要检索  设置attIds参数Map<String,List<String>> attCategoryToAttIds=(Map<String,List<String>>)otherData.get("attCategoryToAttIds");if(attCategoryToAttIds!=null && attCategoryToAttIds.size() > 0) {//按附件类型分开for (Map.Entry<String,List<String>> d : attCategoryToAttIds.entrySet()) {contentTypes=Arrays.asList(contentTypepPefix+"_att_"+d.getKey());List<String> attIds=d.getValue();//公文相关附件de=new FullTextSearchOperateEvent(recordId+"_att_"+d.getKey()+"_FullTextSearch_Update", executePoint,operator, operateTime, mainProjs, contentTypes, null, attIds);DomainEventPublisherFactory.getRegisteredPublisher().publishEvent(de);}}}@Overridepublic boolean listenOn(String eventType) {return "Handle_CmpRecord_FullTextSearch".equals(eventType);}
}

统一处理全文检索事件

@Service
@Transactional
public class FullTextSearchListener extends JpaHibernateRepository implements IDomainEventListener{@Autowiredprivate FullTextSearchRepository fullTextSearchRepository;@Autowiredprivate IFileSysService fileSysService;@Overridepublic void onEvent(DomainEvent event) {if("true".equals(BaseConstants.getProperty("prefetchingRecordNo", "false"))){return;}FullTextSearchOperateEvent de = null;if(event instanceof FullTextSearchOperateEvent) {de=(FullTextSearchOperateEvent)event;}if(de==null) {return;}if(FullTextSearchOperateEvent.EVENTTYPE_UPDATE.equals(de.getEventType())) {/**"mainProjs":List<RelateProjValObj> 必选"contentType":String 必选"content":String 可选"attIds":List<String> 可选  content与attIds都不存在 会删除对应关键词检索"relProjs":List<RelateProjValObj> 可选  指定的需要添加的关系"removeOtherRelProjs":false  可选  是否清除 指定relProjs以外的关联记录*/this.fullTextSearchUpdate(de);}else if(FullTextSearchOperateEvent.EVENTTYPE_REMOVE.equals(de.getEventType())) {/**"mainProjs":List<RelateProjValObj> 必选*/this.fullTextSearchRemoveByProjs(de);}}//关键词检索增加private void fullTextSearchUpdate(FullTextSearchOperateEvent de) {Date date=de.obtainEventTime();if(date==null) {date=new Date();}List<RelateProjValObj> mainProjs=de.getMainProjs();String contentType=null;if(de.getContentTypes()!=null&&de.getContentTypes().size()==1) {contentType=de.getContentTypes().get(0);}String content=de.getContent();List<String> attIds=de.getAttIds();if(mainProjs==null||mainProjs.size()==0||StringUtils.isBlank(contentType)) {throw new RuntimeException("数据指定错误");}Set<String> fullTextIds=new HashSet<String>();for (RelateProjValObj mainProj : mainProjs) {if(StringUtils.isBlank(mainProj.getProjId())||StringUtils.isBlank(mainProj.getProjType())) {continue;}fullTextIds.add(new FullTextSearch(mainProj,contentType,null,null).getId());}if(fullTextIds.size()==0) {throw new RuntimeException("数据指定错误");}//这是从附件中获取文本数据if(StringUtils.isBlank(content)&&attIds!=null) {content="";try {if(attIds.size()>0) {Map<String,String> attIdToContentMao=ThreadLocalCache.fetchAPIData(null,()->{return fileSysService.findFileContentByIds(attIds, true);});for (String attContent : attIdToContentMao.values()) {if(StringUtils.isBlank(attContent)) {continue;}if(StringUtils.isNotBlank(content)) {content+=",";}content+=RegExUtils.replaceAll(attContent, "\\u0000", "");//处理掉非法字符}}} catch (Exception e) {e.printStackTrace();}}//从数据库中获取已经存的List<FullTextSearch> oldFullTexts=this.fullTextSearchRepository.findFullTextSearchByIds(fullTextIds);Map<String,FullTextSearch> oldFullTextMap=oldFullTexts.stream().collect(Collectors.toMap(d->d.getId(),d->d));//遍历这次需要更新的记录for (RelateProjValObj mainProj : mainProjs) {if(StringUtils.isBlank(mainProj.getProjId())||StringUtils.isBlank(mainProj.getProjType())) {continue;}FullTextSearch fullText=new FullTextSearch(mainProj, contentType, content, date);FullTextSearch oldFullText=oldFullTextMap.get(fullText.getId());//旧的记录中已存在 则更新if(oldFullText!=null) {if(StringUtils.isBlank(content)) {//如果内容未空 则删除	this.fullTextSearchRepository.removeFullTextSearch(oldFullText);return;}//如果存在内容,则更新this.fullTextSearchRepository.updateFullTextSearchContent(fullText.getId(), content, date);}else {if(StringUtils.isBlank(content)) {return;}try {//否则 创建全文检索记录this.fullTextSearchRepository.createFullTextSearch(fullText);} catch (Exception e) {e.printStackTrace();return;}}}}//关键词检索删除  根据主相关件private void fullTextSearchRemoveByProjs(FullTextSearchOperateEvent de) {Date date=de.obtainEventTime();if(date==null) {date=new Date();}List<RelateProjValObj> mainProjs=de.getMainProjs();if(mainProjs==null||mainProjs.size()==0) {throw new RuntimeException("数据指定错误");}List<String> projKeys=new ArrayList<String>();for (RelateProjValObj mainProj : mainProjs) {projKeys.add(mainProj.getProjKey());}Map<String,Object> params=new HashMap<String,Object>();StringBuffer hql=new StringBuffer();hql.append("delete from ").append(FullTextSearch.class.getName()).append(" ");hql.append("where mainProj.projKey IN(:projKeys) ");params.put("projKeys", projKeys);if(de.getContentTypes()!=null&&de.getContentTypes().size()>0) {params.put("contentTypes", de.getContentTypes());}this.createHQLQueryByMapParams(hql.toString(), params).executeUpdate();}@Overridepublic boolean listenOn(String eventType) {return eventType.startsWith(FullTextSearchOperateEvent.class.getName());}
}

全文检索实体

@Entity
@Table(name="TV_FULLTEXT_SEARCH",indexes={@Index(name="idx_TV_FULLTEXT_SEARCH1",columnList="projKey"),@Index(name="idx_TV_FULLTEXT_SEARCH2",columnList="contentType")}
)
public class FullTextSearch extends IEntity {@Id@Column(length=200)private String id;private RelateProjValObj mainProj;//来源相关件@Lob@Type(type="org.hibernate.type.TextType")private String content;//检索内容@Column(length=100)private String contentType;//检索类型@Column(length=100)private Date lastUpdateDate;//最后更新时间public String getId() {return id;}public String getContent() {return content;}public String getContentType() {return contentType;}public RelateProjValObj getMainProj() {return mainProj;}public Date getLastUpdateDate() {return lastUpdateDate;}public FullTextSearch() {}public FullTextSearch(RelateProjValObj mainProj, String contentType,String content, Date lastUpdateDate) {this.id = mainProj.getProjKey()+"_"+contentType;this.mainProj = mainProj;this.content = content;this.contentType = contentType;this.lastUpdateDate = lastUpdateDate;if(this.lastUpdateDate==null){this.lastUpdateDate = new Date();}}}

存储数据格式

在这里插入图片描述
在这里插入图片描述

查询

sql大致就是这样的逻辑

select tv.id from tv_cmp_dw_query tv join tv_fulltext_search tvs on tv.id = tvs.proj_id where tvs.contet_type in () and conent like '%测试%'

事件处理机制请看另一篇文章
自定义事件处理机制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/540752.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手机备忘录怎么导出到电脑,如何将手机备忘录导出到电脑

备忘录是我们日常生活和工作中常用的工具之一&#xff0c;我们可以在手机上轻松地记录重要的事务、想法和灵感。然而&#xff0c;在某些情况下&#xff0c;我们可能需要将手机备忘录导出到电脑进行更详细的整理和管理。那么&#xff0c;手机备忘录怎么导出到电脑&#xff0c;如…

Python 3.6.6安装方法(保留环境中python2不受影响)

前言&#xff1a;因为Linux系统下自带了python2的版本&#xff0c;所以我们要用Python3的话需要自己构建安装。并保证某些已经存在的服务可以正常使用python2。 具体步骤如下&#xff1a; 一、python3.6.6 安装 1.安装依赖包&#xff1a; yum -y install zlib zlib-devel yu…

【unity与android的交互(一)】安卓打包相关的常见参数详解

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

2023 PWNHUB 3月赛-【sh_v1_1】(cp ln unsorted bin 与main_arena的偏移 思路 exp)

文章目录 无关干扰cplnunsorted bin 与main_arena的偏移思路exp 无关干扰 这段代码总是重复&#xff0c;但没啥用&#xff0c;我们可以将全部代码复制到vscode后然后将这些部分全部去除掉 if ( dword_A010 > dword_A014 )dword_A018 ^ dword_A020;if ( dword_A010 < dw…

python疑难杂症(12)---生成器、迭代器的基本概念、以及他们之间的关系区别

Python的迭代器和生成器是其量大特色法器&#xff0c;常常用于简化代码、降低数据占用内存提高运行速度上&#xff0c;学会这两件法器&#xff0c;使用Python语言也可以猪鼻子插大葱了。 1、迭代器 在Python中&#xff0c;迭代器是要求支持迭代器协议的对象&#xff0c;而支持…

使用SpaceDesk实现iPad成为电脑拓展屏(保姆级教程)

使用SpaceDesk实现iPad成为电脑拓展屏 SpaceDesk是一个开源的软件, 所以说对学生和平民用户非常的友好, 连接后的画质也非常不错, 而且具有无线和有线两种连接方式. 接下来就开始教程: 1. 安装SpaceDesk电脑版 首先我们要下载SpaceDesk电脑版安装好: SpaceDesk官网 注意: …

FreeRTOS 临界段代码保护及调度器挂起与恢复

1. 临界段代码保护简介 1. 临界段 什么是临界段&#xff1a;临界段代码也叫做临界区&#xff0c;是指那些必须完整运行&#xff0c;不能被打断的代码段。 适用场合如&#xff1a; 外设&#xff1a;需严格按照时序初始化的外设&#xff1a;IIC、SPI等等 IIC 初始化有个几微秒…

Android 录屏操作

Android 录屏操作 本文主要介绍android中如何通过MediaRecorder实现录屏操作的. 1: 申请权限 <uses-permission android:name"android.permission.RECORD_AUDIO" /> <uses-permission android:name"android.permission.WRITE_EXTERNAL_STORAGE"…

Vue.js+SpringBoot开发校园疫情防控管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 学生2.2 老师2.3 学校管理部门 三、系统展示四、核心代码4.1 新增健康情况上报4.2 查询健康咨询4.3 新增离返校申请4.4 查询防疫物资4.5 查询防控宣传数据 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBoot…

系统之家精心打造:游戏专用Win10系统,畅玩游戏无阻碍!

现在很多玩家都会在电脑上畅玩游戏&#xff0c;所以电脑系统是非常重要的。系统之家精心打造的游戏专用Win10系统&#xff0c;是为游戏爱好者量身定制&#xff0c;经过严格的优化&#xff0c;确保游戏运行时更加流畅。选择系统之家的游戏专用Win10系统&#xff0c;让您的游戏体…

德人合科技|办公电脑文件资料防泄密软件

#天锐绿盾# 办公电脑文件资料防泄密软件通常具备以下几个关键功能来保障公司敏感信息的安全&#xff1a; PC端&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee 1. 文件透明加密&#xff1a; 这是此类软件的核心功能之一&#xff…

永热爱 敢向前 | Paraverse平行云的2023 年终总结

永热爱&#xff0c;敢向前 值此新年&#xff0c;回顾2023&#xff0c;仅以此句&#xff0c;献给所有XR产业信仰者 2023 年&#xff0c;是XR产业技术和场景承上启下的关键之年 在这场波澜壮阔的技术潮中 「Paraverse平行云」踏浪前行 已是第八个年头&#xff0c;让我们一起…