【笔记】Elasticsearch学习笔记

前言

Elasticsearch学习笔记

概念

  • 文档(document):每条数据就是一个文档
  • 词条(term):文档中按照语意分开后的词语
  • 索引(index):相同类型文档的集合
  • 映射(mapping):索引对文档字段的约束,类似于数据表的结构约束

Elasticsearch中的概念

MySQL与Elasticsearch的概念对比

Elasticsearch中的概念 说明 MySQL
Index 索引,相同类型文档的集合。类似于MySQL的数据表 Table
Document 文档,每条数据就是一个文档,文档都是JSON格式字符串。类似于与MySQL的行 Row
Field 字段,JSON文档中的各个key。类似于MySQL的列 Column
Mapping 映射,JSON文档中各个value的约束。类似于MySQL的表结构 Schema
DSL JSON风格的请求语句,用来操作Elasticsearch实现CRUD的语句。类似于MySQL的SQL语句 SQL

ik分词器

添加扩展词

  • 除了内置的词库,ik分词器还可以根据扩展词库进行分词

  • 创建/plugins/ik/config/ext.dic文件,文件内每行指定一条扩展词

添加停止词

  • 如果定义了停止词库,ik分词器在遇到扩展词时将不会分词

  • 创建/plugins/ik/config/stopword.dic文件,文件内每行指定一条停止词

分词操作

"analyzer": "":指定分词的方式

standard:标准方式。逐字分词
chinese:内置的中文分词。逐字分词
ik_smart:ik分词器最少词
ik_max_word:ik分词器最多词

"text": "":指定分词的内容

rest-api.http
1
2
3
4
5
6
7
POST /_analyze
Content-Type: application/json

{
"analyzer": "standard",
"text": ""
}

在指定索引库中进行分词操作

rest-api.http
1
2
3
4
5
6
7
POST /索引库名/_analyze
Content-Type: application/json

{
"analyzer": "standard",
"text": ""
}

对索引库的操作

新增索引库

  • 在Elasticsearch中id通常定义为keyword类型

type: "":字段数据类型

字符串

text:可分词的文本
keyword:精确值,不能被拆开的词

数值

long
integer
short
byte
double
float

布尔

boolean

日期

date

数组

elasticsearch中没有数组类型,但是任何数据类型都可以指定多个值

地理坐标

geo_point: "3.1415926, 3.1415926":地理上的一个点
geo_shape: "LINESTARING(第一个点经度 第一个点纬度, 第二个点经度 第二个点纬度)":地理上由多个点组成的一个范围

自动补全

completion:允许通过前缀关键字模糊搜索

index: "":是否创建倒排索引。如果字段参与搜索,才需要创建倒排索引;如果不需要搜索,就不用创建倒排索引

true:缺省值,创建倒排索引
false:不创建倒排索引

analyzer: "":指定使用哪种分词器

standard:标准方式。逐字分词
chinese:内置的中文分词。逐字分词
ik_smart:ik分词器最少词
ik_max_word:ik分词器最多词

properties: {"": "", "": ""}:字段的子字段
copy_to: "其他字段名":将当前字段的值拷贝到其他字段(底层只是创建了一个索引,并不是真的拷贝了一份),这样当搜索其他字段时,当前字段的值也会被搜索到

rest-api.http
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
PUT /索引库名
Content-Type: application/json

{
"mappings": {
"properties": {
"字段名": {
"type": "字段类型"
},
"id": {
"type": "keyword",
"index": false
},
"name": {
"type": "text",
"analyzer": "ik_smart"
},
"engilshname": {
"type": "text",
"copy_to": "name"
},
"father": {
"properties": {
"son": {
"type": "keyword"
}
}
}
}
}
}

指定自定义分词器

  • 将pinyin分词器重新配置后,作为过滤器,与ik分词器组合成新的自定义分词器
  • 创建索引库时,指定新增文档时使用的分词器为自定义分词器,指定查询文档时使用的分词器为ik分词器

character filter:在分词之间对原文档做处理
tokenizer:将文本按照指定的规则或分词器切成词条term,如果不分词就指定为keyword
filter:将tokenizer输出的词条交给指定过滤器或分词器作进一步处理

"analyzer": "":指定创建时使用的分词器
"search_analyzer": "":指定搜索时使用的分词器

rest-api.http
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
PUT /索引库名
Content-Type: application/json

{
"settings": {
"analysis": {
"analyzer": {
"自定义分词器名": {
"tokenizer": "ik_max_word",
"filter": "过滤器名"
}
},
"filter": {
"过滤器名": {
"type": "pinyin",
"keep_full_pinyin": false,
"keep_joined_full_pinyin": true,
"keep_original": true,
"limit_first_letter_length": 16,
"remove_duplicated_term": true,
"none_chinese_pinyin_tokenize": false
}
},
"mappings": {
"properties": {
"字段名": {
"type": "字段类型",
"analyzer": "自定义分词器名",
"search_analyzer": "ik_smart"
}
}
}
}
}
}

查询索引库

rest-api.http
1
2
GET /索引库名
Accept: application/json

删除索引库

1
DELETE /索引库名

修改索引库

  • 索引库不允许修改旧字段,只能添加新字段
rest-api.http
1
2
3
4
5
6
7
8
9
10
PUT /索引库名/_mapping
Content-Type: application/json

{
"properties": {
"新字段名": {
"type": "字段类型"
}
}
}

对文档操作

新增文档

文档id:如果不指定文档id,则索引库会自动随机生成一个id

rest-api.http
1
2
3
4
5
6
7
8
9
POST /索引库名/_doc/文档id
Content-Type: application/json

{
"key": "value",
"key": {
"key": "value"
}
}
  • 结果中result如果为created表示新增成功
  • 每次写操作都会导致version自增

删除文档

1
DELETE /索引库名/_doc/文档id
  • 结果中result如果为deleted表示删除成功

删除所有文档

1
2
3
4
5
6
7
8
POST /索引库名/_delete_by_query
Content-Type: application/json

{
"query": {
"match_all": {}
}
}

修改文档

全量修改

  • 先通过文档id查询旧文档
    • 如果查询到了旧文档,就删除旧文档,新增新文档
    • 如果没有查询到旧文档,就直接新增文档
rest-api.http
1
2
3
4
5
6
7
8
9
PUT /索引库名/_doc/文档id
Content-Type: application/json

{
"key": "value"
"key": {
"key": "value"
}
}
  • 结果中result如果为updated表示全量修改成功
  • 结果中result如果为created表示新增成功

局部修改

  • 只修改某个字段的值
rest-api.http
1
2
3
4
5
6
7
8
POST /索引库名/_update/文档id
Content-Type: application/json

{
"doc": {
"字段名": "字段值"
}
}
  • 结果中result如果为updated表示局部修改成功

查询文档

rest-api.http
1
2
GET /索引库名/_doc/文档id
Accept: application/json
  • 结果中found如果为true表示查询成功,如果为false表示查询失败
  • 如果查询成功,数据会存在_source

搜索文档的操作

rest-api.http
1
2
3
4
5
6
7
8
9
10
POST /索引库名/_search
Content-Type: application/json

{
"query": {
"查询类型": {
"查询条件": "查询值"
}
}
}

简单查询

查询所有

  • 查询所有默认返回的结果并不是全部结果,只会返回部分结果
rest-api.http
1
2
3
4
5
6
7
8
POST /索引库名/_search
Content-Type: application/json

{
"query": {
"match_all": {}
}
}
查看分片信息

"explain": true:列出信息存储的节点位置

rest-api.http
1
2
3
4
5
6
7
8
9
POST /索引库名/_search
Content-Type: application/json

{
"explain": true,
"query": {
"match_all": {}
}
}

全文检索

根据指定的单个字段全文检索
  • 只要指定字段的关键字中,任意子关键字满足条件,即可查询出
rest-api.http
1
2
3
4
5
6
7
8
9
10
POST /索引库名/_search
Content-Type: application/json

{
"query": {
"match": {
"字段名": "关键字"
}
}
}
根据指定的多个字段全文检索
  • 只要多个字段的关键字中,任意子关键字满足条件,即可查询出
  • 多个字段全文检索比单个字段全文检索效率更低,推荐将字段的集合复制为一个单独字段all进行单字段检索

"query": "":指定关键字
"fields": ["", ""]:指定字段名

rest-api.http
1
2
3
4
5
6
7
8
9
10
11
POST /索引库名/_search
Content-Type: application/json

{
"query": {
"multi_match": {
"query": "关键字",
"fields": ["字段名", "字段名"]
}
}
}

精确查询

完全匹配
  • 指定字段的值必须与关键字完全匹配时,才能查询出
rest-api.http
1
2
3
4
5
6
7
8
9
10
11
12
POST /索引库名/_search
Content-Type: application/json

{
"query": {
"term": {
"字段名": {
"value": "关键字"
}
}
}
}
匹配数值范围
  • 指定字段的值必须属于范围内时,才能查询出

gt:大于
gte:大于等于
lt:小于
lte:小于等于

rest-api.http
1
2
3
4
5
6
7
8
9
10
11
12
13
POST /索引库名/_search
Content-Type: application/json

{
"query": {
"range": {
"字段名": {
"gte": "最小值",
"lte": "最大值"
}
}
}
}

地理查询

矩形范围
  • 指定矩形左上角和右下角的坐标,查询矩形范围内的所有文档

top_left:矩形左上角的坐标

lat:经度
lon:纬度

bottom_right:矩形右下角的坐标

lat:经度
lon:纬度

rest-api.http
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
POST /索引库名/_search
Content-Type: application/json

{
"query": {
"geo_bounding_box": {
"字段名": {
"top_left": {
"lat": 1.1,
"lon": 1.1
},
"bottom_rignt": {
"lat": 1.1,
"lon": 1.1
}
}
}
}
}
圆形范围
  • 指定圆心和半径,查询圆形范围内的所有文档

"distance": "":指定半径距离
"中心点坐标": "经度,纬度":指定中心点坐标

rest-api.http
1
2
3
4
5
6
7
8
9
10
11
POST /索引库名/_search
Content-Type: application/json

{
"query": {
"geo_distance": {
"distance": "15km",
"中心点坐标": "经度,纬度"
}
}
}

复合查询

布尔查询

  • 布尔查询是把多个简单查询语句进行组合,形成复合查询语句

must:(与)必须匹配所有子查询
should:(或)选择性匹配子查询
must_not:(非)必须不匹配所有子查询,不参与算分
filter:必须匹配,不参与算分

rest-api.http
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
POST /索引库名/_search
Content-Type: application/json

{
"query": {
"bool": {
"must": [
{}
],
"should": [
{}
],
"must_not": [
{}
],
"filter": [
{}
]
}
}
}

算分函数查询

  • Elasticsearch 5.0之前默认采用TF-IDF算法,会随着词频增加而无限增加
  • Elasticsearch 5.0之后默认采用BM25算法,会随着词频增加而趋于水平
有加分规则
  • 为了能人工干预算法默认的分数,可以采用Function Score Query
    • 通过query获取查询的文档
    • 通过filter过滤,得到需要加分的文档
    • 通过运算函数,将运算函数的结果与原始的分进行组合运算
    • 通过boost_mode指定运算的方式

function_score:通过算分函数重新算分

"query": {}:初始条件,通过简单查询添加条件,得到原始得分

"filter": {}:过滤条件,通过简单查询添加条件
运算函数

weight:给定一个常量值作为函数结果
field_value_factor:用文档的某个字段作为函数结果
random_score:用一个随机数作为函数结果
script_score:自定义一个公式,公式结果作为函数结果

boost_mode:指定算分方式

multiply:缺省值,将原始得分和额外分相乘
add:将原始得分和额外分相加
avg:将原始得分和额外分取平均值
max:将原始得分和额外分取最大值
min:将原始得分和额外分取最小值
replace:将额外分数替换原始分数

rest-api.http
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /索引库名/_search
Content-Type: application/json

{
"query": {
"function_score": {
"query": {},
"functions": [
"filter": {}
"weight": 10
],
"boost_mode": "multiply"
}
}
}
  • 返回的数据中,_score表示最终分数,分数越高排名越靠前
没有加分规则
  • 没有加分规则实际上就是不进行加分
rest-api.http
1
2
3
4
5
6
7
8
9
10
POST /索引库名/_search
Content-Type: application/json

{
"query": {
"function_score": {
"query": {}
}
}
}

搜索结果处理

排序

  • 默认排序规则是按照相关度算分排序

order:指定排序规则

asc:升序
desc:降序

rest-api.http
1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /索引库名/_search
Content-Type: application/json

{
"query": {},
"sort": [
"字段名": {
"order": "asc"
},
"字段名": {
"order": "desc"
}
]
}
  • 简化写法
rest-api.http
1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /索引库名/_search
Content-Type: application/json

{
"query": {},
"sort": [
{
"字段名": "asc"
},
{
"字段名": "desc"
}
]
}
地理类型排序
  • 不会参与打分
rest-api.http
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
POST /索引库名/_search
Content-Type: application/json

{
"query": {},
"sort": [
{
"_geo_distance": {
"地理字段名": {
"lat": 纬度,
"lon":经度
},
"order": "asc",
"unit": "km"
}
}
]
}
简化写法
rest-api.http
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /索引库名/_search
Content-Type: application/json

{
"query": {},
"sort": [
{
"_geo_distance": {
"地理字段名": "纬度, 经度",
"order": "asc",
"unit": "km"
}
}
]
}

分页

  • form+size的值不能大于10000

from:分页起始下标位置,默认为0
size:每页的数据数,默认为10

rest-api.http
1
2
3
4
5
6
7
8
9
POST /索引库名/_search
Content-Type: application/json

{
"query": {},
"sort": [],
"from": 0,
"size": 10
}

高亮

  • 原理:后端得到数据后,先将关键字添加标签,再返回给前端;前端添加指定标签的样式,实现高亮

require_field_match:是否开启搜索字段与高亮字段匹配

true:缺省值,开启搜索字段与高亮字段匹配。如果匹配才会实现高亮,如果不匹配就无法实现高亮
false:关闭搜索字段与高亮字段匹配。即便是不匹配也能实现高亮

pre_tags:添加标签前缀,如果没有指定,缺省值是<em>
post_tags:添加标签后缀,如果没有指定,缺省值是</em>

rest-api.http
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /索引库名/_search
Content-Type: application/json

{
"query": {},
"highlight": {
"fields": {
"字段名": {
"require_field_match": "true",
"pre_tags": "<em>",
"post_tags": "</em>"
}
}
}
}

聚合查询

  • 官方文档
  • 聚合可以实现对文档数据的统计、分析、运算
  • 聚合可以嵌套,在一个聚合的结果上继续做聚合

query:限定聚合条件,可以不指定聚合条件
size:展示的原数文档的个数,0表示不展示原文档
aggs:定义聚合,可以指定多个聚合

rest-api.http
1
2
3
4
5
6
7
8
9
10
11
12
13
POST /索引库名/_search
Content-Type: application/json

{
"query": {},
"size": 0,
"aggs": {
"聚合名": {
"聚合类型": {},
"聚合类型": {}
}
}
}
  • 返回的数据中,aggregations.聚合名表示聚合的结果

桶聚合(Bucket)

  • Term Aggregation方式桶聚合:按照文档字段值分组,统计指定字段相同的文档的数量。类似于MySQL中的分组(Group By)

terms:Term方式的桶聚合
field:指定参与聚合的字段名
size:指定聚合结果数量,默认值为10
_count:返回数据按照文档数量的排序规则

desc:缺省值,按照文档数量的降序排序
asc:按照文档数量的降序升序

rest-api.http
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
POST /索引库名/_search
Content-Type: application/json

{
"query": {},
"size": 0,
"aggs": {
"聚合名": {
"terms": {
"field": "字段名",
"size": 10,
"order": {
"_count": "asc"
}
}
}
}
}

aggregations.聚合名.buckets.key:文档的字段名
aggregations.聚合名.buckets.doc_count:文档的个数

度量聚合(Metric)

  • 在桶聚合的基础上,用于计算数据的值,包括文档个数、文档评分最小值、文档评分最大值、文档评分平均值、文档评分总和
  • 如果没有在桶聚合的基础上,则将所有数据参与分值计算
rest-api.http
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
POST /索引库名/_search
Content-Type: application/json

{
"query": {},
"size": 0,
"aggs": {
"聚合名": {
"terms": {
"field": "桶聚合字段名",
"size": 10,
"order": {
"_count": "asc"
}
}
"aggs": {
"度量聚合名": {
"stats": {
"field": "度量聚合字段名"
}
}
}
}
}
}

aggregations.聚合名.buckets.度量聚合名.count:文档的个数
aggregations.聚合名.buckets.度量聚合名.min:文档评分最小值
aggregations.聚合名.buckets.度量聚合名.max:文档评分最大值
aggregations.聚合名.buckets.度量聚合名.avg:文档评分平均值
aggregations.聚合名.buckets.度量聚合名.sum:文档评分总和

自动补全搜索

自动补全类型

completion:通过前缀关键字模糊搜索

field:指定需要补全查询的字段名
skip_duplicates:跳过重复项
size:查询结果的个数

1
2
3
4
5
6
7
8
9
10
11
12
13
GET /索引库名/_search
{
"suggest": {
"查询名称": {
"text": "前缀关键字",
"completion": {
"field": "字段名",
"skip_duplicates": true,
"size": 10
}
}
}
}

Elasticsearch集群

集群中节点的职责

  • 默认情况下每个节点同时具有这4种职责
节点类型 配置参数 默认值 节点职责
master eligible node.master true 备选主节点:主节点可以管理和记录集群状态、决定分片在哪个节点、处理创建和删除索引库的请求
data node.data true 数据节点:存储数据、搜索、聚合、CRUD
ingest node.ingest true 数据预处理节点:数据存储之前的预处理
coordinating 以上节点如果都为false,则coordinating为true 协调节点:路由请求到其他节点,合并其他节点处理的结果,返回给用户

脑裂现象

  • 主节点由于网络原因导致与其他节点失联,此时其他节点会重新选举出新的主节点,当旧的主节点恢复连接时,集群中会出现多个主节点,这种现象就是脑裂现象
  • 选举票数 > (所有节点个数 + 1) / 2时,可以避免脑裂现象
  • Elasticsearch7.x版本以上,选举算法直接作为了配置的缺省值,所以不需要任何配置即可解决脑裂现象

分布式新增

  1. 将得到的数据交给主节点,主节点通过计算hash(文档id) % 所有节点个数的值,可以得到将数据存储到哪个分片
  2. 将数据交给指定节点进行存储
  3. 将数据交给其他节点进行备份

分布式查询

  1. 分散阶段(scatter phase):coordinating节点会把请求分发给集群中的每一个节点
  2. 聚集阶段(gather phase):coordinating节点会将从所有data节点中搜索到的结果进行汇总后返回给用户

故障转移

  • 主节点会监控集群中所有节点的健康状态,当集群中其中一个节点宕机,会立即将宕机节点的数据重新备份给其他健康的节点,这就是故障转移

    • 如果是数据节点宕机,直接进行转移数据
    • 如果是主节点宕机,先进行主节点选举,再进行转移数据
  • 如果宕机的节点恢复健康,则新的主节点会将备份数据重新从其他节点转移给恢复健康的节点

完成

参考文献

哔哩哔哩——黑马程序员
博客园——只会玩辅助
CSDN——qq_47452239