0%

Elasticsearch入门笔记(二)

Elasticsearch入门笔记(二)

基本使用

1. 数据类型

1.1字符串类型

1.1.1 text

文本数据类型,使用之后该类型字段的值会被分词,生成倒排索引,用于全文检索。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PUT library
{
"mappings": {
"properties": {
"describe": {
"analyzer": "ik_smart", // 指定分词器
"type": "text", // 设置类型为text
"fielddata": true, // 默认为false,该字段会启用内存空间进行一些排序、聚合操作
"fields": { // 多类型,若某个字段既要满足能被搜索,又要满足能排序、聚合
"sort": {
"type": "keyword"
}
}
}
}
}
}
1.1.2 keyword

关键字类型,该字段不会被分词,只能按确切的值进行搜索,可以进行排序、聚合、过滤。一般使用在唯一性字段邮箱地址、Mac地址、身份证号之类不需要被分词的字段。

1
2
3
4
5
6
7
8
9
10
11
PUT library
{
"mappings": {
"properties": {
"author": {
"ignore_above": 1024, // 指定查询的最大长度,超过则不会进行索引
"norms": true // 在进行查询评分时,是否需要考虑字段长度,默认为false
}
}
}
}

1.2数字类型

支持longintegershortbytedoublefloathalf_float(半精度浮点数)、scaled_float(带缩放因子浮点数)

scaled_float 举例:

在索引时会将原始值*缩放因子后四舍五入得到一个新值,将这个新值作为索引值(查询时返回的还是原值)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PUT library
{
"mappings": {
"properties": {
"author":{
"type": "keyword"
},
"price":{
"type": "scaled_float",
"scaling_factor": 100 # 需指定缩放因子
}
}
}
}

添加两条数据

1
2
3
4
5
6
7
8
9
PUT book/_doc/1
{
"price": 10.1111 # 实际的索引值为1011
}

PUT book/_doc/2
{
"price": 11.1111 # 实际的索引值为1111
}

查询语句

1
2
3
4
5
6
7
8
GET book/_doc/_search
{
"query": {
"match": {
"price": 11.2222 # 11.2222 * 100再四舍五入后的值为1122,匹配不到文档
}
}
}

1.3布尔类型

布尔值,可以识别字符串的布尔值"true""false"truefalse

1.4日期类型

日期类型,由于在json中是没有具体的日期类型的表达,所以可以是日期格式化后的字符串,可以是毫秒数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
PUT library
{
"mappings": {
"properties": {
"publish_date": {
"type": "date",
"format": "yyyy-mm-dd||yyyy/mm/dd||epoch_millis" //指定了三种格式的日期
}
}
}
}

PUT library/_doc/1
{
"describe": "aaa",
"publish_date": "2021-11-14"
}

# 添加失败,日期格式不对
PUT library/_doc/2
{
"describe": "aaa",
"publish_date": "2021-11-14 14:20:20"
}

1.5范围类型

范围类型,表示一个区间,若指定该类型,字段值为一个对象。需使用到gtgleltlte等逻辑标识符表示。integer_rangelong_rangefloat_rangedouble_rangedate_rangeip_range(ip地址的范围)。

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
PUT library
{
"mappings": {
"properties": {
"price": {
"type": "integer_range"
},
"my_date": {
"type": "date_range",
"format": "yyyy-mm-dd"
}
}
}
}

PUT library/_doc/1
{
"price": {
"gt": 1,
"lt": 10
},
"my_date": {
"gte": "2021-01-21",
"lte": "2021-05-22"
}
}

1.6地理类型

1.6.1 geo_point

表示一个坐标点。

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
36
37
38
39
40
# 创建点结构
PUT map
{
"mappings": {
"properties": {
"location": {
"type": "geo_point"
}
}
}
}

# 1.通过对象指定经纬度
PUT map/_doc/1
{
"location": {
"lat": 30,
"lon": 108
}
}

# 2.通过字符串“经度,维度”指定
PUT map/_doc/1
{
"location": "30,108"
}

# 3.通过geohash指定
PUT map/_doc/1
{
"location": "wmkf9t74wmkf"
}

# 4.通过数组[维度, 经度]指定
PUT map/_doc/1
{
"location": [108, 30]
}

GET map/_doc/1
1.6.2 geo_shape

地理形状数据类型,使用geoJson表示。

geoJson ElasticSearch Type 描述
Point point 单个地理坐标
LineString lineString 两点或多点组成任意线
Polygon polygon 一个闭合的多边形,其第一个点和最后一个点必须匹配。因此需要n+1个顶点来创建一个n边多边形,且最少4个顶点(构成三角形)
MultiPoint multipoint 一组未连接但可能相关的点
MultiLineString multilinestring 一组独立的线
MultiPolygon multipolygon 一组独立的多边形
N/A envelope 通过仅指定左上角和右下角构成的边界矩形
N/A circle 由中心点和半径指定的圆,默认单位为m

例子

在线制作geojson -> http://geojson.io/

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# 1. point 单坐标,由[维度, 经度]组成
PUT map/_doc/1
{
"location": {
"type": "point",
"coordinates": [108, 30]
}
}

# 2. lineString 地理线
PUT map/_doc/1
{
"location": {
"type": "lineString",
"coordinates": [
[108, 30],
[120, 30]
]
}
}

# 3. polygon 闭合多边形,首位需要一致
PUT map/_doc/1
{
"location": {
"type": "polygon",
"coordinates": [
[
[108, 30],[120, 30],[120, 90],[108, 90],[108, 30]
]
]
}
}

# 第一个数组表示多边形外边界,第二个数组表示内边界
PUT map/_doc/1
{
"location": {
"type": "polygon",
"coordinates": [
[[106,28],[117,28],[117,34],[106,34],[106,28]],
[[108,29],[115,29],[115,33],[108,33],[108,29]]
]
}
}
# 4. multiPoint 多个点
PUT map/_doc/1
{
"location": {
"type": "multiPoint",
"coordinates": [
[108, 30], [108, 31]
]
}
}

# 5. multilinestring 多条线
PUT map/_doc/1
{
"location": {
"type": "multilinestring",
"coordinates": [
[[108, 30],[120, 30]],
[[109, 30],[121, 30]],
[[110, 30],[122, 30]]
]
}
}

# 6. multiPolygon 多个多边形
PUT map/_doc/1
{
"location": {
"type": "multipolygon",
"coordinates": [
[
[[108, 30],[120, 30],[120, 90],[108, 90],[108, 30]]
],
[
[[106,28],[117,28],[117,34],[106,34],[106,28]],
[[108,29],[115,29],[115,33],[108,33],[108,29]]
]
]
}
}

# 7.envelope,指定形状的左上角和右下角的坐标,以表示边界矩形:
PUT map/_doc/1
{
"location": {
"type": "envelope",
"coordinates": [
[110,39],[114,37]
]
}
}
# 8. circle,添加一个圆,指定半径和中心点坐标
# 好像6版本之后不支持circle了,插入报错 circle geometry is not supported
PUT map/_doc/1
{
"location": {
"type": "circle",
"coordinates": [110, 39],
"radius": "100km"
}
}

1.7特殊类型

1.7.1 IP类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
PUT library
{
"mappings": {
"properties": {
"ipAddress":{
"type": "ip"
}
}
}
}

PUT library/_doc/1
{
"address":"192.168.1.1"
}
# 可以通过网段搜索
GET library/_search
{
"query": {
"term": {
"ipAddress": "192.168.0.0/16"
}
}
}

1.8复合类型

1.8.1数组

默认情况下任何字段都可以包含一个或多个值,当包含多个值时,就表示是一个数组,唯一要求是数组中的每个值的类型一致,不指定的情况下,以第一个值的类型为准。

1.8.2对象类型

一个文档的一个属性可以是一个对象,这个对象的某一个属性可以在包含一个对象。

1
2
3
4
5
6
7
8
9
10
11
12
PUT library/_doc/1
{
"name": "tom",
"class": {
"name": "一班",
"location": {
"province": "福建省",
"city": "福州市",
"county": "闽侯县"
}
}
}
1.8.3嵌套类型

nested类型。若文档的属性是一个对象数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
PUT user/_doc/1
{
"address": [
{
"id": 1,
"name": "福建"
},
{
"id": 2,
"name": "北京"
}
]
}

以上例子,若使用对象类型保存,由于luncene没有内部对象的概念,所以es会将以上数据扁平化为简单的字段和值列表。

1
2
3
4
{
"address.id": [1, 2],
"address.name": ["福建", "北京"]
}

这时若查询语句为查找id=2,name="福建"也会被查询到。使用nested类型之后,将数组中的每一个对象作为独立文档来索引。

1
2
3
4
5
6
7
8
9
10
{
{
"address.id": 1,
"address.name": "福建"
},
{
"address.id": 2,
"address.name": "北京"
}
}

2.索引操作

2.1创建索引

1
PUT library

2.2删除索引

1
DELETE library

2.3修改索引

可以修改索引的属性Settings,比如索引的分片数、副本数、路由、读写权限等。

修改索引分片数和副本数都为2

1
2
3
4
5
6
7
PUT library
{
"settings": {
"number_of_shards": 2,
"number_of_replicas": 2
}
}

2.4复制索引

复制索引时只会复制其中的数据,不会复制setting中的信息

1
2
3
4
5
6
7
8
9
POST _reindex
{
"source": {
"index": "library"
},
"dest": {
"index": "library2"
}
}

2.5 索引别名

索引别名可以指向一个或者多个索引,也就是说多个索引可以共用一个别名。比如library_log_20210514library_log_20210515两个索引都对它们设置索引别名为library_log

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
POST _aliases
{
"actions": [
{
"add": {
"index": "library_log_20210514",
"alias": "library_log"
}
},
{
"add": {
"index": "library_log_20210515",
"alias": "library_log"
}
}
]
}

# 设置之后就可以通过一个名称来进行全文检索
GET library_log/_search

通过别名创建视图,为library中作者是tom创建一个视图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST _aliases
{
"actions": [
{
"add": {
"index": "library",
"alias": "library_alias",
"filter": {
"term": {
"author": "tom"
}
}
}
}
]
}

索引别名其实类似关系数据库中视图的概念,只提供读操作,不能进行写操作。

1
2
# 获取所有别名
GET _cat/aliases

在真是环境中,索引的设计不可能呢一步到位,随着需求、业务的扩展,可能会修改索引的mapping的字段类型、或者是setting中的配置信息,若简单采用reindex操作,在实施运维操作更新这段时间内,有可能导致数据不可访问,所以可以通过指定别名的方式进行索引迁移。

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
36
# 1. 创建新索引结构
PUT library_new
{
... // 索引结构等等
}

# 2. 使用reindex进行索引数据迁移
POST _reindex
{
"source": {
"index": "library"
},
"dest": {
"index": "library_new"
}
}

# 最后在修改索引别名
POST _aliases?pretty
{
"actions""[
{
"remove": {
"index": "library",
"alias": "library"
}
},
{
"add": {
"index": "library_new",
"alias": "library"
}
}
]
}

2.6关闭/开启索引

索引关闭后只能获取索引的setting信息,不能进行读写。

关闭索引:

1
POST book/_close

打开索引:

1
POST book/_open

3.文档操作

3.1新增文档

6.*版本之后一个索引只允许一个类型,所以路径上固定索引名/_doc/id(可选),可以指定id,不指定会自动生成。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 指定id
PUT library/_doc/1
{
"name": "c++入门",
"author": "tom"
}

# 不指定id,由es生成
PUT library/_doc
{
"name": "c入门",
"author": "marry"
}

返回参数解析,其中_shards节点中total为2但是只有一个分片成功的原因是,我的环境是单机单节点,但是刚刚设置的时候又设置了两个分片,其实这种设置是不符合逻辑的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"_index" : "library", // 对应的索引
"_type" : "_doc", // 文档类型
"_id" : "1", // 创建的文档id
"_version" : 2, // 文档版本
"result" : "created", // 操作状态,有created、updated
"_shards" : { // 分片上的执行状态
"total" : 2, // 分片数量
"successful" : 1, // 执行成功分片数量
"failed" : 0 // 执行失败分片数量
},
"_seq_no" : 2, // 索引写操作版本
"_primary_term" : 1 // 文档所在分片
}

​ 在6.7之前使用_version来做并发控制,每一个文档都维护各自的_version字段,每次多这个文档的写操作都会自增这个字段,当写操作之后_version字段进行自增后小于存储中的_version时,写操作所提交的内容会被拒绝。

​ 在6.7之后使用_seq_no_primary_term来做并发控制,seq_no类似于_version字段,但是它的生命周期是整个索引,而_version的生命周期是某一个文档。

3.2修改文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 指定id进行更新,这种更新会覆盖旧文档
# 原文档有name和author字段,经过以下更新后只剩name字段
PUT library/_doc/1
{
"name": "c++入门"
}
# 更新指定字段
POST library/_update/1
{
"script":{
"lang": "painless",
# 设置name为参数params中的name
"source": "ctx._source.name=params.name",
"params": {
"name": "java入门"
}
}
}

更新指定文档通过脚本的方式更新,ctx可以获取到上下对象,即通过GET library/_doc/1获取到的数据。上下文对象中的_source节点就是存储的数据。

1
2
3
4
5
6
7
8
9
10
11
12
{
"_index" : "library",
"_type" : "_doc",
"_id" : "1",
"_version" : 5,
"_seq_no" : 5,
"_primary_term" : 1,
"found" : true,
"_source" : {
"name" : "java入门"
}
}

也可以通过条件进行更新操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST library/_update_by_query
{
"script":{
"lang": "painless",
"source": "ctx._source.name=params.name",
"params": {
"name": "java入门"
}
},
"query": {
"term": {
"name": "c++"
}
}
}

3.3删除文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 指定文档
DELETE library/_doc/1

# 根据查询条件删除
POST library/_delete_by_query
{
"query": {
"term": {
"name": {
"value": "java"
}
}
}
}

3.4批量操作

es中通过Bluk api来执行批量操作。

1.可以通过Kibana来访问Bluk接口。批量操作的数据语法为一行操作(auction),一行数据。

2.也可以通过请求直接执行。

curl -XPOST “http://localhost:9200/library/_bulk" -H “content-type:application/json” –data-binary @es-pipeline.json

注意:结尾要空出一行。

否则会报错:

{“error”:{“root_cause”:[{“type”:”illegal_argument_exception”,”reason”:”The bulk request must be terminated by a newline [\n]”}],”type”:”illegal_argument_exception”,”reason”:”The bulk request must be terminated by a newline [\n]”},”status”:400}%

注意:执行时需要添加-H "content-type:application/json"

否则会报错

{“error”:”Content-Type header [application/x-www-form-urlencoded] is not supported”,”status”:406}%

1
2
3
4
5
6
7
8
POST _bulk
{"index": {"_index": "library", "_id": "1"}}
{"name": "c++入门"}
{"index": {"_index": "library", "_id": "2"}}
{"name": "java入门"}
{"index": {"_index": "library", "_id": "3"}}
{"name": "pyton入门"}

3.5查询文档

1
2
# 通过id获取文档
GET library/_doc/1

3.6 数据迁移

​ 在别名那里顺便提了reindex,通过该关键字进行索引间的数据拷贝,只拷贝数据,不拷贝mappingsetting信息。

4. 关键字

4.1 analyzer/search_analyzer

通过analyzer指定分词器,在查询时若没有通过search_analyzer指定分词器,默认使用该索引的analyzer分词器来进行分词。

1
2
3
4
5
6
7
8
9
10
PUT library
{
"mappings": {
"properties": {
"decr":{
"type":"text",
"analyzer": "ik_smart" // 指定分词器
}
}
}
4.2 normailizer

预处理,在查询前进行数据预处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PUT library
{
"settings": {
"analysis": {
"normalizer": { // 创建一个预处理
"lowerNor": { // 创建一个名为lowerNor的预处理器
"type": "custom", // 类型为自定义
"filter": ["lowercase"] // 过滤器:大小写转换
}
}
}
},
"mappings": {
"properties": {
"name": {
"type": "keyword",
"normalizer": "lowerNor"
},
}
}
}
4.3 coerce

设置不进行类型转换。默认情况下,若指定字段类型为integer,插入数据时若插入为字符串"10",也会插入成功。若不需要该类型自动转换,则标记该字段为false

1
2
3
4
5
6
7
8
9
10
11
PUT user
{
"mappings": {
"properties": {
"id": {
"type": "integer",
"coerce": false
}
}
}
}
4.4 enabled/index

enabled表示若某字段仅存储,不需要进行全文检索。默认为true表示需要进行检索。

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
PUT user
{
"mappings": {
"properties": {
"name": {
"enabled": true
}
}
}
}

PUT user/_doc/1
{
"name": "tom"
}

# 查询结果为空
GET library/_search
{
"query": {
"term": {
"name": "tom"
}
}
}

index指定一个字段是否被索引。true表示字段会被索引。

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
PUT library
{
"mappings": {
"properties": {
"publish_date":{
"type": "date",
"index": false
}
}
}
}

PUT library/_doc/1
{
"publish_date": "2020"
}

# 直接报错
# failed to create query: Cannot search on field [publish_date] since it is not indexed.
GET library/_search
{
"query": {
"term": {
"publish_date": "2020"
}
}
}
4.5 ignore_above

用于keyword类型,表示指定分词和索引字符串的最大长度,超过该长度将不会被索引。

1
2
3
4
5
6
7
8
9
10
11
PUT library
{
"mappings": {
"properties": {
"author":{
"type": "keyword",
"ignore_above": 10
}
}
}
}
4.6 ignore_malformed

忽略不匹配类型数据。

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 user
{
"mappings": {
"properties": {
"name": {
"type": "integer",
"ignore_malformed": true // 标记允许
}
}
}
}

PUT user/_doc/1
{
"name": "abc" // 不是integer乐西
}

GET user/_doc/1

# 返回结果
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"_ignored" : [
"name"
],
"found" : true,
"_source" : {
"name" : "abc"
}
}
4.7 null_value

es 中,值为 nul 的字段不索引也不可以被搜索,null_value 可以让值为null 的字段显式的可索引、可搜索。

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
PUT library
{
"mappings": {
"properties": {
"name":{
"type": "keyword",
"null_value": "nameNull"
}
}
}
}

PUT library/_doc/1
{
"name": null,
"author": "tom"
}

# 通过null_value指定的值来进行查询。
GET users/_search
{
"query": {
"term": {
"name": "nameNull"
}
}
}
4.8 fields

fields参数可以让同一字段有多种不同的索引方式

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
36
37
38
39
PUT library
{
"mappings": {
"properties": {
"describe":{
"type": "text",
"analyzer": "ik_smart",
"fields": {
"raw":{
"type":"keyword"
}
}
}
}
}
}

PUT library/_doc/1
{
"describe":"这是一本中国人写的书"
}

GET library/_search
{
"query": {
"term": {
"describe": "这是"
}
}
}

GET library/_search
{
"query": {
"term": {
"describe.raw": "这是一本中国人写的书"
}
}
}
4.9.Dynamic_templates

映射模板,默认情况下,由es根据默认规则对数据进行预判对应的类型,若需要自定义规则,可以使用Dynamic_templates

4.10 dynamic

用于检测新发现的字段。

  • true: 默认,新发现的字段添加到映射中。
  • false: 必须显示指定新字段
  • strict:若检测到新字段,抛出异常。
1
2
3
4
5
6
7
8
9
10
11
PUT library
{
"mappings": {
"dynamic": false,
"properties": {
"username": {
"type": "keyword"
}
}
}
}
-------------本文结束感谢您的阅读-------------