ES(8.1)
认证考题大纲参考如下
https://mp.weixin.qq.com/s/x7lMmMAo2563JysMJ8vgRQ
今天是我们学习ES8.1
官方搜索工程师的第一课,首先第一个问题就是根据给定的需求创建一个索引,本篇文章将采用如下(总分总)的形式分析该题目,第一部分先进行考题的分析,猜测要考的知识点大概有什么,然后第二部分对该题目涉及的知识点进行分析讲解,每一个考点后都跟着简单的解读,最后模拟汇总该考题,最终实现熟练掌握该题目。后续本类型文章如不特殊说明均使用该种方式进行讲解,如果有好的建议欢迎留在评论区讨论
本文结构如下:
1、题目分析(总)
2、题目拆解知识点(分)
3、总结(模拟出题考试,总)
下面进入第一部分,题目分析阶段
一、题目分析
题目:根据给定的需求创建一个索引
解读:首先我在看到这个题目的第一感觉就是这个题目很简单,不就是创建个索引吗,最多就是设置几个主分片,几个副本分片,稍微再深点就是字段类型、自定义分词器、索引别名,看着好像很简单,其实实际操作起来还是有一定难度的,不过考试过程中能够查阅官网,所以我们只要熟记考点官网位置即可,下面我就该题目涉及到的(索引设置,字段映射类型,文本分析)这三点进行分析
通过阅读本文,你可以获得什么?
1、定义一个索引常用的配置项有哪些
2、自定义字段映射与动态字段映射
3、如何设置字段的数据类型
4、文本分析中 Tokenizer
、Token filter
、Character filters
各自发挥什么作用以及如何设置
5、定义一个指定分片数量的索引,包含自定义分词器,自定义字段映射
二、题目拆解
2.1、索引设置
2.1.1、静态索引设置
静态索引设置是只能在索引创建时或者索引关闭时使用的
-
index.number_of_shards
索引主分片数量设置,默认1,只能在索引创建时设置,索引关闭时不能修改此选项
-
index.number_of_routing_shards
与
index.number_of_shards
一起使用的整数值,文档路由到主分片的分片数 -
index.codec
压缩存储数据策略选择,默认
LZ4
-
index.routing_partition_size
路由可以到达的分片数,默认1,只能在索引创建时设置,这个值必须小于
index.number_of_shards
,除非index.number_of_shards
的值也是1 -
index.soft_deletes.enabled
索引软删除开关,只能在创建索引时进行配置,默认
true
-
index.soft_deletes.retention_lease.period
软删除记录保留最长期限策略,默认值
12h
-
index.load_fixed_bitset_filters_eagerly
嵌套查询预加载缓存过滤器,默认
true
-
index.shard.check_on_startup
分片打开时是否对分片进行检查的开关。默认
false
,还有true
,checksum
,除非特别精通各配置项含义,否则不建议更改
2.1.2、动态索引设置
动态索引设置是可以使用update-index-settings
API在激活的索引上来动态设置
更改关闭掉的静态或者动态索引设置可能导致不正确的设置,如果不删除或者重建索引,则无法更正这些错误设置
-
index.number_of_replicas
副本分片数量设置,默认为1
-
index.auto_expand_replicas
根据集群中数据节点的数量自动展开的副本数量,默认
false
关闭 -
index.search.idle.after
分片多久没有请求或搜索会被认为是空闲的分片,默认
30s
-
index.refresh_interval
执行refresh的间隔,默认
1s
,可以设置-1
来禁用 -
index.max_result_window
es搜索结果返回的最大记录数量(from+size),默认
10000
-
index.max_inner_result_window
es搜索结果中聚合桶返回的最大数量,默认
100
-
index.max_rescore_window
Rescore
请求的返回记录最大值,默认10000
-
index.max_docvalue_fields_search
查询请求中对
docvalue_fields
数量的限制,默认100
-
Index.max_script_fields
查询请求中允许的
script_fields
的数量最大值,默认32
-
Index.max_ngram_diff
NGramTokenizer
和NGramTokenFilter
之间min_gram
与max_gram
之间的最大差异值,默认1
-
index.max_shingle_diff
shingle token filter
中max_shingle_size
与min_shingle_size
最大差值,默认3
-
index.max_refresh_listeners
索引分片上最大的监听器数量,实现了
refresh=wait_for
的监听器 -
index.analyze.max_token_count
使用analyze api 获取最大的token数量,默认
10000
-
index.highlight.max_analyzed_offset
高亮显示时设置的最大字符数,默认
1000000
-
index.max_terms_count
Term
查询中term
的最大的数量,默认65536
-
index.max_regex_length
正则查询中表达式的最大长度,默认
1000
-
index.query.default_field
查询语句中查询的字段设置,默认
*
,查询除了元数据之外的所有字段 -
index.routing.allocation.enable
控制索引分片分配
-
all
允许所有分片分配(默认) -
primaries
只允许主分片分配 -
new_primaries
只允许新创建的主分片分配 -
none
不允许分片分配
-
-
index.routing.rebalance.enable
控制索引分片重新平衡
-
all
允许所有分片重新平衡(默认) -
primaries
允许主分片重新平衡 -
replicas
运行副本分片重新平衡 -
none
不允许分片重新平衡
-
-
index.gc_deletes
已删除文档的历史版本号可被用于版本化操作的间隔,默认
60s
-
index.default_pipeline
设置索引默认的
ingest pipeline
-
index.final_pipeline
索引最终的
final ingest pipeline
,所有的请求都会走,如果该管道不存在,请求将会失败不能使用
final ingest pipeline
修改_index
字段,如果使用final ingest pipeline
修改_index
字段,该请求将失败 -
index.hidden
索引是否隐藏,默认 不返回隐藏的索引,请求时也可以使用参数
expand_wildcards
控制,默认false
2.2、映射
映射在我们的使用中有动态映射、动态映射模版、显式设置映射三块。动态映射也就是我们不需要使用显式的设置字段类型,由Elasticsearch
来进行推测类型生成映射;动态映射模版就是介于中间的一种,意思是我们提前设置好映射关系,并定一个模版名称、匹配规则,在进行索引插入数据的时候,根据匹配规则找到符合条件的动态模版,根据模版中的显式设置来生成索引;显式设置映射关系就是对索引中的每个字段都固定类型,无需进行类型推测。下面我们就以这三个方面深入的看下字段映射关系如何设置,在看字段映射如何设置之前先来了解一下Elasticsearch
中的数据类型都支持哪些?
2.2.1、支持的数据类型
本文数据类型只介绍下工作中经常使用的,个别的数据类型参考《根据给定需求创建索引进阶篇》,后续推出都会有的,全都会有的
本文数据类型只介绍下工作中经常使用的,个别的数据类型参考《根据给定需求创建索引进阶篇》,后续推出都会有的,全都会有的
本文数据类型只介绍下工作中经常使用的,个别的数据类型参考《根据给定需求创建索引进阶篇》,后续推出都会有的,全都会有的
-
聚合字段类型
类型设置为
aggregate_metric_double
作为一个对象,子字段可以有min
,max
,sum
,value_count
,当我们对字段设置为aggregate_metric_double
字段做聚合时,它能够直接使用子字段的值进行聚合,比如最大值就可以直接去子对象中max
的值比较,详情可参考如下文章https://mp.weixin.qq.com/s/erLmbgMA9mMrytSAQ9Bt3Q
-
字段别名类型
举例一个实用的例子,我们在做es中数据汇总的时候有这么一个统计,统计log.level为info级别的日志,但是因为数据源来自各个渠道,无法都满足ECS(Elastic Common Schema),所以我们可以使用字段别名,比如索引1中对应的统计字段是log.level.keyword,索引2对应的是level.keyword,但是mapping又无法进行修改来完成,所以我们创建一个字段别名log.level对应log.level.keyword,索引二中对应level.keyword,我们统计时只需要对lov.level统计即可了
我们知道索引可以起一个别名,字段也可以起一个别名,做搜索、聚合、排序的时候直接使用字段别名进行查询、聚合和排序,下面是本人对字段别名的一个深度学习,参考如下:
https://mp.weixin.qq.com/s/YMm-CedHtPnY6MIvGE1X9g
-
数组字段
数组字段没有固定的类型,但是数组中的所有元素类型必须一致,数组里可以包含零个或多个元素,如果为空,则是代表这个数组是没有值的字段
-
binary
二进制类型,默认该类型字段不可以被搜索,也不存储
-
Boolean
接收
true
和false
或者可以解析为true
和false
的字符串”true“
和"false"
和空字符串""
-
Date
时间类型,可以是
2022-08-01
或者2022-08-01 12:00:00
,也可以是一个秒数或者毫秒数,时间格式可以自定义格式,如果不添加使用默认格式"strict_date_optional_time||epoch_millis"
,使用时间类型字段排序时会自动转换为时间戳毫秒数进行排序 -
Date nano
Date
类型的补充字段,纳秒存储 -
Geo
描述地理位置信息的字段数据类型,因为知识点较多,详细信息可参考另一篇文章,通俗解读了
geo_point
与geo_shape
两种类型的数据,如果查询,聚合,排序等https://mp.weixin.qq.com/s/nw2_M8icS3v94w4sfrbqaw
-
Ip
保存网络地址信息,支持
ipv4
和ipv6
类型的存储与检索,详细可查看官网 -
Join
对于
Join
类型,不是很常用,但是也有使用,常用的我单独摘出来写了一篇文章,不常用或者比较简单常见的就简单一句话概括了,这个Join
就在下面大概说说吧,首先还是先创建一个索引PUT my-index-000001 { "mappings": { "properties": { "my_id": { "type": "keyword" }, "my_join_field": { "type": "join", "relations": { "user": "hobby" } } } } }
如上索引,父子文档,指定一个人的爱好,一个人可以有多个爱好,所以创建索引时指定父子关系字段类型,指定user
的子类为hobby
,每条文档都有一个名称(父文档或子文档
),以上图创建的索引为例,每个文档都要指定这是个user
还是个hobby
,如下,文档1
是父文档张三
用户,文档2
是父文档李四
用户,文档3,4
是张三
的爱好,文档5,6
是李四
的爱好
PUT my-index-000001/_doc/1?refresh
{
"my_id": "1",
"text": "张三",
"my_join_field": {
"name": "user"
}
}
PUT my-index-000001/_doc/2?refresh
{
"my_id": "2",
"text": "李四",
"my_join_field": {
"name": "user"
}
}
PUT my-index-000001/_doc/3?routing=1&refresh
{
"my_id": "3",
"text": "乒乓球",
"my_join_field": {
"name": "hobby",
"parent": "1"
}
}
PUT my-index-000001/_doc/4?routing=1&refresh
{
"my_id": "4",
"text": "篮球",
"my_join_field": {
"name": "hobby",
"parent": "1"
}
}
PUT my-index-000001/_doc/5?routing=1&refresh
{
"my_id": "5",
"text": "读书",
"my_join_field": {
"name": "hobby",
"parent": "2"
}
}
PUT my-index-000001/_doc/6?routing=1&refresh
{
"my_id": "6",
"text": "下棋",
"my_join_field": {
"name": "hobby",
"parent": "2"
}
}
需要注意的是 写入数据时必须指定路由值,因为父子文档必须保证数据存储在同一个分片
一个父文档可以有多个子文档,如下,user
是hobby
和girlfriend
的父类,grielfriend
是fans
的父类
PUT my-index-000001
{
"mappings": {
"properties": {
"my_join_field": {
"type": "join",
"relations": {
"user": ["hobby", "girlfriend"],
"girlfriend": "fans"
}
}
}
}
}
-
Keyword
- 关键字类型,单一的内容,比如身份证,姓名,Ip 等这种数据使用
- 常量类型字段,一个索引中该字段的值是固定不变的,创建索引时指定,如果与指定的值不同,文档拒绝写入
- 模糊查询类型,模糊查询可在
keyword
类型时使用,通配符类型在文本内容基数较大时候的做了优化
关键字类型数据可以用于聚合,排序,查询(
term
) -
Nested
嵌套对象类型,作为一个单独的
lucene
文档索引,支持查询,排序,聚合,聚合父文档等功能,详细可参考博主的另一篇Nested
详解,点击标签即可访问 -
Numeric
数字类型,支持
long
、integer
、short
、byte
、double
、float
、half_float
、scaled_float
、unsigned_long
对于整数类型,使用
long
、integer
、short
、byte
就可以,浮点数可以使用scaled_float
,如果scaled_float
不能满足,那可以选取精读更高的double
、float
、half_float
-
Object
在学习
Nested
嵌套对象类型的时候我们已经提到了一点,如果我们不知道对象类型,默认的就是Object
类型的数据,在Elasticsearch
内部,这个文档会被索引成一个简单扁平化的键值对形式 -
Range
表示一个范围内的数据,使用
gt
或者gte
定义下界,使用lt
、lte
定义上界,不过支持的聚合的类型有限,支持histogram
或者cardinality
支持的类型有
integer_range
、float_range
、long_range
、double_range
、date_range
、ip_range
-
Text
文本类型、为了
text
和match_only_text
,text
传统的全文检索字段类型;match_only_text
一种优化占用空间的文本类型,关闭打分,对需要执行查看进行较慢的操作,适合用作日志索引一般情况下使用
text
文件类型的字段还会使用一个keyword
的字段,进行聚合、排序、关键字匹配等操作
2.2.2、字段动态映射
何为Dynamic mapping,我们知道在使用关系型数据库mysql
的时候,我们创建一个表之后需要创建字段,比如字符串ID,字符串NAME,时间类型CREATE_TIME等字段来使用,而Elasticsearch
强大的就在于我们不在像关系型数据库那样提前设置好字段以及每个字段的类型,我们只需要保存一个文档,ES会自动的推断出数据类型并创建好索引,类型,字段映射关系等
当Elasticsearch
检测到新字段时,默认情况下Elasticsearch
会自动的识别字段数据类型,并将字段添加到mapping
映射中,但是我们可以通过参数dynamic
来指定是否自动添加字段映射,可选值有true
和runtime
,如果为true
默认使用以下规则生成字段映射关系
JSON data type | "dynamic":"true" | "dynamic":"runtime" |
---|---|---|
null | ||
true or false | boolean | boolean |
double | float | double |
long | long | long |
object | object | |
array | 根据数组中第一个非空的值判断 | 根据数组中第一个非空的值判断 |
日期类型的字符串 | date | date |
数字类型的字符串 | float or long | double or long |
不是日期也不是数字的字符串 | text 类型以及.keyword 的字类型 |
keyword |
也可以设置dynamic
为false
,这样在遇到新的字段时会抛出异常
2.2.3、字段动态映射模版
上面我们看到Elasticsearch
会自动的推断数据类型,生成字段映射,那么Dynamic template
动态模版就是自定义映射,提前内置好映射关系,在使用时通过一定的规则匹配上即可
如下:如果字段名称是ip开头的字符串,映射为ip类型的运行时字段
PUT my-index-000001/
{
"mappings": {
"dynamic_templates": [
{
"strings_as_ip": {
"match_mapping_type": "string",
"match": "ip*",
"runtime": {
"type": "ip"
}
}
}
]
}
}
如果想加到mapping
中只需替换runtime
为mapping
即可
PUT my-index-000001/
{
"mappings": {
"dynamic_templates": [
{
"strings_as_ip": {
"match_mapping_type": "string",
"match": "ip*",
"mapping": {
"type": "ip"
}
}
}
]
}
}
2.2.4、自定义字段映射
自定义字段映射,其实就是关系数据库中创建表时的字段设置,顾名思义就是提前设置好索引需要的字段以及字段类型,在添加文档时如遇到新字段报错(可配置),一般在规范索引字段时使用,比如日志索引,根据日期每天定时生成一个新的索引,这个索引我们就可以提前设置好模版,生成索引时直接使用模版生成,而模版中,直接定义好字段的类型,也就是说,自定义字段映射关系可以在映射模版的基础上使用的(关于索引模版的使用查看后文)如下是自定义字段映射的一个小例子
我们创建一个索引my-index-000001
,其中age
时integer
类型,email
是keyword
类型,name
是text
类型
PUT /my-index-000001
{
"mappings": {
"properties": {
"age": { "type": "integer" },
"email": { "type": "keyword" },
"name": { "type": "text" }
}
}
}
后续如果我们想修改这个已经创建的索引,比如增加个字段,那可以使用如下语句
PUT /my-index-000001/_mapping
{
"properties": {
"employee-id": {
"type": "keyword",
"index": false
}
}
}
查看索引mapping
语句如下
# 查看所有的字段映射
GET my-index-000001/_mapping
# 查看某一个字段的映射关系,此处以age举例
GET my-index-000001/_mapping/field/age
2.3、文本分析
2.3.1、什么是文本分析
文本分析是Elasticsearch
实现全文检索的前提,通过全文检索可以实现返回结果不仅包含精确完全匹配的结果,还包含相近的结果。比如我们有个数据中华人民共和国
,其中我们搜索中华
或者人民
或者共和国
或者其他组合词语的时候都可以搜索出来,不仅限于中华人民共和国
Tokenization
: 当我们进行全文检索时,分词可以让我们把一个词语拆分成更小的词语,默认情况下,这些小的词语也是有单独的词语语义-
Normalization
: 其中Tokenization
可以解决单个关键词的匹配查询, 但是还是精确匹配,不能解决以下这种情况的查询-
Quick
与quick
的大小写 -
fox
与foxes
的词根 -
jumps
与leaps
的近义词
为了解决这些问题,文本分析可以将这些token分词规范化为标准格式,这样就可以实现大小写,词根匹配,近义词匹配。
为了确保搜索时搜索词与预期的词匹配,尽量要使用相同的
tokenization
和normalization
规则 -
2.3.2、配置文本分析器
默认情况下,Elasticsearch
使用standard analyzer
,如果不能满足我们的需求,我们可以使用其他的内置分析器,或者自定义分词器,分析器的组成有三部分,character filter
、tokenizer
、token filter
其中这三部分都可以根据我们的需求来进行选择设置
-
Character filter
翻译一下就是字符过滤器,用在分词之前,常用的有html符号移除,正则替换、映射字符替换
-
Tokenizer
核心分词器,将一组字符流拆成单个词语,具体的分词策略在此实现,默认的标准分词器,常用的中文分词器,英文分词器、拼音分词器等
-
Token filter
分词之后的词语过滤,比如英文中的停用词移除(a,an,the)、转换大小写等
2.3.4、自定义分词器
需求
- 实现中文分词
- 实现拼音分词(词语全拼,词语首字母简拼)
- 实现
:) => _happy_
,:( => _sad_
- 使用
pattern_replace
替换我
为机智如我
解决方案
- 使用中文
IK分词器
加拼音分词器
组合实现上述需求1,2
- 使用
char_filter
实现上述需求3
- 使用
filter
实现上述需求4
过程分析
首先自定义分词器ik_smart_pinyin
与ik_max_pinyin
-
ik_smart_pinyin
:简单分词,分的比较粗,词语少 -
ik_max_pinyin
:最大粒度分词,分的比较细,词语多
其中ik_smart_pinyin
加入pinyin_first_letter_and_full_pinyin_filter
实现拼音分词
ik_max_pinyin
也加入pinyin_first_letter_and_full_pinyin_filter
实现拼音分词,但是为了对比,此处ik_max_pinyin
分词器再加入自定义的pattern_replace_filter
实现上述的需求4
,加入my_mappings_char_filter
实现上述需求3
settings全部内容
完整的创建索引settings
内容如下
PUT ik_index
{
"settings": {
"analysis": {
"analyzer": {
"ik_smart_pinyin": {
"tokenizer": "ik_smart",
"filter": [
"pinyin_first_letter_and_full_pinyin_filter"
]
},
"ik_max_pinyin": {
"tokenizer": "ik_max_word",
"filter": [
"pinyin_first_letter_and_full_pinyin_filter",
"pattern_replace_filter"
],
"char_filter": "my_mappings_char_filter"
}
},
"filter": {
"pinyin_first_letter_and_full_pinyin_filter": {
"type": "pinyin",
"keep_separate_first_letter": false,
"keep_full_pinyin": true,
"keep_original": true,
"limit_first_letter_length": 16,
"lowercase": true,
"remove_duplicated_term": true
},
"pattern_replace_filter": {
"type": "pattern_replace",
"pattern": "(我)",
"replacement": "机智如$1"
}
},
"char_filter": {
"my_mappings_char_filter": {
"type": "mapping",
"mappings": [
":) => _happy_",
":( => _sad_"
]
}
}
}
}
}
2.4、小结
在上面创建的自定义分词器的基础上,我们在创建mapping
字段关系映射时就可以直接指定ik_smart_pinyin
或者ik_max_pinyin
分词器即可实现分词啦,如下示例所示:
PUT ik_index/_mapping/
{
"properties": {
"content": {
"type": "text",
"analyzer": "ik_smart_pinyin"
}
}
}
三、总结
假总结,真心理总结
通过上面的学习,我们知道了设置索引时的常用参数、常见的字段类型以及建立字段映射关系时的参数设置,可以看得出来,创建索引就这三块,只要这三块使用熟练了,哪怕不查看官网也可以直接创建出一个满足自己需求的索引,鉴于考试时也都是可以参考官网的,所以上面的内容也不用死记硬背,只需要理解,多操作,记住常见的配置项即可了。好了该篇文章已经进入尾声了,一直想早点结束,但是写着写着就发现,知识点太多, 假设都写到这篇文章里,那就长的过分了,所以文中就分了几篇小的,比如字段类型那块,Geo、Nested等单独输出了一篇文章,想参考学习的可以关注公众号【醉鱼JAVA】直接去获取。后面的话,也是尽快吧,尽量赶点进度,把工作中常用的知识点整理起来,结合考点输出汇总,如果大家有好的意见或者建议欢迎评论区留言,下篇文章见喽!!!
本文由博客一文多发平台 OpenWrite 发布!