【技能】项目中的hibernate-search6.2全文搜索实践

公司内部决定开发一个类似飞书的资源管理项目,其中包括了资源的权限控制管理等等、素材的存储、素材的预览,中间还有一个功能点是文件库搜索,作为一个"资深"后端人,直觉来看这块得上个全文搜索以绝后患,今天我主要介绍下自己对这个单体项目全文搜索框架的hibernate-search实践。

全文搜索

全文搜索:从文本或数据库中,不限定资料字段,自由地萃取出消息的技术[zh.wikipedia.org/zh-cn/%E5%8...

在全文搜索的功能需求之上,我们需要了解实现的这个方案的核心的技术,就是倒排索引

倒排索引

这个知识点有点烂大街了,简而言之就是我们先从内容提取出索引片段,然后mapping 索引到具体内容的位置,因为是从索引查找内容,称为倒排索引。有倒排就有正排索引,正排索引是通过id去搜索对应的完整数据的位置,和搜索内容本身无关,正排索引被应用在关系和非关系的数据库中。

倒排索引可以类比就是我们以前买新华字典的每一页最边侧有一个小小的字母

以前小的时候,查阅自己不会的字的发音解释,都会先把字典大致一翻,然后根据心里字母表的顺序从前或者从后找到对应的字母页,然后根据第二元音的顺序继续使用这个算法,现在想想是不是觉得一切生活处处是算法,整个过程基于倒排索引将数据存储在"字典中",然后我们通过二分的思想不断收缩查找范围实现内容检索。

Lucene

Lucene项目是后续很多比较知名的全文搜索项目的基石:en.wikipedia.org/wiki/Apache...

css 复制代码
Apache Nutch -- 提供网络爬行和 HTML 解析  
Apache Solr -- 企业搜索服务器
CrateDB -- 基于 Lucene 构建的开源分布式 SQL 数据库
DocFetcher -- 多平台桌面搜索应用程序  
Elasticsearch -- 2010 年发布的企业搜索服务器

hibernate-search 提供了Lucene(默认) 和 Elasticsearch两种方案的底层实现,Lucene 是基于文件系统存储索引。Lucene的索引是分层的,依次为 Index(索引) --> Segment(段) --> Document(文档) --> Field(域) --> Term(词/词汇单元)。

名称 扩展名 简要描述 相关源码
Segment File segments_N commit点信息,其中N是一个36进制表示的值 SegmentInfos
Lock File write.lock 文件锁,避免多个writer同时写;默认和索引文件一个目录。
Segment Info .si segment的元数据信息,指明这个segment都包含哪些文件 Lucene70SegmentInfoFormat
Compound File .cfs, .cfe 如果启用compound功能,会压缩索引到2个文件内 Lucene50CompoundFormat
Fields .fnm 存储有哪些Field,以及相关信息 Lucene60FieldInfosFormat
Field Index .fdx Field数据文件的索引 Lucene50StoredFieldsFormat
Field Data .fdt Field数据文件 Lucene50StoredFieldsFormat
Term Dictionary .tim Term词典 BlockTreeTermsWriter
Term Index .tip 指向Term词典的索引 BlockTreeTermsWriter
Frequencies .doc 保留包含每个Term的文档列表 Lucene50PostingsWriter
Positions .pos Term在文章中出现的位置信息 Lucene50PostingsWriter
Payloads .pay offset偏移/payload附加信息 Lucene50PostingsWriter
Norms .nvd, .nvm .nvm保存加权因子元数据;.nvd存储加权数据 Lucene70NormsFormat
Per-Document Values .dvd, .dvm .dvm存文档正排元数据;.dvd存文档正排数据 Lucene70DocValuesFormat
Term Vector Index .tvx 指向tvd的offset Lucene50TermVectorsFormat
Term Vector Data .tvd 存储term vector信息 Lucene50TermVectorsFormat
Live Documents .liv 活着的文档列表。位图形式 Lucene50LiveDocsFormat
Point Values .dii, .dim 多维数据,地理位置等信息,用于处理数值型的查询 Lucene60PointsFormat

6.2官方文档[docs.jboss.org/hibernate/s...] 前面都是hibernate-search的基础,hibernate-search 相当于是luence 和 mysql关系型数据库之间的一个同步器,在基于orm框架下,同一个entity被更新到mysql的同时,luence 也会自动将对应的数据更新到luence中,官方的架构图:

  1. 添加对应的依赖
xml 复制代码
<dependency>
    <groupId>org.hibernate.search</groupId>
    <artifactId>hibernate-search-mapper-orm</artifactId>
</dependency>
<dependency>
    <groupId>org.hibernate.search</groupId>
    <artifactId>hibernate-search-backend-lucene</artifactId>
</dependency>
  1. 自定义索引的存储位置(6.2版本)
yaml 复制代码
spring:
    properties:
      hibernate:
        search:
          backend:
            directory:
              root: /nfs/index
  1. 使用注解的方式配置需要全文搜索的javabean
less 复制代码
@Data
@Entity
@Table(name="t_document_file_research")
@Indexed(index = "index_file")
public class FileResearchEntity {

    @Id
    @GeneratedValue(strategy =  GenerationType.IDENTITY )
    private Long id;

    @GenericField(projectable = Projectable.YES)  
    private Long sourceId;

    @FullTextField(name = "search_name")
    @GenericField(sortable = Sortable.YES,projectable = Projectable.YES)  
    private String name;

    @GenericField(projectable = Projectable.YES)  
    private Long userId;

    @GenericField(sortable = Sortable.YES,projectable = Projectable.YES)
    private LocalDateTime updateTime;

    @GenericField(projectable = Projectable.YES)
    private Boolean deleteState;                  

    @FullTextField(name = "search_label")
    @GenericField(sortable = Sortable.YES,projectable = Projectable.YES) //
    private String labelList;
}

使用 FullTextField 注解,配置哪些字段是需要被搜索的,FullTextField不支持排序高亮持久化的配置,可以添加GenericField 重新命名索引字段名。具体的注解使用自行参考官方文档哈。

  1. 代码完成查询的代码,比如我们需要有一个功能需求是 对name和labelList的全文匹配,如果两者命中一个就分页返回10条返回结果。
scss 复制代码
SearchSession session = null;
try {
        Session hibernateSession = sessionFactory.openSession();
        SearchSession session = Search.session(hibernateSession);
	SearchScope<FileResearchEntity> scope = session.scope( FileResearchEntity.class );
	return session.search(FileResearchEntity.class)
			.where( f -> f.bool()
					.minimumShouldMatchNumber(1) // 必须匹配成功一条条件
					.should(f.match().field("search_name" ).matching(pageRulerRequest.getKeyword().getValue()))
					.should( f.match().field("search_label").matching(pageRulerRequest.getKeyword().getValue())
					)).fetchHits(0,10);
}finally {
	close(session);
}

如果我们使用spring jpa 修改FileResearchEntity 数据,那么hibernate-search会自动级联完成数据更新同步。

后言

在持久层操作的框架上,基本上的公司都是使用mybatis或者spring data jpa ,简单说下自己对这两个框架的理解和感受。

mybatis 这个框架在国内刚流行的时候,大部分都是基于ssm框架,早期的框架配置比较多,不像如今进入了spring boot的注解时代,所以以前的mybatis 都是通过配置各种xml,在xml编写mybatis语法的sql。后续基于mybatis的增强框架mybatis-plus问世,基于注解开发让操作数据变的更加的方便。

mybatis 不是强orm框架,缺少像hibernate框架,对代码层和数据库层一些表明字段名的等信息硬绑定,但是mybatis 更加灵活,但是mybatis自带了很多的脚本语法,可以很方便实现出复杂的动态查询语句,目前看热度mybatis更胜spring data jpa。

spring data jpa 是基于hibernate 的orm持久层框架,属于spring 全家桶的子项目,全注解的开发模式,但是因为基于hibernate ,稍微复杂点的还能依赖JpaSpecificationExecutor,但是如果一些很复杂的联表动态参数,可能短板就比较明显。一般我们都是配合querydsl一起使用 ,或者评论可以告知下是不是有更好的解决方案。

相关推荐
Vitalia25 分钟前
从零开始学Rust:枚举(enum)与模式匹配核心机制
开发语言·后端·rust
飞飞翼1 小时前
python-flask
后端·python·flask
草捏子2 小时前
最终一致性避坑指南:小白也能看懂的分布式系统生存法则
后端
一个public的class2 小时前
什么是 Java 泛型
java·开发语言·后端
头孢头孢4 小时前
k8s常用总结
运维·后端·k8s
TheITSea4 小时前
后端开发 SpringBoot 工程模板
spring boot·后端
Asthenia04124 小时前
编译原理中的词法分析器:从文本到符号的桥梁
后端
Asthenia04124 小时前
用RocketMQ和MyBatis实现下单-减库存-扣钱的事务一致性
后端
Pasregret4 小时前
04-深入解析 Spring 事务管理原理及源码
java·数据库·后端·spring·oracle
Micro麦可乐4 小时前
最新Spring Security实战教程(七)方法级安全控制@PreAuthorize注解的灵活运用
java·spring boot·后端·spring·intellij-idea·spring security