相关文章推荐
刚毅的莴苣  ·  Enable the MySQLi ...·  2 月前    · 
谈吐大方的跑步鞋  ·  公 安·  7 月前    · 
耍酷的脸盆  ·  window.location.href和w ...·  1 年前    · 

更多内容关注微信公众号:fullstack888

简单介绍一下需求

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

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

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

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

Elasticsearch 简介

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

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

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

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

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

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

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

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

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

Elasticsearch主页面

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

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

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

引用自Elastic 中国社区官方博客

Ingest Attachment Processor Plugin 是一个文本抽取插件,本质上是利用了 Elasticsearch ingest 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指定分析器analyzerik_max_word,以让ElasticSearch在建立全文索引时对它们进行中文分词。

建立文档结构

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

测试文档如图:

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

文件上传测试

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

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

文件上传结果查看

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

没有指定pipeline的情况

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

关键字查询

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

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_smart模式

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

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

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

highlight效果

编码使用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; //用于存储文件id
    String name; //文件名
    String type; //文件的type,pdf,word,or txt
    String 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写好了,但是效果怎么样还需要使用多个文件进行测试。这是我的一个测试文件夹,里面下面放了各种类型的文件。a00144f13e7bbbc42303c95ac7251307.png

原来 Elasticsearch 还可以这么理解

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

导入的文件

搜索代码:

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

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

还存在的一些问题

1. 文件长度问题

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

2. 编码上的一些问题

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

- END -

◆Redis和Mysql如何保持数据一致性

软件架构可能不是你想象的那个样子

容器与Pod到底有什么区别和联系?

如何从0到1进行电商平台订单系统的搭建?

SRE本质就是一个懂运维的资深开发

Spring Cloud应用的优雅下线与灰度发布

一文帮你彻底掌握Nginx

Redis 7.0 新功能新特性总览

漫画 | Kubernetes 如果是个水族馆

7 种提升 Spring Boot 吞吐量神技!

大型 SaaS 平台产品架构设计

代码规范&设计模式落地之路

技术交流,请加微信: jiagou6688 ,备注:Java,拉你进架构群

更多内容关注微信公众号:fullstack888简单介绍一下需求能支持文件的上传,下载要能根据关键字,搜索出文件,要求要能搜索到文件里的文字,文件类型要支持word,pdf,txt文件上传,下载比较简单,要能检索到文件里的文字,并且要尽量精确,这种情况下很多东西就需要考虑进去了。这种情况下,我决定使用Elasticsearch来实现。因为准备找工作刷牛客的原因,发现很多面...
  何为文件搜索系统?其实简单一点,就可以想象为一个带用户界面的grep,可以根据你提供的关键字查询包含该内容文件。与grep一样,该系统应该能返回包含该搜索条件的文件名,行号和具体内容等,同时应该支持高亮。与grep只支持正则表达式不一样的是,以elasticsearch为基础的搜索系统,能支持更多的搜索模式和匹配模式。当然,我们的系统肯定是比grep提供更多的功能。
ElasticSearch文件全文检索方案探讨 基于文件全文检索将文档(pdfwordtxt等)文本内容提取并写入ElasticSearch中,以便检索非结构文件数据内容. 使用Java传统poi或者文件读取效率低下,个人推荐FsCrawler可以部署监控任务进行文件的读取以及到ES的传输,并根据文件更新ES数据. 目前全文检索方案简介, 该文章主要介绍了 Ingest Attachmen...
本书涵盖了Elasticsearch的许多中高级功能,并介绍了缓存、ApacheLucene库以及监控等模块的内部运作机制。其中,还涉及一些实用案例,比如配置Elasticsearch参数、使用监控API等。 前言第1章 Elasticsearch简介11.1 Apache Lucene简介11.1.1 熟悉Luce...
Elasticsearch通常用于为字符串,数字,日期等类型的数据建立索引。但是,如果要直接为.pdf或.doc等文件建立索引并使其可搜索该怎么办?在HCM,ERP和电子商务等应用程序中有这种实时用例的需求。 在今天的这篇文章中我们来讲一下如何实现对.pdf或.doc文件的搜索。本解决方案使用于Elasticsearch 5.0以后的版本。 实现原理 我们采用如下的方法来实现把一个.p...
1、ElasticSearch 全文搜索是对非结构化数据的一种搜索方式,所谓非结构化数据是指相对于结构化数据(如数据库)来说长度不固定或无固定格式的数据,例如文档、邮件等。 对非结构化数据的搜索最常见的方式是顺序扫描法,即对整个文档从头到尾逐字匹配检索,例如Windows的文件搜索或者Linux的grep命令。这种方式适用于数据量较小的文件,当文件量过大时搜索将变得异常缓慢。另一种搜索方式全文搜索对非结构化数据按照一定的索引进行重新组织,从而达到按照索引对数据进行快速搜索的目的。 在Java中常用的全文搜索
2201_75700233: https://mp.weixin.qq.com/s?__biz=MzU3Mzg2MDkyMA==&tempkey=MTE5NF9tS3orRExpbjFXcHEwWlJCS2pSTkZyMnBzYU9wMUh6d1dQa3dyWUc3WjVMUmgwUlVGQUxneTlEdm5WQmg2SzdVY0RXSEdsbktnSTMwUkMyZi1SSF9NdFp5aWNpMTRXR0F4Q245ZDJMc19UUGpYbEp6YXZLSWRvcDhJT3pQSDZEdHpOV2hoZ0JWWldsb1JEZXFNbmdGS2VNb1VsZTZIdEcwSHFVZ3BRfn4%3D&chksm=7d39fde24a4e74f491becbf9569b9d9c726a90b860e1228b7591ef054f5a4475b9ecc0f39e53&__mpa_temp_link_flag=1&token=242678095#rd