46-Elasticsearch-文档分析:
文档分析
分析 包含下面的过程:
将一块文本分成适合于倒排索引的独立的 词条
将这些词条统一化为标准格式以提高它们的“可搜索性”,或者 recall
分析器执行上面的工作。分析器实际上是将三个功能封装到了一个包里:
1)字符过滤器
首先,字符串按顺序通过每个 字符过滤器 。他们的任务是在分词前整理字符串。一个
字符过滤器可以用来去掉 HTML,或者将 & 转化成 and。
2)分词器
其次,字符串被 分词器 分为单个的词条。一个简单的分词器遇到空格和标点的时候,
可能会将文本拆分成词条。
3)Token 过滤器
最后,词条按顺序通过每个 token 过滤器 。这个过程可能会改变词条(例如,小写化
Quick ),删除词条(例如, 像 a, and, the 等无用词),或者增加词条(例如,像 jump
和 leap 这种同义词)。
内置分析器
Elasticsearch 还附带了可以直接使用的预包装的分析器。看看每个分析器会从下面的字符串得到哪些词条:
“Set the shape to semi-transparent by calling set_trans(5)”
1)标准分析器
标准分析器是 Elasticsearch 默认使用的分析器。它是分析各种语言文本最常用的选择。
它根据 Unicode 联盟 定义的 单词边界 划分文本。删除绝大部分标点。最后,将词条小写。
它会产生:
set, the, shape, to, semi, transparent, by, calling, set_trans, 5
2)简单分析器
简单分析器在任何不是字母的地方分隔文本,将词条小写。它会产生:
set, the, shape, to, semi, transparent, by, calling, set, trans
3)空格分析器
空格分析器在空格的地方划分文本。它会产生:
Set, the, shape, to, semi-transparent, by, calling, set_trans(5)
4)语言分析器
特定语言分析器可用于 很多语言。它们可以考虑指定语言的特点。例如, 英语 分析
器附带了一组英语无用词(常用单词,例如 and 或者 the ,它们对相关性没有多少影响),
它们会被删除。 由于理解英语语法的规则,这个分词器可以提取英语单词的 词干 。
英语 分词器会产生下面的词条:
set, shape, semi, transpar, call, set_tran, 5
注意看 transparent、 calling 和 set_trans 已经变为词根格式
分析器使用场景
当我们 索引 一个文档,它的全文域被分析成词条以用来创建倒排索引。 但是,当我
们在全文域 搜索 的时候,我们需要将查询字符串通过 相同的分析过程 ,以保证我们搜索
的词条格式与索引中的词条格式一致。
全文查询,理解每个域是如何定义的,因此它们可以做正确的事:
- 当你查询一个 全文 域时, 会对查询字符串应用相同的分析器,以产生正确的搜
索词条列表。
- 当你查询一个 精确值 域时,不会分析查询字符串,而是搜索你指定的精确值
测试分析器
以使用 analyze API 来看文本是如何被分析的。
http://192.168.1.102:9200/_analyze
{
"analyzer": "standard",
"text": "Text to analyze"
}
结果
{
"tokens": [
{
"token": "text",
"start_offset": 0,
"end_offset": 4,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "to",
"start_offset": 5,
"end_offset": 7,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "analyze",
"start_offset": 8,
"end_offset": 15,
"type": "<ALPHANUM>",
"position": 2
}
]
}
token 是实际存储到索引中的词条。 position 指明词条在原始文本中出现的位置。
start_offset 和 end_offset 指明字符在原始字符串中的位置
指定分析器
当Elasticsearch在你的文档中检测到一个新的字符串域,它会自动设置其为一个全文 字 符串 域,使用 标准 分析器对它进行分析。你不希望总是这样。可能你想使用一个不同的 分析器,适用于你的数据使用的语言。有时候你想要一个字符串域就是一个字符串域—不使用分析,直接索引你传入的精确值,例如用户 ID 或者一个内部的状态域或标签。要做到这 一点,我们必须手动指定这些域的映射。
IK 分词器
首先我们通过 Postman 发送 GET 请求查询分词效果
{
"text":"测试单词"
}
结果
{
"tokens": [
{
"token": "测",
"start_offset": 0,
"end_offset": 1,
"type": "<IDEOGRAPHIC>",
"position": 0
},
{
"token": "试",
"start_offset": 1,
"end_offset": 2,
"type": "<IDEOGRAPHIC>",
"position": 1
},
{
"token": "单",
"start_offset": 2,
"end_offset": 3,
"type": "<IDEOGRAPHIC>",
"position": 2
},
{
"token": "词",
"start_offset": 3,
"end_offset": 4,
"type": "<IDEOGRAPHIC>",
"position": 3
}
]
}
采用 IK 中文分词器,下载地址为:
https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.8.0
将解压后的后的文件夹放入 ES 根目录下的 plugins 目录下,重启 ES 即可使用。
我们这次加入新的查询参数"analyzer":“ik_max_word”
1、上传解压到es的plugins下。
http://192.168.1.102:9200/_analyze
{
"text":"测试单词",
"analyzer":"ik_max_word"
}
结果
{
"tokens": [
{
"token": "测试",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 0
},
{
"token": "单词",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 1
}
]
}
ik_max_word:会将文本做最细粒度的拆分
ik_smart:会将文本做最粗粒度的拆分
ES 中也可以进行扩展词汇,首先查询
{
"text":"弗雷尔卓德",
"analyzer":"ik_max_word"
}
仅仅可以得到每个字的分词结果,我们需要做的就是使分词器识别到弗雷尔卓德也是一个词
语
{
"tokens": [
{
"token": "弗",
"start_offset": 0,
"end_offset": 1,
"type": "CN_CHAR",
"position": 0
},
{
"token": "雷",
"start_offset": 1,
"end_offset": 2,
"type": "CN_CHAR",
"position": 1
},
{
"token": "尔",
"start_offset": 2,
"end_offset": 3,
"type": "CN_CHAR",
"position": 2
},
{
"token": "卓",
"start_offset": 3,
"end_offset": 4,
"type": "CN_CHAR",
"position": 3
},
{
"token": "德",
"start_offset": 4,
"end_offset": 5,
"type": "CN_CHAR",
"position": 4
}
] }
首先进入 ES 根目录中的 plugins 文件夹下的 ik 文件夹,进入 config 目录,创建 custom.dic
文件,写入弗雷尔卓德。同时打开 IKAnalyzer.cfg.xml 文件,将新建的 custom.dic 配置其中,
重启 ES 服务器
自定义分析器
虽然 Elasticsearch 带有一些现成的分析器,然而在分析器上 Elasticsearch 真正的强大之
处在于,你可以通过在一个适合你的特定数据的设置之中组合字符过滤器、分词器、词汇单
元过滤器来创建自定义的分析器。在 分析与分析器 我们说过,一个 分析器 就是在一个包
里面组合了三种函数的一个包装器, 三种函数按照顺序被执行:
字符过滤器
字符过滤器 用来 整理 一个尚未被分词的字符串。例如,如果我们的文本是 HTML 格
式的,它会包含像
或者
们可以使用 html 清除 字符过滤器 来移除掉所有的 HTML 标签,并且像把 Á 转换
为相对应的 Unicode 字符 Á 这样,转换 HTML 实体。一个分析器可能有 0 个或者多个字符
过滤器。
分词器
一个分析器 必须 有一个唯一的分词器。 分词器把字符串分解成单个词条或者词汇单
元。 标准 分析器里使用的 标准 分词器 把一个字符串根据单词边界分解成单个词条,并
且移除掉大部分的标点符号,然而还有其他不同行为的分词器存在。
例如, 关键词 分词器 完整地输出 接收到的同样的字符串,并不做任何分词。 空格 分词
器 只根据空格分割文本 。 正则 分词器 根据匹配正则表达式来分割文本 。
词单元过滤器
经过分词,作为结果的 词单元流 会按照指定的顺序通过指定的词单元过滤器 。
词单元过滤器可以修改、添加或者移除词单元。我们已经提到过 lowercase 和 stop 词过滤
器 ,但是在 Elasticsearch 里面还有很多可供选择的词单元过滤器。
词干过滤器 把单词 遏 制 为 词干。 ascii_folding 过滤器移除变音符,把一个像 “très” 这样的词转换为 “tres” 。ngram 和 edge_ngram 词单元过滤器 可以产生 适合用于部分匹配或者自动补全的词单元。
接下来,我们看看如何创建自定义的分析器:
测试演示
创建索引
put http://192.168.1.102:9200/my_index
{
"settings": {
"analysis": {
"char_filter": {
"&_to_and": {
"type": "mapping",
"mappings": [ "&=> and "]
}},
"filter": {
"my_stopwords": {
"type": "stop",
"stopwords": [ "the", "a" ]
}},
"analyzer": {
"my_analyzer": {
"type": "custom",
"char_filter": [ "html_strip", "&_to_and" ],
"tokenizer": "standard",
"filter": [ "lowercase", "my_stopwords" ]
}}
}}}
结果
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "my_index"
}
索引被创建以后,使用 analyze API 来 测试这个新的分析器
GET http://192.168.1.102:9200/my_index/_analyze
{
"text":"The quick & brown fox",
"analyzer": "my_analyzer"
}
结果
{
"tokens": [
{
"token": "quick",
"start_offset": 4,
"end_offset": 9,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "and",
"start_offset": 10,
"end_offset": 11,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "brown",
"start_offset": 12,
"end_offset": 17,
"type": "<ALPHANUM>",
"position": 3
},
{
"token": "fox",
"start_offset": 18,
"end_offset": 21,
"type": "<ALPHANUM>",
"position": 4
}
]
}
文档处理
** 文档冲突
使用 index API 更新文档 ,可以一次性读取原始文档,做我们的修改,然后重
新索引 整个文档 。 最近的索引请求将获胜:无论最后哪一个文档被索引,都将被唯一存
储在 Elasticsearch 中。如果其他人同时更改这个文档,他们的更改将丢失。
很多时候这是没有问题的。也许我们的主数据存储是一个关系型数据库,我们只是将数
据复制到 Elasticsearch 中并使其可被搜索。 也许两个人同时更改相同的文档的几率很小。
或者对于我们的业务来说偶尔丢失更改并不是很严重的问题。
但有时丢失了一个变更就是 非常严重的 。网上商城商品库存的数量, 每次我们卖一个商品的时候,我们在 Elasticsearch 中将库存数量减少。有一天,管理层决定做一次促销。突然地,我们一秒要卖好几个商品。 假设有两个 web 程序并行运行,每一个都同时处理所有商品的销售
web_1 对 stock_count 所做的更改已经丢失,因为 web_2 不知道它的 stock_count 的
拷贝已经过期。 结果我们会认为有超过商品的实际数量的库存,因为卖给顾客的库存商品
并不存在,我们将让他们非常失望。
变更越频繁,读数据和更新数据的间隙越长,也就越可能丢失变更。
在数据库领域中,有两种方法通常被用来确保并发更新时变更不会丢失:
悲观并发控制
这种方法被关系型数据库广泛使用,它假定有变更冲突可能发生,因此阻塞访问资源以
防止冲突。 一个典型的例子是读取一行数据之前先将其锁住,确保只有放置锁的线程能够
对这行数据进行修改。
乐观并发控制
Elasticsearch 中使用的这种方法假定冲突是不可能发生的,并且不会阻塞正在尝试的操
作。 然而,如果源数据在读写当中被修改,更新将会失败。应用程序接下来将决定该如何
解决冲突。 例如,可以重试更新、使用新的数据、或者将相关情况报告给用户。
2** 乐观并发控制
Elasticsearch 是分布式的。当文档创建、更新或删除时, 新版本的文档必须复制到集
群中的其他节点。Elasticsearch 也是异步和并发的,这意味着这些复制请求被并行发送,并
且到达目的地时也许 顺序是乱的 。 Elasticsearch 需要一种方法确保文档的旧版本不会覆
盖新的版本。
当讨论 index ,GET 和 delete 请求时,我们指出每个文档都有一个 _version (版本)号,当文档被修改时版本号递增。 Elasticsearch 使用这个 version 号来确保变更 以正确顺序得到执行。如果旧版本的文档在新版本之后到达,它可以被简单的忽略。 我们可以利用 version 号来确保 应用中相互冲突的变更不会导致数据丢失。我们通过 指定想要修改文档的 version 号来达到这个目的。 如果该版本不是当前版本号,我们的请 求将会失败。 老的版本 es 使用 version,但是新版本不支持了,会报下面的错误,提示我们用 if_seq_no 和 if_primary_term
{
"error": {
"root_cause": [
{
"type": "action_request_validation_exception",
"reason": "Validation Failed: 1: internal versioning can not be used
for optimistic concurrency control. Please use `if_seq_no` and `if_primary_term`
instead;"
}
],
"type": "action_request_validation_exception",
"reason": "Validation Failed: 1: internal versioning can not be used for
optimistic concurrency control. Please use `if_seq_no` and `if_primary_term`
instead;"
},
"status": 400
}
Kibana 入门操作。
Kibana 是一个免费且开放的用户界面,能够让你对 Elasticsearch 数据进行可视化,并
让你在 Elastic Stack 中进行导航。你可以进行各种操作,从跟踪查询负载,到理解请求如
何流经你的整个应用,都能轻松完成。
下载地址:https://artifacts.elastic.co/downloads/kibana/kibana-7.8.0-windows-x86_64.zip
-
解压缩下载的 zip 文件
-
修改 config/kibana.yml 文件
# 默认端口 server.port: 5601 # ES 服务器的地址 elasticsearch.hosts: ["http://localhost:9200"] # 索引名 kibana.index: ".kibana" # 支持中文 i18n.locale: "zh-CN"
-
Windows 环境下执行 bin/kibana.bat 文件
-
4、通过浏览器访问 : http://localhost:5601