一二三文档管理系统设计——3.集成Elastic Search实现文档的全文搜索功能实战及文件预览

全文搜索

技术选型

该领域已被Lucene独占,几乎无竞争对手。
但是直接使用Lucene非常复杂,因此在出现了两个组件,一是solr,二是elastic search,elastic search流行度更高,但并非在所有应用场景占优,对于索引库已建立的情况下,如将某人的个人办公电脑所有文档进行全文搜索,这种情况下,solr的性能要明显优于es;但对于动态数据的不断插入索引库,如互联网应用,则es性能明显优于solr。

对于企业文档管理系统而言,文档处于动态变化中,但变化频率相对互联网应用频率较低,solr和es都可以使用,考虑到流行程度,最终选择的es。

集成方式选择

在已经选择es的情况下,与SpringBoot整合,有几种技术方案:
**1.transport-api:**springboot版本不同,transport-api不同,不能适配不同的es版本;7.x不建议使用,8以后废弃
**2.JestClient:**非官方,更新慢;
**3.RestTemplate:**模拟发送Http请求,Es很多操作需要自己封装,麻烦;
4.HttpClient:同上;
**5.ElasticSearch-Rest-Client:**官方RestClient,封装了很多ES操作,
优点:API层次分明,上手简单;
缺点:

  • 很多地方需要拼接Json字符串,在java中拼接字符串有多恐怖你应该懂的
  • 需要自己把对象序列化为json存储
  • 查询到结果也需要自己反序列化为对象

6.Spring Data Elasticsearch

支持Spring的基于@Configuration的java配置方式,或者XML配置方式
提供了用于操作ES的便捷工具类ElasticsearchTemplate。包括实现文档到POJO之间的自动智能映射。
利用Spring的数据转换服务实现的功能丰富的对象映射
基于注解的元数据映射方式,而且可扩展以支持更多不同的数据格式
根据持久层接口自动生成对应实现方法,无需人工编写基本操作代码(类似mybatis,根据接口自动得到实现)。当然,也支持人工定制查询。

1-4种集成方式缺点很明显,不予考虑。第5种是es官方自带的类库,易用性较好,但仍存在一些缺点;第6种则是spring提供的服务,考虑到spring这个大家族,最终选择Spring Data Elasticsearch。

需求概述

需求上,需要实现全文搜索的对象是文档(搜索文件夹的名称意义有限,不考虑),进行全文搜索时,参与搜索的有2项信息,文档名称和文档内容,参与排序的是相关度、创建时间、更新时间。

文件内容读取

创建索引,需要读取文件内容。

文件类型

粗略考虑,能进行全文搜索的主要是office、文本、代码、pdf、markdown这几种常见格式,图片、视频、音频和压缩包不处理,需要在配置文件中明确定义,以便使用合适的读取器进行处理。
office:“docx”, “wps”, “doc”, “xls”, “xlsx”, “ppt”, “pptx”
图片:“jpg”, “jpeg”, “png”, “gif”, “bmp”, “ico”, “raw”
文本:txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd
代码:“java”, “c”, “php”, “go”, “python”, “py”, “js”, “html”, “ftl”, “css”, “lua”, “sh”, “rb”, “yml”, “json”, “h”, “cpp”, “cs”, “aspx”, “jsp”
压缩包:“rar”, “zip”, “jar”, “7-zip”, “tar”, “gzip”, “7z”
多媒体:mp3,wav,mp4
markdown:md
xml:xml
pdf:pdf
flv:flv
cad:cad

文件编码

对于文本类的文件,文件编码可能会有多种,如UTF-8,UTF-16,GB2312,GBK,GB18030等,读取内容时需要保证编码方式正确,否则会产生乱码。

可以分为两类,一类带bom,即文件前几个字节代表编码方式,另外一类则不带bom,需要根据字节内容判断
以常见的windows记事本生成的txt文件为例,使用以下代码可正确读取(已验证)

/**
	 * 判断文件的编码格式
	 * @param fileName :file
	 * @return 文件编码格式
	 * @throws Exception
	 */
	public static String codeString(File fileName) throws Exception{
		BufferedInputStream bin = new BufferedInputStream(
		new FileInputStream(fileName));
		int p = (bin.read() << 8) + bin.read();
		String code = null;
		
		switch (p) {
			case 0xefbb:
				code = "UTF-8";
				break;
			case 0xfffe:
				code = "Unicode";
				break;
			case 0xfeff:
				code = "UTF-16BE";
				break;
			default:
				code = "GBK";
		}
		IOUtils.closeQuietly(bin);
		return code;
	}

这种判断方式和原理对于不带bom的文件无能为力。

看上去是个小事,但是真要自己写,工作量非常可观……
网上找了下第三方类库,有以下几个:
cpdetector:基于统计学原理的,不保证完全正确,api使用相当繁琐

public static String getFileEncode(String filePath) {
        String charsetName = null;
        try {
            File file = new File(filePath);
            CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
            detector.add(new ParsingDetector(false));
            detector.add(JChardetFacade.getInstance());
            detector.add(ASCIIDetector.getInstance());
            detector.add(UnicodeDetector.getInstance());
            java.nio.charset.Charset charset = null;
            charset = detector.detectCodepage(file.toURI().toURL());
            if (charset != null) {
                charsetName = charset.name();
            } else {
                charsetName = "UTF-8";
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
        return charsetName;
    }

icu4j:ibm出品的,看了下最新版本是2020年12月更新的,应该还算可靠

 <dependency> 
	 <groupId>com.ibm.icu</groupId> 
	 <artifactId>icu4j</artifactId> 
	 <version>59.2</version> 
 </dependency>
    public static String getFileEncoding(File file) {
        //默认设置为utf-8
        String encoding = "utf-8";

        try {
            Path path = Paths.get(file.getPath());
            byte[] data = Files.readAllBytes(path);
            CharsetDetector detector = new CharsetDetector();
            detector.setText(data);
            CharsetMatch match = detector.detect();
            if (match != null) {
                encoding = match.getName();
            }
        } catch (IOException exception) {
            //读取文件失败,不处理异常
        }
        return encoding;
    }

拿记事本试了下,另存为几种格式,发现类库输出的判断优于上面拿bom简易判断读取,最终选择使用该类库处理。

内容解析

文本内容只要保证编码格式,直接读取即可,需要做内容解析的主要是二进制格式的pdf及office系列文档。

采用apache poi类库来读取Word、Excel和PowerPoint这三类文件,注意2003版本及以下,和2007版本及以上需要分别处理

 <!--读取Word/Excel/PowerPoint文件内容-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.17</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-scratchpad</artifactId>
            <version>3.17</version>
        </dependency>

采用apache pdf库来读取pdf内容

        <!--读取pdf文件内容-->
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>pdfbox</artifactId>
            <version>2.0.3</version>
        </dependency>

索引创建

读取文件内容,创建索引是常规操作,这块技术上没什么问题,不过从处理流程考虑,用户上传文件,系统进行存储和写库表是主流程,且必须完成,出错应给予提示,但创建索引并不属于主流程,该工作不应该占用主流程的处理时间,所以应该进行异步处理。

因此这里通过新线程来读取文件内容并创建索引。

权限控制

全文搜索组件Elasticsearch并不支持带数据权限搜索,因此只能从查询结果入手来解决,实现思路如下:
首先获取1页数据(例如,页面记录数为10),然后逐条判断当前用户是否有预览权限,如有,则加入结果列表;
判断结果列表是否已够10条,如不够,则继续读取下一页数据,当读取的记录数超出结果总数则停止。

前端默认只显示单页数据,同时不显示命中记录数,而是通过点击加载更多来加载下页数据。

同时,需考虑当前页数据之后是否还有数据,以便前端控制是否显示加载更多提示。

此处还有个棘手问题,带权限的数据拼接问题,首次查询没问题,但是后续查询,从哪里开始则是问题,相当于还需要将上次查询到哪一页记录下来,如果每次查询返回一个固定条数(如10条),则受数据权限过滤的影响,容易出现部分数据丢失的情况,例如单页数据大小是10,一共查询出28条数据,先查出10条,权限过滤后剩余了8条,再读取第2页数据,假设第11-20条数据经过权限过滤后有5条数据,则取2条补足,作为结果返回,用户此时点击了加载更多,这时候后端就面临1个麻烦,从哪个地方开始取数据,才能不重不漏,很明显上面例子产生了“半页数据”。

进一步查看es的api,发现这种类似搜索引擎的应用场景,es提供了scroll 来支持,查询结果会自动产生一个scrollId,下次查询传入该参数,则会从该记录继续往下搜索。

但是,如果每次加载相同数量的数据,还是面临半页数据的问题,因此调整实现思路,在查询结果足量情况下,每次至少返回10条数据,最多返回20条数据(主要受数据权限过滤的影响),这种实现方式,对于业务用户而言,并无多大影响。

疑难问题与解决方案

  • 什么阶段创建索引库和映射?系统启动时,判断是否已存在索引库,若不存在,则创建?

实际并不需要自己创建索引,给实体类加上注解后,es引擎会自动创建索引库以及映射

  • 应该为文件夹和文档各建立1个索引库,还是共用1个索引库,在查询环节进行合并?

各建1个,es7.x版本已经废弃了类型,每个索引库只有1个默认的类型,即_doc,只存在一种数据结构的映射
调整需求,只考虑文档的全文搜索,因此该问题将不存在

  • 注解加到已有的实体对象,还是另行定义?

暂时没发现应另行定义的必要性,先按照复用已有的实体对象处理

  • 通过注解实现的IK分词未起作用?

SpringDataElasticSearch组件自身问题,根本就未读取ES组件的Field注解属性,在系统启动完成后,使用ElasticSearchRestTemplate接口,手动创建索引。

文档预览

技术方案

文档预览是一个很棘手的问题,对于pdf格式,有较完善的解决方案,通过技术组件可以实现预览,但对于常用的office系列,使用一些第三方的技术组件将其先转换为pdf在预览,速度是一方面,转换效果也存在较大的不确定性,要想实现比较好的预览效果,则往往需要收费服务。
永中office】【office365】【idocv

通过搜索,找到一个介绍看上去不多的开源的项目https://kkfileview.keking.cn/zh-cn/docs/home.html,且基于apache 2.0开源协议。

权限控制方案

kkfileview基于rest接口,调用方式如下:
var originUrl = ‘http://127.0.0.1:8080/filedownload?fileId=1’
var previewUrl = originUrl + ‘&fullfilename=test.txt’
window.open(‘http://127.0.0.1:8012/onlinePreview?url=’+encodeURIComponent(Base64.encode(previewUrl)))

在自己实现的文档管理系统中,文档的权限进行严格控制,因此,从自己系统发起的请求,可以控制是否允许访问,但是,以上调用方式,相当于kkfileview作为请求发起方,这样就涉及到1个关键问题,如何确认这个请求合法?

方案:基于令牌方式
发起预览请求时,先验证权限,权限通过后生成令牌,传递给kkfileview,kkfileview基于该令牌向应用系统请求文件。

开源资料

系统名称:一二三文档管理系统
简介: 企事业单位一站式文档管理系统,让组织内文档管理有序,协作高效、安全可控
资料:csdn专栏
开源地址:Gitee
开源协议:MIT
欢迎收藏、点赞、评论,你的支持是我前行的动力。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
好的,我能为您提供一些关于ElasticSearch设计文档的信息。 首先,ElasticSearch是一个分布式、可扩展的搜索和分析引擎,它使用Lucene库作为其核心搜索引擎。ElasticSearch设计目标是为了实现高吞吐量和低延迟的全文搜索,同时支持结构化和非结构化数据的搜索和分析。 以下是ElasticSearch设计文档的主要内容: 1. 数据存储:ElasticSearch使用分片和副本的方式存储数据,每个分片可以存储大量的数据,并且可以在集群中的多个节点上进行复制。这种设计保证了数据的高可用性和可扩展性。 2. 数据索引:ElasticSearch使用倒排索引的方式索引数据,这种索引方式可以快速地查找和过滤数据,同时支持全文搜索、模糊搜索、短语搜索等多种搜索方式。 3. 搜索算法:ElasticSearch使用BM25算法进行搜索,该算法可以根据查询词的重要性和文档的相关性来计算文档的得分,从而得出最匹配的结果。 4. 查询语言:ElasticSearch支持多种查询语言,包括基于JSON的原始查询语言、结构化查询语言、全文搜索查询语言等,可以满足不同的搜索需求。 5. 分布式架构:ElasticSearch是一个分布式系统,可以在多个节点上运行,每个节点可以处理搜索请求和存储数据。ElasticSearch使用了一系列分布式技术,包括分片、副本、负载均衡、路由等,来保证数据的高可用性和可扩展性。 以上是ElasticSearch设计文档的基本信息,希望对您有所帮助。如果您有其他问题,可以继续问我。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学海无涯,行者无疆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值