【Elasticsearch】Elastic Stack
一、Elasticsearch 入门
1-1 术语介绍
Elasticsearch——Mysql结构对照表:
- 文档 Document
用户存储在ES中的数据文档
个人理解:相当于数据库中的一行数据,有不同的字段。
- 索引 Index
具有相同字段的文档列表组成
个人理解:由相同字段的文档组成
-
类型 Type 注:ES6.x已经取消其概念,现在默认为doc
-
节点 Node
一个Elasticsearch的运行实例,是集群的构成单元
个人理解:集群是由多个Node构成,每一个Node是Elasticsearch的运行实例
- 集群 Cluster
由一个或多个节点组成,对外提供服务
1-2 Document介绍
Document在ES中实际就是一个Json Object
- Json Object,由字段(Field)组成,常见数据类型如下
字符串
:text、keyword? - 区别 text是分词的,keyword是不分词的
? 使用:结构化搜索,全文文本搜索、聚合、排序等
数值型
:long、integer、short、byte、double、float、half_float、scaled_float? - ES提供了节省空间的数据类型,如上述:short、byte、half_float、scaled_float
? 整数类型:inyeger,long,short,byte 字段的长度越短,索引和搜索的效率越高
布尔
:boolean
二进制
:binary
范围类型
:integer_range,float_range,long_range,double_range,date_range
- 每个文档都有唯一的id标示
自行指定:我们可以指定字段中的主键为document_id
自动生成:不自动指定,es会自动生成唯一的document_id
- document in es
{
"name":"唐三",
"sex":"男"
}
Document 在ES中由field name 和 field value组成
- 在ES中每个Document都有一条MetaData
_index : 文档所在的索引名
_type : 文档所在的类型名ES6.x以后默认为doc
_id : 文档唯一id
_uid : 组合id,由 _type 和 _id 组成6.x _type不再起作用,现在 _uid 同 _id 数据是一样了
_source : 文档的原始Json数据,可以从这里获取每个字短的内容
_all : 整合所有字短内容到该字段,默认禁用现在已经不推荐使用了,针对所有字段进行分词,占磁盘空间且查询效率也不是很好
1-3 Index介绍
- 索引中存储具有相同结构的文档(Document)
每个索引都有自己的 mapping 定义,用于定义字段名称和类型
- 一个集群可以有多个索引,比如:
nginx 日志存储的时候可以按照每天生成一个索引来存储
nginx-log-2021-11-22
nginx-log-2021-11-23
nginx-log-2021-11-24
1-4 Rest API介绍
- Elasticsearch 集群对外提供Restful API
Rest - 全称 :Representational State Transfer
? 翻译:表现层状态转移,表现层其实就是资源,简单理解就是你对这个资源操作[增加、删除等]状态会发生改变
URI : 指定资源,如Index、Document等
Http Method :指明资源操作类型,如 GET获取、POST修改或更新、PUT新建、DELETE 删除 资源等
- Rest API 两种交互方式
Curl 命令行
curl -XPUT 'http://localhost:9200/employee/doc/1' -i -H "Content-Type:application/json" -d '
{
"usename":"rockybean",
"job":"software engineer"
}'
Kibana DevTools 【kibana 插件】,也可以用Postman,我比较喜欢使用postman
1-5 Index API
- ES有专门的Index API,用于创建[PUT]、更新[POST]、删除[DELETE]索引配置等
request
创建索引
PUT http://127.0.0.1:9200/test_index
#执行成功后会有 response
{
"acknowledged":"true",
"shards_acknowledged":"true",
"index":"test_index"
}
查看现有索引
GET http://127.0.0.1:9200/_cat/indices?v
1-6 Document API
-
ES 有专门的 Document API
- 创建文档 PUT
PUT http://127.0.0.1:9200/test_idex/doc/1 { "usename":"alfred", "age":1 }
这个主要是在创建索引后,添加一条一条数据,可以指定也可以不指定id
- 查询文档 GET
GET http://127.0.0.1:9200/test_index/doc/_search
{ "query": { "term": { "_id": "1" } } }
根据id查询,也可以根据字段查询
查询语句,json格式,放在http body 中发送到es
- 更新文档 POST
根据id更新
- 删除文档 DELETE
补充:批量创建文档 _bulk
- es允许一次创建多个文档,从而减少网络传输开销,提升写入速率
- endpoint 为 _bulk
#批量插入 格式如下
{"index":{"_index":"test_index","_type:"doc","_id":"3"}}{"usename":"alfred","age":"10"}
或
{"create":{"_index":"test_index","_type:"doc","_id":"4"}}{"usename":"小明","age":"11"}
#批量删除
{"delete":{"_index":"test_index","_type:"doc","_id":"3"}}
{"delete":{"_index":"test_index","_type:"doc","_id":"4"}}
#批量更新
{"update":{"_id":"2","_index":"test_index","_type":"doc"}}{"doc":{"age":"20"}}
{"update":{"_id":"3","_index":"test_index","_type":"doc"}}{"doc":{"age":"30"}}
二、Elasticsearch 倒排索引与分词
2-1 书的目录与索引
- 书本的目录 用于查看章节查找
指定章节名称 指明页数
第一章 Mysql 架构与历史 1
现在有个需求,如何快速查找 “ACID” 关键词所在的页面?
- 书的索引 用于关键词查找
指明关键词 指明页数
ab tool 51,89
ACID 6,551
这样就可以快速定位到要找的页码,然后定位到需要的内容
- 书与搜索引擎
目录页对应正排索引
索引页对应倒排索引
在搜索引擎中
- 正排索引
- 文档id到文档内容,单词的关联关系
- 这里的单词就是把文档内容做了分词后,每个单词的结果
- 文档id到文档内容,单词的关联关系
- 倒排索引
- 单词到文档id的关联关系
2-2 正排与倒排索引简介
- 正排索引
- 文档 id 到文档内容、单词的关联关系
文档ID 文档内容
? 1 elasticsearch是最流行的搜索引擎
? 2 php是世界上最好的语言
? 3 搜索引擎是如何诞生的
正排索引做了分词生成倒排索引
- 倒排索引
- 单词到文档id的关联关系
单词 文档ID列表
elasticsearch 1
流行 1
搜索引擎 1,3
php 2
世界 2
最好 2
语言 2
如何 3
诞生 3
倒排索引-查询流程:正排索引和倒排索引结合完成一个查询的全过程
- 查询包含 “搜索引擎”的文档
- 通过倒排索引获得 “搜索引擎”对应的文档id有 1 和3
- 通过正排索引查询1 和 3的完整内容
- 返回用户最终结果
2-3 倒排索引详解
-
倒排索引是搜索引擎的核心,主要包含两部分
- 单词词典 (Term Dictionary)
- 倒排列表 (Posting List)
-
单词词典(Term Dictionary)是倒排索引的重要组成
记录所有文档的单词,一般都比较大
记录单词到倒排列表的关联信息
-
单词字典的实现一半是用 B+Tree
- 下图排序采用拼音实现
倒排索引-倒排列表
- 下图排序采用拼音实现
-
倒排列表(Posting List)
记录了单词对应的文档集合,由倒排索引项(Posting)组成
- 倒排索引项(Posting)主要包含如下信息
文档id :用于获取原始信息
单词频率(TF,Term Frequency):记录guidance在文档中的出现次数,用于后续相关性算分
位置(Position):记录单词在文档中的分词位置(多个),用于做词语搜索(Phrase Query)
偏移(Offset):记录单词在文档的开始和结束位置,用于做高亮显示
- 以搜索引擎为例
文档ID 文档内容
? 1 elasticsearch是最流行的搜索引擎
? 2 php是世界上最好的语言
? 3 搜索引擎是如何诞生的
Posting List
DocId TF Position Offset
1 1 2 <18,22>
3 1 0 <0,4>
例:查找 “搜索引擎” 相关类型
查询流程:首先根据搜索的词 搜索引擎
在 Term Dictionary 找到Posting List对应的信息【Docld、TF、Position、Offset】,根据倒排索引项去正排索引拿到原始的信息,再计算相关性算分,然后进行排序返回给用户
- es存储的是一个json格式的文档,其中包含多个字段,每个字段会有自己的倒排索引
{
"usename":"rockybean",
"job":"software engineer"
}
username 倒排索引
job 倒排索引
2-4 分词介绍
- 分词
分词是指将文本转换成一系列单词(term or token )的过程,也可以叫做文本分析,在es里面称为Analysis
例子:
文本
easticsearch是最流行的搜索引擎
分词结果
elasticsearch 流行 搜索引擎
-
分词器是es中专门处理分词的组件,英文为Analyzer ,它的组成如下 分词器-调用顺序:先后如下
- Character Filters
针对原始文本进行处理,比如去除 html 特殊标记符,去除标签
- Tokenizer
将原始文本按照一定规则切分为单词
- Token Filters
针对tokenizer处理的单词就行再加工,比如转小写、删除或新增等处理
2-5 Analyze API
需要重视这个 API 如果查询的结果和你想的不一样,就可以使用这个api去排查这个结果
- es提供了一个测试分词的api接口,方便验证分词效果,endpoint 是 _analyze
可以直接指定 analyzer进行测试
可以直接指定索引中的字段进行测试
可以自定义分词器进行测试
测试
POST _analyze
{
"analyzer": "standard"
, "text": "hello world!"
}
结果
{
"tokens" : [
{
"token" : "hello",
"start_offset" : 0,
"end_offset" : 5,
"type" : "",
"position" : 0
},
{
"token" : "world",
"start_offset" : 6,
"end_offset" : 11,
"type" : "",
"position" : 1
}
]
}
- 自定义分词器analyzer
POST _analyze
{
"tokenizer":"standard",//tokenizer 指明使用哪个分词器
"filter":["lowercase"],//转换成小写
"text":"Hello World!"
}
结果
{
"tokens" : [
{
"token" : "hello",
"start_offset" : 0,
"end_offset" : 5,
"type" : "",
"position" : 0
},
{
"token" : "world",
"start_offset" : 6,
"end_offset" : 11,
"type" : "",
"position" : 1
}
]
}
2-6 自带分词器
预定义的分词器
-
Standard Analyzer
- 默认分词器
- 按照非字母切分
- 小写处理
- 默认分词器
-
Simple Analyzer 【单引号都会被干掉】
- 按照非字母切分
- 小写处理
-
Whitespace Analyzer
- 按照空格切分
-
Stop Analyzer
- Stop Word 指语气助词等修饰性的词语,比如 the、an、的、这等等
- 相比Simple Analyzer 多了Stop Word处理
-
Keyword Analyzer
- 不分词,直接将输入作为一个单词输出
-
Pattern Analyzer
- 通过正则表达式自定义分割符
- 默认是 \W+ 即非字词的符号作为分割符
-
Language Analyzer
- 提供了30+常见语言的分词
2-7 中文分词
- 难点
中文分词指的是将一个汉字序列切分成一个一个单独的词。在英文中,单词之间是以空格作为自然分界符,汉语中词没有一个形式上的分界符
上下文不同,分词结果迥异,比如交叉歧义的问题,例:
- 乒乓球拍/卖/完了
- 乒乓球/拍卖/完了
常见分词系统
-
IK
- 实现中英文单词切分,支持ik_smart、ik_maxword等模式
- 可自定义词库,支持热更新分词词典
-
jieba
- python中最流行的分词系统,支持分词和词性标注
- 支持繁体分词、自定义词典、并行分词等
-
基于自然语言处理的分词系统
- Hanlp
- 由一系列模型与算法组成的Java工具包,目标是普及自然语言处理在生产环境中的应用
- THULAC
- 由清华大学自然语言处理与社会人文计算实验室研制推出的yitao中文词法分析工具包,具有中文分词和词性标注功能
- Hanlp
2-8 自定义分词器之 CharacterFilter
-
当自带的分词无法满足需求时,可以自定义分词
- 通过自定义Character Filters 、Tokenizer 和 Token Filter实现
-
Character Filters
- 在Tokenizer 之前对原始文本进行处理,比如增加、删除、替换字符等
- 自带如下
- HTML Strip 去除 html 标签和转换 html实体
- Mapping 进行字符替换操作
- Pattern Repalce 进行正则匹配转换
- 会影响后续tokenizer解析的postion 和 offset信息
例:
POST _analyze
{
"tokenizer":"keyword",
"char_filter":["html_strip"],
"text":"I'm so happy!
"
}
输出结果:
{
"tokens" : [
{
"token" : """
I'm so happy!
""",
"start_offset" : 0,
"end_offset" : 32,
"type" : "word",
"position" : 0
}
]
}
2-9 自定义分词之 Tokenizer
- Tokenizer
- 将原始文本按照一定规则切分为单词(term or token)
- 自带如下
- Standard 按照单词进行分割
- letter 按照非字符类进行分割
- Whitespace 按照空格进行分割
- UAX URL Email 按照 standard 分割,但不会分割邮箱和url
- NGram 和 Edge NGram 连词分割
- Path Hierarchy 按照文件路径进行分割
例:
POST _analyze
{
"tokenizer":"path_hierarchy",
"text":"/one/two/three"
}
输出
{
"tokens" : [
{
"token" : "/one",
"start_offset" : 0,
"end_offset" : 4,
"type" : "word",
"position" : 0
},
{
"token" : "/one/two",
"start_offset" : 0,
"end_offset" : 8,
"type" : "word",
"position" : 0
},
{
"token" : "/one/two/three",
"start_offset" : 0,
"end_offset" : 14,
"type" : "word",
"position" : 0
}
]
}
2-10 自定义分词之 TokenFilter
- Token Filters
- 对于tokenizer输出的单词(term)进行增加、删除、修改等操作
- 自带的如下
- lowercase 将所有term 转换为小写
- stop 删除stop words
- NGram 和Edge Ngram 连词分割
- Synonym 添加近义词的term
例
POST _analyze
{
"text":"a Hello,world!",
"tokenizer":"standard",
"filter":[
"stop",
"lowercase",
{
"type":"ngram",
"min_gram":4,
"max_gram":4
}
]
}
输出
{
"tokens" : [
{
"token" : "hell",
"start_offset" : 2,
"end_offset" : 7,
"type" : "",
"position" : 1
},
{
"token" : "ello",
"start_offset" : 2,
"end_offset" : 7,
"type" : "",
"position" : 1
},
{
"token" : "worl",
"start_offset" : 8,
"end_offset" : 13,
"type" : "",
"position" : 2
},
{
"token" : "orld",
"start_offset" : 8,
"end_offset" : 13,
"type" : "",
"position" : 2
}
]
}
2-11 自定义分词
- 自定义分词的 api
- 自定义分词需要在索引的配置中设定
分词配置,可以自定义 char_filter、tokenizer、filter、analyzer
PUT test_index #索引名
{
"settings":{
"analysis":{
"char_filter":{},
"tokenizer":{},
"filter":{},
"analuzer":{}
}
}
}
- 自定义分词器 Custom Analyzer
Character Filters
- HTML Strip
Tokenizer
- Standard
Token Filters
- Lower case
- ASSCII Folding
自定义分词器一
PUT test_index_1
{
"settings":{
"analysis":{
"analyzer": {
"my_custom_analyzer":{
"type":"custom",
"tokenizer":"standard",
"char_filter":[
"html_strip"
],
"filter":[
"lowercase",
"asciifolding"
]
}
}
}
}
}
结果
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "test_index_1"
}
自定义分词验证
POST test_index_1/_analyze
{
"analyzer": "my_custom_analyzer",
"text": "Is this a box ?"
}
结果
{
"tokens" : [
{
"token" : "is",
"start_offset" : 0,
"end_offset" : 2,
"type" : "",
"position" : 0
},
{
"token" : "this",
"start_offset" : 3,
"end_offset" : 7,
"type" : "",
"position" : 1
},
{
"token" : "a",
"start_offset" : 11,
"end_offset" : 12,
"type" : "",
"position" : 2
},
{
"token" : "box",
"start_offset" : 13,
"end_offset" : 16,
"type" : "",
"position" : 3
}
]
}
2-12 分词使用说明
-
分词会在如下两个时机使用
- 创建或更新文档时(Index Time),会对相应的文档进行分词处理
- 查询时(Search Time),会对查询语句进行分词
-
索引时分词是通过配置Index Mapping 中每个字段的analyzer 属性实现的
- 不指定分词时,使用默认standard
PUT test_index
{
"mappings":{
"doc":{
"properties":{
"title":{
"type":"text",
"analyzer":"whitespace"
}
}
}
}
}