如何用 Elasticsearch 实现 Word、PDF,TXT 文件的全文内容检索?

news2024/11/28 18:34:54

简单介绍一下需求

  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。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能。

项目地址:https://github.com/YunaiV/ruoyi-vue-pro

开发环境

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

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

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

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

Elasticsearch 主页面

基于微服务的思想,构建在 B2C 电商场景下的项目实战。核心技术栈,是 Spring Boot + Dubbo 。未来,会重构成 Spring Cloud Alibaba 。

项目地址:https://github.com/YunaiV/onemall

核心问题

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

文件上传

首先对于 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 指定分析器 analyzer 为 ik_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 对象,使用 FastJson 将 fileObj 转化为 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 */ @Test public void fileSearchTest() throws IOException { ElasticOperation elo = eloFactory.generate(); elo.search("数据库国务院计算机网络"); }

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

搜索结果

还存在的一些问题

1. 文件长度问题

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

2. 编码上的一些问题

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2022-ISCTF-部分MISC和PWN

misc 两层编码 第一层 sha256掩码爆破 第二层 base64解码找到key import string,sys from hashlib import sha256 from multiprocessing import Process from Crypto.Util.number import * from pwn import * import base64 from primefac import * context(log_leveldebug)…

【STL】容器 - set和map的使用

目录 前言 一.键值对 1.在SGI - STL中对键值对的定义: 2.make_pair 二.set 1.set的概念与注意事项 2.set的使用(常用接口) <1>.构造函数 <2>.迭代器与范围for <3>.插入和查找 <4>.删除erase <5>.计数count 三.map 1.map的概念与注…

洛谷千题详解 | P1012 [NOIP1998 提高组] 拼数【C++、Java语言】

博主主页&#xff1a;Yu仙笙 专栏地址&#xff1a;洛谷千题详解 目录 题目描述 输入格式 输出格式 输入输出样例 解析&#xff1a; C源码&#xff1a; C源码2&#xff1a; C源码3&#xff1a; Java源码&#xff1a; ---------------------------------------------------------…

element-ui upload图片上传组件使用

图片上传前端收集 数据 再调用接口发送到后端 组件标签内的参数&#xff1a; 参数说明类型可选值默认值action必选参数&#xff0c;上传的地址string——headers设置上传的请求头部object——multiple是否支持多选文件boolean——data上传时附带的额外参数object——name上传…

【数据结构】链表OJ第一篇 —— 移除链表元素 反转链表 合并两个有序链表

文章目录0. 前言1. 移除链表元素2. 反转链表3. 合并两个有序链表4. 结语0. 前言 上篇博客中&#xff0c;我们学习了实现了单链表。但是仅仅实现并不算掌握&#xff0c;所以我们需要做些题目来练习巩固。而从今天开始的几期&#xff0c;anduin 都会为大家带来链表OJ题&#xff…

在Linux环境下VScode中配置ROS、PCL和OpenCV开发环境记录

一.安装必要的插件 打开VScode&#xff0c;在开展中安装CMake、CMake Tools&#xff0c;ROS和catkin-tools插件&#xff0c;截图如下&#xff0c;安装后重新打开VScode插件生效。 二.创建ROS工作空间 在选择的路径下&#xff0c;打开终端创建工作空间&#xff0c;具体命令如下…

【概率论笔记】正态分布专题

文章目录一维正态分布多维正态分布n维正态分布二维正态分布一维正态分布 设X~N(μ,σ2)X\text{\large\textasciitilde}N(\mu,\sigma^2)X~N(μ,σ2)&#xff0c;则XXX的概率密度为f(x)12πσe−(x−μ)22σ2f(x)\frac{1}{\sqrt{2\pi}\sigma}e^{-\frac{(x-\mu)^2}{2\sigma^2}}f(…

WXML模板语法

文章目录1、数据绑定1.1 数据绑定的基本原则1.2在data中定义页面的数据1.3 Mustache语法的格式1.4 Mustache语法的应用场景1.5 算数运算2、事件绑定2.1 小程序常用的事件2.2事件对象的属性列表2.3 target和currentTarget的区别2.4 <font colorred>bindtap的语法格式2.5 在…

狗厂员工来面试本想难为一下,结果被虐得连console.log也不敢写了

这次说到的面试题是关于node服务端内存溢出的问题&#xff0c;狗厂员工来面试本想难为一下&#xff0c;现在我连console.log也不敢写了 关于这道node内存溢出的问题&#xff0c;大哥从以下几个方面讲的&#xff0c;讲完我觉得自己得到了升华&#xff0c;现在搞得连代码也快不敢…

2.24 OrCAD Cadence16.6怎么更改原理图中做好的库文件?

笔者电子信息专业硕士毕业&#xff0c;获得过多次电子设计大赛、大学生智能车、数学建模国奖&#xff0c;现就职于南京某半导体芯片公司&#xff0c;从事硬件研发&#xff0c;电路设计研究。对于学电子的小伙伴&#xff0c;深知入门的不易&#xff0c;特开次博客交流分享经验&a…

FPGA代码设计规范一些探讨

代码设计规范的重要性 经过一段的工作积累已经慢慢进入了提高和进阶的阶段&#xff0c;在这篇博客里多聊一聊在现实工作中的话题&#xff0c;比如代码规范以及如何尽快接手前人代码&#xff0c;快速定位项目问题。 显然每个FPGA工程师的设计理念和代码风格很多情况下有一些差别…

python 基于PHP+MySQL的学生成绩管理系统

学生成绩管理是每一个学校都会面临的一个问题,传统的管理模式已经明显到和时代不同步。通过我对当前学校成绩管理的需求和自己的实习经验整理出了一个能够满足大多数学校的学生成绩管理系统。本系统分为管理人员,教师和学生三种用户,每种用户各负责其一部分功能然后通过他们的整…

Web前端开发基础教程二

注释和特殊字符&#xff1a; 如果需要在html文档添加一些便于阅读和理解但又不需要显示在页面中的注释文字&#xff0c;就需要使用注释标签。 html中的注释以“<!--”开头&#xff0c;以“-->”结束或者快捷键&#xff1a;Ctrl/。 举例&#xff1a; <!-- 我想放假 …

【实战】Mysql 千万级数据表结构变更 、含脚本

一、实测结果 业务无感知&#xff0c;无死锁平滑 线上800万数据以下 直接使用 alter 新增字段 300ms左右 2000万数据&#xff0c;强制使用主键索引&#xff0c;每次查询50万数据 并插入新表 &#xff0c;耗时 20s &#xff0c;cpu 占45% 二、整体步骤 创建新表 biz_table_ne…

Vue-脚手架的创建

本篇vue3的脚手架主要是使用vue-cli进行创建&#xff0c;有网的情况下才能创建成功 文章目录一、下载node.js二、全局安装vue/cli三、使用vue-cli创建项目3.1 使用vscode打开终端3.2 创建项目3.3 创建成功四、注意事项一、下载node.js 1、打开node的官网 node官网 2、点击下方图…

Oxygen XML Editor 25.0.X Crack

XML 编辑器 Oxygen XML Editor 是完整的 XML 编辑解决方案&#xff0c;适用于 XML 开发人员和内容作者。它提供了必备的 XML 编辑工具&#xff0c;涵盖了大多数 XML 标准和技术。Oxygen XML Editor 包括 Oxygen XML Developer 和 Author 的所有功能。 特点与技术 单一来源出版 …

【树莓派不吃灰】使用frp内网穿透,实现远程访问树莓派

目录1. 前言2. frp内网穿透2.1 概述2.2 实现原理3. 开源frp项目4. 公网服务器选型5. 下载frp软件5.1 公网服务器下载frp服务器5.1.1 github选择适合服务端的版本5.1.2 公网服务器进行下载解压5.2 树莓派下载frp客户端5.2.1 github选择适合客户端的版本5.2.2 树莓派进行下载解压…

流形上的预积分(上)

预积分和流形 论文&#xff1a;IMU Preintegration on Manifold for Effificient Visual-Inertial Maximum-a-Posteriori Estimation 引言 Recent results in monocular visual-inertial navigation (VIN) have shown that optimization-based approaches outperform filteri…

免费体验CSDN云IDE使用指南

云IDE产品介绍 云IDE使用教程 免费使用地址&#xff1a;点击【云IDE】&#xff0c;即可开始创建工作空间啦~ 官方活动入口 文章目录1.免费体验CSDN云IDE使用指南1.1云IDE优点2.自己的代码在云IDE上跑起来看看如何操作2.1 克隆开源仓库2.2 创建一个空工作空间2.3 使用默认模板代…

Thread类中run和start的区别

一.认识 Thread类 中的 start() 和 run() 首先来看一张jdk-api中对于start 和 run 的描述. 二.方法的区别 下面我们简单看一下代码: // 继承Thread类 重写run() class MyThread extends Thread {Overridepublic void run() {while (true) {System.out.println("thread&q…