作者 | HENG

来源 | https://www.cnblogs.com/strongchenyu/p/13777596.html

简单介绍一下需求

  1. 能支持文件的上传,下载

  2. 要能根据关键字,搜索出文件,要求要能搜索到文件里的文字,文件类型要支持word,pdf,txt

文件上传,下载比较简单,要能检索到文件里的文字,并且要尽量精确,这种情况下很多东西就需要考虑进去了。这种情况下,我决定使用Elasticsearch来实现。

因为准备找工作刷牛客的原因,发现很多面试官都问到了Elasticsearch,再加上那时候我连Elasticsearch是什么东西都不知道,所以就决定尝试一下新东西。不得不说Elasticsearch版本更新的是真的快,前几天才使用了7.9.1,结果25号就出来了7.9.2版本。

Elasticsearch简介

Elasticsearch是一个开源的搜索文献的引擎,大概含义就是你通过Rest请求告诉它关键字,他给你返回对应的内容,就这么简单。

Elasticsearch封装了LuceneLuceneapache软件基金会一个开放源代码的全文检索引擎工具包。Lucene的调用比较复杂,所以Elasticsearch就再次封装了一层,并且提供了分布式存储等一些比较高级的功能。

基于Elasticsearch有很多的插件,我这次用到的主要有两个,一个是kibana,一个是Elasticsearch-head

  • kibana主要用来构建请求,它提供了很多自动补全的功能。

  • Elasticsearch-head主要用来可视化Elasticsearch

开发环境

首先安装ElasticsearchElasticsearch-headkibana,三个东西都是开箱即用,双击运行。需要注意的是kibana的版本要和Elasticsearch的版本对应。

Elasticsearch-headElasticsearch的可视化界面,Elasticsearch是基于Rest风格的API来操作的,有了可视化界面,就不用每次都使用Get操作来查询了,能提升开发效率。

Elasticsearch-head是使用node.js开发的,在安装过程中可能会遇到跨域的问题:Elasticsearch的默认端口是9200,而Elasticsearch-head的默认端口是9100,需要改一下配置文件,具体怎么改就不详细说啦,毕竟有万能的搜索引擎。

Elasticsearch安装完成之后,访问端口,就会出现以下界面。

核心问题

有两个需要解决的核心问题,文件上传和输入关键词查询。

文件上传

首先对于txt这种纯文本的形式来说,比较简单,直接将里面的内容传入即可。但是对于pdf,word这两种特殊格式,文件中除了文字之外有很多无关的信息,比如图片,pdf中的标签等这些信息。这就要求对文件进行预处理。

Elasticsearch5.x以后提供了名为ingest node的功能,ingest node可以对输入的文档进行预处理。如图,PUT请求进入后会先判断有没有pipline,如果有的话会进入Ingest Node进行处理,之后才会正式被处理。

Ingest Attachment Processor Plugin是一个文本抽取插件,本质上是利用了Elasticsearchingest node功能,提供了关键的预处理器attachment。在安装目录下运行以下命令即可安装。

./bin/elasticsearch-plugin install ingest-attachment

定义文本抽取管道

PUT /_ingest/pipeline/attachment
{"description": "Extract attachment information","processors": [{"attachment": {"field": "content","ignore_missing": true}},{"remove": {"field": "content"}}]
}

attachment中指定要过滤的字段为content,所以写入Elasticsearch时需要将文档内容放在content字段。

运行结果如图:

建立文档结构映射

文本文件通过预处理器上传后以何种形式存储,我们需要建立文档结构映射来定义。PUT定义文档结构映射的时候就会自动创建索引,所以我们先创建一个docwrite的索引,用于测试。

PUT /docwrite
{"mappings": {"properties": {"id":{"type": "keyword"},"name":{"type": "text","analyzer": "ik_max_word"},"type":{"type": "keyword"},"attachment": {"properties": {"content":{"type": "text","analyzer": "ik_smart"}}}}}
}

在 ElasticSearch 中增加了attachment字段,这个字段是attachment命名pipeline抽取文档附件中文本后自动附加的字段。这是一个嵌套字段,其包含多个子字段,包括抽取文本 content 和一些文档信息元数据。

同是对文件的名字name指定分析器analyzer为 ik_max_word,以让 ElasticSearch在建立全文索引时对它们进行中文分词。

测试

经过上面两步,我们进行简单的测试。因为ElasticSearch是基于JSON 格式的文档数据库,所以附件文档在插入ElasticSearch之前必须进行Base64编码。先通过下面的网站将一个pdf文件转化为base64的文本。

测试文档如图:

然后通过以下请求上传上去,我找了一个很大的pdf文件。需要指定的是我们刚创建的pipeline,结果如图所示。

原来的索引有个type类型,新版本后面会被弃用,默认的版本都是_doc

然后我们通过GET操作看看我们的文档是否上传成功。可以看到已经被解析成功。

如果不指定pipline的话,就会出现无法解析的情况。

根据结果我们看到,我们的PDF文件已经通过我们自行定义的pipline,然后才正式进入索引数据库docwrite

如果您正在学习Spring Boot,那么推荐一个连载多年还在继续更新的免费教程:http://blog.didispace.com/spring-boot-learning-2x/

关键字查询

关键字查询即对输入的文字,能进行一定的分词处理。比如说对于“数据库计算机网络我的电脑”这一串词来说,要能将其分为“数据库”,“计算机网络”,“我的电脑”三个关键词,然后分别根据关键字查询。

Elasticsearch自带了分词器,支持所有的Unicode字符,但是它只会做最大的划分,比如对于进口红酒这四个字,会被分为“进”,“口”,“红”,“酒”这四个字,这样查询出来的结果就会包括“进口”,“口红”,“红酒”

这并不是我们想要的结果。我们想要的结果是,只分为“进口”,“红酒”这两段,然后查询相应的结果。这就需要使用支持中文的分词器了。

ik分词器

ik分词器是开源社区比较流行的中文分词插件,我们首先安装ik分词器,注意以下代码不能直接使用。

./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/...这里找你的版本

ik分词器包括两种模式。

  1. ik_max_word会把中文尽可能的拆分。

  2. ik_smart会根据常用的习惯进行划分,比如"进口红酒”会被划分为“进口”,“红酒”

我们使用在查询时,指定ik分词器进行查询文档,比如对于插入的测试文档,我们使用ik_smart模式搜索,结果如图。

GET /docwrite/_search
{"query": {"match": {"attachment.content": {"query": "实验一","analyzer": "ik_smart"}}}
}

我们可以指定Elasticsearch中的高亮,来为筛选到的文字添加标签。这样的话文字前后都会被添加上标签。如图。

编码

编码使用Idea+maven的开发环境,首先导入依赖,依赖一定要与Elasticsearch的版本相对应。

导入依赖

Elstacisearch对于Java来说有两个API,我们使用的封装的比较完善的高级API

<dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.9.1</version>
</dependency>

文件上传

先建立一个与上文对应的fileObj对象

public class FileObj {String id; //用于存储文件idString name; //文件名String type; //文件的type,pdf,word,or txtString content; //文件转化成base64编码后所有的内容。
}

首先根据上文所诉,我们要先将文件以字节数组的形式读入,然后转化成Base64编码。

public FileObj readFile(String path) throws IOException {//读文件File file = new File(path);FileObj fileObj = new FileObj();fileObj.setName(file.getName());fileObj.setType(file.getName().substring(file.getName().lastIndexOf(".") + 1));byte[] bytes = getContent(file);//将文件内容转化为base64编码String base64 = Base64.getEncoder().encodeToString(bytes);fileObj.setContent(base64);return fileObj;
}

java.util.Base64已经提供了现成的函数Base64.getEncoder().encodeToString供我们使用。

接下来就可以使用Elasticsearch的API将文件上传了。

上传需要使用IndexRequest对象,使用FastJsonfileObj转化为Json后,上传。需要使用indexRequest.setPipeline函数指定我们上文中定义的pipline。这样文件就会通过pipline进行预处理,然后进入fileindex索引中。

public void upload(FileObj file) throws IOException {IndexRequest indexRequest = new IndexRequest("fileindex");//上传同时,使用attachment pipline进行提取文件indexRequest.source(JSON.toJSONString(file), XContentType.JSON);indexRequest.setPipeline("attatchment");IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);System.out.println(indexResponse);
}

文件查询

文件查询需要使用SearchRequest对象,首先我要指定对我们的关键字使用ik分词器ik_smart模式分词

SearchSourceBuilder srb = new SearchSourceBuilder();
srb.query(QueryBuilders.matchQuery("attachment.content", keyword).analyzer("ik_smart"));
searchRequest.source(srb);

之后我们就可以通过返回的Response对象获取每一个hits,之后获取返回的内容。

Iterator<SearchHit> iterator = hits.iterator();
int count = 0;
while (iterator.hasNext()) {SearchHit hit = iterator.next();
}

Elasticsearh一个非常强大的功能是文件的高亮(highlight)功能,所以我们可以设置一个highlighter,对查询到的文本进行高亮操作。

HighlightBuilder highlightBuilder = new HighlightBuilder();
HighlightBuilder.Field highlightContent = new HighlightBuilder.Field("attachment.content");
highlightContent.highlighterType();
highlightBuilder.field(highlightContent);
highlightBuilder.preTags("<em>");
highlightBuilder.postTags("</em>");
srb.highlighter(highlightBuilder);

我设置了前置<em></em>标签对对查询的结果进行包裹。这样查询到的结果中就会包含对应的结果。

多文件测试

简单的demo写好了,但是效果怎么样还需要使用多个文件进行测试。这是我的一个测试文件夹,里面下面放了各种类型的文件。

将这个文件夹里面的全部文件上传之后,使用elestacisearch-head可视化界面查看导入的文件。

搜索代码:

/*** 这部分会根据输入的关键字去查询数据库中的信息,然后返回对应的结果* @throws IOException*/@Testpublic void fileSearchTest() throws IOException {ElasticOperation elo = eloFactory.generate();elo.search("数据库国务院计算机网络");}

运行我们的demo,查询的结果如图所示。

如果您正在学习Spring Cloud,推荐一个经典教程(含Spring Cloud Alibaba):https://blog.didispace.com/spring-cloud-learning/

还存在的一些问题

1. 文件长度问题

通过测试发现,对于文本内容超过10万字的文件,elasticsearch只保留10w字,后面的就被截断了,这就需要进一步了解Elasticsearch对10w字以上的文本的支持。

2. 编码上的一些问题

我的代码中,是将文件全部读入内存之后,在进行一系列的处理,毫无疑问,必定会带来问题,比如假如是一个超出内存的超大文件,或者是若干个大文件,在实际生产环境中,文件上传就会占用服务器的相当一大部分内存和带宽,这就要根据具体的需求,做进一步的优化。

参考的内容

[1] ElasticSearch 全文检索实战

[2] 如何在 Elasticsearch 中使用 pipeline API 来对事件进行处理

[3] b站狂神说教学

[4] Elasticsearch中ik分词器的使用

往期推荐

IDEA 2021.3 正式发布:支持远程开发、故障排查、Java和Spring等多项优化改进

如何防止你的 jar 被反编译?

Jetbrains推出新一代编辑器:Fleet,网友:VS Code迎来劲敌JB Code!

免费开源、功能完善、暗黑风格,你会拒绝这款SSH工具吗?

那个当上非洲酋长的交大才子,如今怎么样了?

技术交流群

最近有很多人问,有没有读者交流群,想知道怎么加入。加入方式很简单,有兴趣的同学,只需要点击下方卡片,回复“加群“,即可免费加入我们的高质量技术交流群!

点击阅读原文,领取Spring Boot免费教程

如何实现Word、PDF,TXT文件的全文内容检索?相关推荐

  1. 如何实现Word、PDF、TXT文件的全文内容检索?

    简单介绍一下需求 能支持文件的上传,下载 要能根据关键字,搜索出文件,要求要能搜索到文件里的文字,文件类型要支持word,pdf,txt 文件上传,下载比较简单,要能检索到文件里的文字,并且要尽量精确 ...

  2. 面试官问:如何用Elasticsearch实现Word、PDF,TXT文件的全文内容检索?

    Elasticsearch简介 开发环境 核心问题 文件上传 关键字查询 编码 导入依赖 文件上传 文件查询 多文件测试 还存在的一些问题 简单介绍一下需求 能支持文件的上传,下载 要能根据关键字,搜 ...

  3. Android 读取本地Word/Pdf/Txt文件转文本输出

    毫无疑问支持.doc和.docx的只有POI 好多jar都是积分下载,要么就是官网下载,真心垃圾,jar包免费下载: 链接:https://pan.baidu.com/s/14MWWHN3cTsr0m ...

  4. Java word和txt文件转换图片

    /*** word和txt文件转换图片** @param inputStream* @return* @throws Exception*/private static List<Buffere ...

  5. Flex读取txt文件里的内容(二)

    Flex读取txt文件里的内容 自己主动生成的文件 LoadTxt-app.xml: <?xml version="1.0" encoding="utf-8&quo ...

  6. Android 读取本地txt文件中的内容

    Android 读取本地txt文件中的内容 import java.io.BufferedReader; import java.io.File; import java.io.FileInputSt ...

  7. localhost_access_log.*.txt文件含义及内容配置

    官方网址:http://tomcat.apache.org/tomcat-5.5-doc/config/valve.html 转载博客:https://blog.csdn.net/qq_3012124 ...

  8. 根据文献标题免费下载PDF格式文件的文献内容

    根据文献标题免费下载PDF格式文件的文献内容 通过文献名下载文献:较为通用,如有不能下载情况请留言. 第一步:根据文献名,查出文献的DOI. 进入https://www.crossref.org/ , ...

  9. R语言读取txt文件中的内容

        在RGui中读取.txt文件中的内容,采用函数read.table("xxx.txt",head=TRUE|FALSE); 其中head=TRUE表示含有属性的标题,hea ...

最新文章

  1. 成都计算机大专学校公办,成都设有计算机应用技术的公办大专学校
  2. audacity_如何在Audacity中快速编辑多个文件
  3. 【README2】动态规划之斐波那契数列说明重叠子问题如何解决
  4. java jvisualvm linux,从Linux JDK中发出jvisualvm时出现乱码
  5. java nio oio_Java NIO框架Netty教程(十四) Netty中OIO模型(对比NIO)
  6. mysql备份与还原,增量备份;使用ibd和frm文件恢复数据
  7. 北师大版图形的旋转二教案_北师大版三年级数学下册微课精讲+课件教案试卷(文末下载)...
  8. Windows 7的中国DNA
  9. flink读取不到文件_Flink读取本地文件
  10. excel表格如何画斜线并写字
  11. proxychains DNS解析失败问题
  12. 解决报错: ‘v-slot‘ directive must be owned by a custom element, but ‘div‘ is not
  13. 超简单PictureSelector使用,从相册中选取多张图片并显示再列表中,微信样式,解决相册全白问题,可以拖拽删除并排序
  14. 【转】GitHub 优秀的 Android 开源项目
  15. 算法导论------递归算法的时间复杂度求解
  16. C语言课设中的问题(数据写入文件)
  17. 百度地图之地图切换及复位
  18. 计算智能——感知器模型
  19. 我是如何成为一名程序员的?
  20. java 操作 cfs_Lucene 打开cfs文件 并获取数据

热门文章

  1. 图像处理-PCA人脸识别
  2. docker 查看容器占用磁盘大小
  3. python 3.9 新特性 简介
  4. linux 分区u盘 fdisk 简介
  5. linux traceroute 命令 查看路由表
  6. linux c 通过套接字获取本地远程地址信息 getsockname getpeername 简介
  7. OD里alt+F9和Ctrl+F9和shift+F9的区别
  8. 一个Demo让你掌握所有的android控件
  9. Android/Service详解/实例分析
  10. VFS文件系统结构分析 与socket