您的位置:首页 > 其它

ElasticSearch 全文检索实战

2020-07-15 05:16 417 查看

一、概述

Elasticsearch 是一个分布式的 RESTful 风格的搜索和数据分析引擎,它基于 Lucene 实现了强大的全文检索功能。本文针对一个通用的应用场景,讲解如何利用 ElasticSearch 快速实现对关系型数据库文本和常见文档格式附件的全文检索。

二、应用场景

描述

数字图书馆有一套基于 MySQL 的电子书管理系统,电子书的基本信息保存在数据库表中,书的数字内容以多种常见的文档格式(PDF、Word、PPT、RTF、TXT、CHM、EPUB等)保存在存储系统中。现在需要利用 ElasticSearch 实现一套全文检索系统,以便用户可以通过对电子书的基本信息和数字内容进行模糊查询,快速找到相关书籍。

数据结构

数据库表 BOOK 结构:

CREATE TABLE `book` (
`id` varchar(100) NOT NULL,
`title` varchar(50) DEFAULT NULL,
`desc` varchar(1000) DEFAULT NULL,
`path` varchar(200) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
字段 意义
id 主键
title 书名
desc 介绍
path 存储路径
create_time 创建时间
update_time 更新时间

逻辑约束:创建书籍记录时,create_time 等于 update_time,即当前时间,每次更新书籍时,更新 update_time 时间。全文检索系统根据 update_time 时间更新书籍索引。

三、技术方案

基本思路就是:

  1. 定期扫描 MySQL 中的 book 表,根据字段 update_time 批量抓取最新的电子书数据。
  2. 从 path 字段获取电子书数字内容的文档存储路径。从存储系统中抓取电子书文档并进行 BASE64编码。
  3. 将从 book 表批量抓取的数据转换为 JSON 文档,并将 BASE64编码后的电子书文档合并入 JSON,一同写入 ElasticSearch,利用 ElasticSearch 的插件 Ingest Attachment Processor Plugin 对电子书文档进行文本抽取,并进行持久化,建立全文索引。

本文采用开源数据处理工具 Apache NiFi http://nifi.apache.org 来实现上述流程,具体使用方法后续实施过程会详细讲解。如果读者不了解 Apache NiFi ,也可以使用 Logstash、Kettle 等工具或者使用自己熟悉的编程语言开发应用来完成上述流程。

四、安装并初始化 ElasticSearch

安装 ElasticSearch

访问ElasticSearch官网,根据操作系统选择下载软件包,并安装
https://www.elastic.co/downloads/elasticsearch
当前最新版本是 v6.2.4

Linux/Unix 下运行 bin/elasticsearch (在windows操作系统下运行 bin\elasticsearch.bat )

ElasticSearch的默认服务端口是 9200,所有 API 都可以通过 REST 方式调用。

关于JVM内存:ElasticSearch是基于Java开发,部署需要配置合理的JVM Heap内存,官方建议分配内存不高于本机物理内存的二分之一,最好不要超过32G。具体配置方法如下:

设置环境变量 ES_HEAP_SIZE,ElasticSearch启动时会读取这个环境变量。 在命令行运行如下:

export ES_HEAP_SIZE=8g

安装中文分词插件

IK Analysis for Elasticsearch 是开源社区比较流行的中文分词插件
官网:https://github.com/medcl/elasticsearch-analysis-ik

安装方法:
在安装目录下运行

./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.2.4/elasticsearch-analysis-ik-6.2.4.zip

运行结果:

➜  elasticsearch-6.2.4 ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.2.4/elasticsearch-analysis-ik-6.2.4.zip-> Downloading https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.2.4/elasticsearch-analysis-ik-6.2.4.zip
[=================================================] 100%
-> Installed analysis-ik

安装成功后,在 plugin 文件夹下可以看到出现了 analysis-ik 文件夹。

安装附件文本抽取插件

ElasticSearch 官方提供插件:
Ingest Attachment Processor Plugin
https://www.elastic.co/guide/en/elasticsearch/plugins/current/ingest-attachment.html#ingest-attachment

此插件开箱即用,用于实现常见格式文档的文本抽取,它基于另一个开源的文本抽取工具库 Apache Tika http://tika.apache.org 实现。

安装方法:
在安装目录下运行

./bin/elasticsearch-plugin install ingest-attachment

安装过程中提示此插件需要一些额外的权限,输入

y
回车,继续安装即可,运行结果:

-> Downloading ingest-attachment from elastic
[=================================================] 100%
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@     WARNING: plugin requires additional permissions     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
* java.lang.RuntimePermission accessDeclaredMembers
* java.lang.RuntimePermission getClassLoader
* java.lang.reflect.ReflectPermission suppressAccessChecks
* java.security.SecurityPermission createAccessControlContext
* java.security.SecurityPermission insertProvider
* java.security.SecurityPermission putProviderProperty.BC
See http://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.html
for descriptions of what these permissions allow and the associated risks.

Continue with installation? [y/N]y
-> Installed ingest-attachment

安装成功后,在 plugin 文件夹下可以看到出现了 ingest-attachment 文件夹。

重新启动 ElasticSearch

所有插件安装完成后重新启动 ElasticSearch

五、文档附件的文本抽取

解决方式

ElasticSearch只能处理文本,不能直接处理二进制文档。要利用 ElasticSearch 实现附件文档的全文检索需要 2 个步骤:

  1. 对多种主流格式的文档进行文本抽取。
  2. 将抽取出来的文本内容导入 ElasticSearch ,利用 ElasticSearch强大的分词和全文索引能力。

上文安装的 Ingest Attachment Processor Plugin 是一个开箱即用的插件,使用它可以帮助 ElasticSearch 自动完成这 2 个步骤。

基本原理是利用 ElasticSearch 的 Ingest Node 功能,此功能支持定义命名处理器管道 pipeline,pipeline中可以定义多个处理器,在数据插入 ElasticSearch 之前进行预处理。而 Ingest Attachment Processor Plugin 提供了关键的预处理器 attachment,支持自动对入库文档的指定字段作为文档文件进行文本抽取,并将抽取后得到的文本内容和相关元数据加入原始入库文档。

因为 ElasticSearch 是基于 JSON 格式的文档数据库,所以附件文档在插入 ElasticSearch 之前必须进行 Base64 编码。

当然,Attachment Processor Plugin 不是唯一方案。如果需要深入定制文档抽取功能,或基于功能解耦等考量,完全可以利用 Apache Tika http://tika.apache.org 实现独立的文档抽取应用。

建立文本抽取管道

ElasticSearch 支持 REST API,我们可以用 cURL、Postman 等工具调用。为方便查看,本文使用如下这种表示方式来展示 REST 调用,请注意,它并不是可执行代码。

PUT http://localhost:9200/_ingest/pipeline/attachment

{
"description": "Extract attachment information",
"processors": [
{
"attachment": {
"field": "data",
"ignore_missing": true
}
},
{
"remove": {
"field": "data"
}
}
]
}

以上,我们建立了 1 个命名 pipeline 即 "attachment",其中定义了 2 个预处理器 "attachment" 和 "remove" ,它们按定义顺序对入库数据进行预处理。

"attachment" 预处理器即上文安装的插件 "Ingest Attachment Processor Plugin" 提供,将入库文档字段 "data" 视为文档附件进行文本抽取。要求入库文档必须将文档附件进行 BASE64编码写入 "data" 字段。

文本抽取后, 后续不再需要保留 BASE64 编码的文档附件,将其持久化到 ElasticSearch 中没有意义,"remove" 预处理器用于将其从源文档中删除。

如何使用 pipeline

按照 ElasticSearch 的 API 定义,插入文档时可以在请求地址末尾加
?pipeline=attachment 的形式指定使用上文建立的 "attachment" 命名 pipeline。

六、建立文档结构映射

ElasticSearch 是文档型数据库,以 JSON 文档为处理对象。文档结构以 mapping 形式定义,相当于关系型数据库建立表结构。以下,我们建立 MySQL 的 book 表在 ElasticSearch 中的文档结构映射。

PUT http://localhost:9200/book

{
"mappings": {
"idx": {
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"analyzer": "ik_max_word"
},
"desc": {
"type": "text",
"analyzer": "ik_max_word"
},
"path": {
"type": "keyword"
},
"create_time": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
},
"update_time": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
},
"attachment": {
"properties": {
"content": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
}
}
}

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

在本文的应用场景中,我们需要对 book 的 title、desc 和 attachment.content 进行全文检索,所以在建立 mapping 时,我们为这 3 个字段指定分析器 "analyzer" 为 "ik_max_word",以让 ElasticSearch 在建立全文索引时对它们进行中文分词。

七、安装并配置 Apache NiFi

Apache NiFi (http://nifi.apache.org) 是一个易用、强大、可靠的数据处理、分发系统。本文使用它来完成数据流转处理,如果读者使用其它工具或者自行编程开发应用,请忽略本章。

本文不是专门的 Apache NiFi 教程,只针对相关应用场景介绍如何使用 Apache NiFi。

安装 Apache NiFi

在 Apache NiFi 官网下载并解压到本地,本文当前最新版本为 1.6.0
下载地址:http://nifi.apache.org/download.html

Apache NiFi 基于 java 开发,要求运行环境为 JDK 8.0 以上。

常用配置在 conf 目录下的 nifi.properties 和 bootstrap.conf 文件中,详见:NiFi System Administrator's Guide

其中,web 控制台端口在 nifi.proerties 文件中的

nifi.web.http.port
参数修改,默认值
8080
。JVM启动参数在 bootstrap.conf 文件中,内存分配在
# JVM memory settings
段,默认
-Xms512m -Xmx512m

下载 MySQL Connector/J

因为 NiFi 需要连接 MySQL 抓取数据,请到 MySQL 官网下载 MySQL Connector/J
https://dev.mysql.com/downloads/connector/j/

本文当前最新版本 5.1.46, 将 mysql-connector-java-5.1.46-bin.jar 拷贝到 NiFi 安装目录备用。

启动 Apache NiFi

命令行进入 Apache NiFi 目录,运行命令

./bin/nifi.sh start

Apache NiFi 的常用命令:

命令 说明
run 交互式启动
start 后台启动
stop 停止
status 查看服务状态

Apache NiFi 提供图形化的 Web 管理控制台,内置丰富的功能组件,通过拖拽的方式即可建立数据处理流程, 启动以后访问 http://localhost:8080/nifi ,控制台如下图:

配置数据处理流程

篇幅有限,本文不详细讲解 Apache NiFi,如果读者有兴趣,请前往阅读官方文档:
http://nifi.apache.org/docs.html

导入模板

Apache NiFi 支持将配置好的流程保存为模板,鼓励社区开发者之间分享模板。本章及使用的流程模板已上传至开源项目:
https://gitee.com/streamone/full-text-search-in-action
模板文件在 /nifi/FullText-mysql.xml

下载模板文件 FullText-mysql.xml ,然后点击控制台左侧 "Operate" 操作栏里的 "Upload Template" 上传模板。

应用模板

拖拽控制台顶部一排组件图标中的 "Template" 到空白网格区域,在弹出的 "Add Template" 窗口中选择刚刚上传的模板 "FullText-mysql",点击 "Add"。空白网格区域将出现如下下图的 "process group",它是一组 "processor" 的集合,我们的处理流程就是由这组 "processor" 按照数据处理逻辑有序组合而成。

双击此 "process group" 进入,将看到完整的流程配置,如下图:

运行这个流程之前需要完成几个配置项:

  1. 配置并启动数据库连接池
    在空白网格处点击鼠标右键,在弹出菜单中点击 "configure",在弹出的 "FullText-mysql Configuration" 窗口中打开 "controller services" 标签页如下图,点击表格中 "DBCPConnectionPool" 右侧 "Configure" 图标,进行数据库连接池配置。
    在弹出的 "Configure Controller Service" 窗口中打开 "PROPERTIES" 标签页,在表格中填写 MySQL数据库相关信息,如下图:
    其中的 "Database Driver Location(s)" 填写我们下载的 "mysql-connector-java-5.1.46-bin.jar" 路径。 配置好数据库连接池以后点击 "APPLY" 回到 "controller services" 标签页,点击表格中 “DBCPConnectionPool” 右侧 “Enable” 图标启动数据库连接池。

  2. 修改变量
    在空白网格处点击鼠标右键,在弹出菜单中点击 "variables",打开 "Variables" 窗口,修改表格中的 "elasticSearchServer" 参数值为 ElasticSearch 服务地址,修改表格中的 "rootPath" 参数为电子书数字文档在文件系统中的根路径。

回到 "process group" 流程页面,在空白网格处点击鼠标右键,在弹出菜单中点击 "start" 菜单,启动流程。

至此,我们完成了本文应用场景中 Apache NiFi 的流程配置。Apache NiFi 每隔 10 秒扫描 MySQL 的 book 表,抓取最新的电子书数据,处理后导入 ElasticSearch。

八、全文检索查询

完成以上内容,我们应该已经将 MySQL 数据库中的电子书信息导入 ElasticSearch,并建立了全文索引。

本章应用场景中,我们想要对电子书的 "title"、"desc"、"attachment.content" (抽取文本) 进行全文检索,帮助用户快速找到关键词为 "计算" 的全部电子书。

ElasticSearch 提供 REST API,各种编程语言都可以很方便地实现客户端调用,官方提供了多种语言的 client :
https://www.elastic.co/guide/en/elasticsearch/client/index.html

本章沿用前述方式展示全文检索请求结构:

POST http://localhost:9200/book/idx/_search

{
"query": {
"multi_match": {
"query": "计算",
"fields": ["title", "desc", "attachment.content"]
}
},
"_source": {
"excludes": ["attachment.content"]
},
"from": 0, "size": 200,
"highlight": {
"encoder": "html",
"pre_tags": ["<em>"],
"post_tags": ["</em>"],
"fields": {
"title": {},
"desc": {},
"attachment.content": {}
}
}
}

我们采用 "multi_match" 进行跨多字段查询。
关于 "multi_match" 的更多信息,请前往 https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html

"_source" 用于从返回结果中将 "attachment.conent" 字段过滤掉,因为此字段是从电子书中抽取的文本,内容太大,我们不希望在列表查询中显示它。

ElasticSearch 默认是分页查询,以 "from" 和 "size" 分别表示偏移量和每页记录数。

"highlight" 是高亮配置,其中 "fields" 属性中配置的字段高亮信息都会被查询结果返回。"encoder" 是在对关键词加高亮标签之前对原文转义的方式。"pre_tags" 和 "post_tags" 是关键词高亮标签。
关于 "highlight" 的更多信息,请前往 https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-highlighting.html

请注意,这段不是可执行程序,这样写仅仅是为了方便查看。以下为对应的 cURL 调用命令:

curl -X POST \
http://localhost:9200/book/idx/_search \
-H 'Cache-Control: no-cache' \
-H 'Content-Type: application/json' \
-d '{
"query": {
"multi_match": {
"query": "计算",
"fields": ["title", "desc", "attachment.content"]
}
},
"_source": {
"excludes": ["attachment.content"]
},
"from": 0, "size": 200,
"highlight": {
"encoder": "html",
"pre_tags": ["<em>"],
"post_tags": ["</em>"],
"fields": {
"title": {},
"desc": {},
"attachment.content": {}
}
}
}'

转载于:https://my.oschina.net/streamone/blog/1825807

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: