官方文档

该文档使用的ElasticSearch版本为7.4.2

官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/7.4/index.html

快速开始:https://www.elastic.co/guide/en/elasticsearch/reference/7.4/getting-started-search.html

Docker安装

需要给挂载的目录给777权限 chmod -R 777 文件夹

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
version: '3.8'

services:
elasticsearch_742:
container_name: elasticsearch_742
image: elasticsearch:7.4.2
restart: always
ports:
- "9200:9200"
- "9300:9300"
environment:
- "discovery.type=single-node"
- "ES_JAVA_OPTS=-Xms64m -Xmx512m"
networks:
- xiaofei-net
volumes:
- ./elasticsearch_742/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
- ./elasticsearch_742/data:/usr/share/elasticsearch/data
- ./elasticsearch_742/plugins:/usr/share/elasticsearch/plugins
kibana_742:
image: kibana:7.4.2
container_name: kibana_742
restart: always
ports:
- "5601:5601"
environment:
SERVER_NAME: kibana742
ELASTICSEARCH_HOSTS: http://elasticsearch_742:9200
volumes:
- ./kibana_742/config/kibana.yml:/usr/share/kibana/config/kibana.yml
networks:
- xiaofei-net
depends_on:
- elasticsearch_742
links:
- elasticsearch_742
networks:
xiaofei-net:

elasticsearch.yml

1
http.host: 0.0.0.0

kibana.yml

1
2
3
4
5
server.name: kibana
server.host: "0"
elasticsearch.hosts: [ "http://elasticsearch:9200" ]
xpack.monitoring.ui.container.elasticsearch.enabled: true
i18n.locale: "zh-CN"

基本概念

  • 索引(index):相当于MySQL数据库
  • 类型(_type):相当于MySQL的表,7.x建议使用_doc,如果使用其他的type,则该索引下面的的type都应该一致,不能出现一个索引下面指定多个type
    • _ type是早期版本的设计缺陷。在5.x以前的版本里边,一个所以下面是支持多个type的。6版本以后改为只支持一个type, type可以自定义。7以后所有的typr就默认为_doc,7.。8版本后移除type
  • 文档(Document):相当于MySQL表里面的内容,类型全是JSON格式
  • 文档存在某个索引下面的某个类型下面

索引增删改查

下列操作都是在kibana里面进行的操作,如果使用接口调试工具进行调试,需要在地址前面加上ElasticSearch的地址

新增 / 修改【索引、类型、文档】

  • PUT:必须指定id
  • POST:如果指定id,则使用指定的id,执行新增 / 修改操作;如果未指定id,则会自动生成id且添加数据
1
2
3
4
5
6
7
8
9
10
11
12
13
# 使用put
# 格式:【/索引/_doc/id】 或 【/索引/_create/id】
PUT /test/_doc/1
{
"username":"xiaofei"
}

# 使用post
# 格式:【/索引/_doc/id】 或 【/索引/_doc】 或 【/索引/_create/id】
POST /test/_doc/1
{
"username":"测试"
}

删除【索引、类型、文档】

1
2
3
4
5
6
7
# 删除文档
# 格式:DELETE /索引/_doc/id
DELETE /test/_doc/1

# 删除索引
# 格式:DELETE /索引
DELETE /test

修改

批量添加的数据不能换行,必须在一行显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 修改一条数据,如果存在则修改,不存在则添加
# 格式:/索引/_doc/id
POST /test/_doc/1
{
"username":"测试"
}

# 批量修改 / 添加,如果存在则添加,如果不存在则创建

# 格式 :
# POST /索引/_bulk
# {"index":{_id:"id值"}}
# 数据
# {"index":{_id:"id值"}}
# 数据

POST /test/_bulk
{"index":{"_id":"1"}}
{"username":"测试","password":"123456"}
{"index":{"_id":"2"}}
{"username":"测试2","password":"123456"}
{"index":{"_id":"3"}}
{"username":"测试3"}

查询

查询DSL文档:https://www.elastic.co/guide/en/elasticsearch/reference/7.4/query-dsl.html

测试数据:https://github.com/elastic/elasticsearch/blob/7.4/docs/src/test/resources/accounts.json

1
2
3
# 添加测试数据
POST /bank/_bulk
上面下载的测试数据
  • 一个是通过使用REST request URI发送搜索参数(uri + 检索参数)
  • 另一个是通过使用REST request body来发送它们(uri + 请求体)

基本DSL查询使用

该文档查询根据MySQL查询方式来进行对照编写,因为ES分页有默认值,所以MySQL语句都会加上分页,文档中对应的SQL语句只能用于参考

1
2
3
4
5
6
7
8
9
10
SELECT ...... , ...... , ......
FROM ......
WHERE
不包含组函数的过滤条件
AND / OR 不包含组函数的过滤条件
GROUP BY ...... , ...... , ......
HAVING
包含组函数的过滤条件
ORDER BY ...... ASC / DESC
LIMIT ...... , ......

match_all & match【匹配查询】

1
2
3
4
5
6
7
8
9
10
11
12
# MySQL,查询全部字段、分页
SELECT *FORM bank LIMIT 0,2

#ES
GET /bank/_search
{
"query": {
"match_all": {}
},
"from": 0,
"size": 2
}
1
2
3
4
5
6
7
8
9
10
11
12
13
# MySQL:查询部分字段、分页
SELECT account_number,balance,firstname FORM bank LIMIT 0,2

#ES
GET /bank/_search
{
"_source": ["account_number","balance","firstname"],
"query": {
"match_all": {}
},
"from": 0,
"size": 2
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# MySQL:查询部分字段、分页、排序、排序(排序只能是数字和日期类型)
SELECT account_number,balance,firstname FORM bank ORDER BY account_number DESC LIMIT 0,2

#ES
GET /bank/_search
{
"_source": ["account_number","balance","firstname"],
"query": {
"match_all": {}
},
"sort": [
{
"account_number": {
"order": "desc"
}
}
],
"from": 0,
"size": 2
}
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
# MySQL:查询全部字段、分词查询,等值查询,ES还会做分词
SELECT *FORM bank WHERE address LIKE '%mill Road%'

#ES:match
# 字符串:分词匹配,mill Road会分为mill和Road去进行匹配
# 数字:等值匹配,因为数字不能进行分词了
# )
GET /bank/_search
{
"query": {
"match": {
"address": "mill Road"
}
}
}

# MySQL:查询全部字段、分词查询,等值查询
SELECT *FORM bank WHERE account_number = 1

# ES
GET /bank/_search
{
"query": {
"match": {
"account_number": 1
}
}
}

match_phrase【短词匹配】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# MySQL:查询全部字段、分页、分词查询,等值查询
SELECT *FORM bank WHERE address LIKE '%mill Road%'

#ES:match_phrase(
# 字符串:全词匹配,mill Road不会再进行分词去进行匹配,必须分词中有mill Road才能匹配成功
# 数字:等值匹配,因为数字不能进行分词了
#
GET /bank/_search
{
"query": {
"match_phrase": {
"address": "mill Road"
}
}
}

multi_match【多字段匹配】

1
2
3
4
5
6
7
8
9
10
11
12
13
# MySQL:查询全部字段、分页、分词查询,等值查询
SELECT *FORM bank WHERE address LIKE '%mill Road%' OR `state` LIKE '%mill Road%'

#ES:multi_match(mill Road会再进行分词,在state或address中只要分词匹配上就能命中)
GET /bank/_search
{
"query": {
"multi_match": {
"query": "mill Road"
, "fields": ["state","address"]
}
}
}

bool【复合查询】

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
# MySQL
SELECT *FROM bank WHERE address LIKE '%mill%' AND gender = 'M' AND email NOT LIKE '%baluba.com%'

# address 包含mill,并且 gender 是M,如果 address 里面有 lane 最好不过,但是 email 必 须不包含 baluba.com
# must:必须达到列举的条件
# should:应该达到 should 列举的条件,如果达到会增加相关文档的评分,并不会改变 查询的结果。如果 query 中只有 should 且只有一种匹配规则,那么 should 的条件就会被作为默认匹配条件而去改变查询结果
# must_not:必须不是指定的情况
GET bank/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"address": "mill"
}
},
{
"match": {
"gender": "M"
}
}
],
"should": [
{
"match": {
"address": "lane"
}
}
],
"must_not": [
{
"match": {
"email": "baluba.com"
}
}
]
}
}
}

filter【过滤查询】

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
# MySQL
SELECT *FROM bank WHERE address LIKE '%mill%' AND gender='%M%' AND balance >= 10000 AND balance <=20000

# filter结果过滤:仅仅过滤结果,不会影响相关性得分
GET bank/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"address": "mill"
}
},
{
"match": {
"gender": "M"
}
}
],
"filter": {
"range": {
"balance": {
"gte": 10000,
"lte": 20000
}
}
}
}
}
}

term【非text精确匹配】

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
# MySQL
SELECT *FROM table WHERE address LIKE '%mill%' AND age = 28

# term:对于非文本匹配用term精确匹配
GET bank/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"age": {
"value": "28"
}
}
},
{
"match": {
"address": "Mill"
}
}
]
}
}
}

aggs【聚合查询】

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
# MySQL分组
SELECT age , COUNT(*) FROM `user1` GROUP BY age;
SELECT AVG(balance) FROM `user1`;

# aggs分组统计,size:0,只做聚合查询,不做数据查询
# 统计age的各个年龄段的人数
# 统计所有人的平均薪资
GET bank/_search
{
"aggs": {
"group_by_age": {
"terms": {
"field": "age"
}
},
"group_by_balance": {
"avg": {
"field": "balance"
}
}
},
"size": 0
}

# MySQL分组
SELECT age , COUNT(*) , AVG(balance) FROM `user1` GROUP BY age;

# 查询每个年龄段的平均人的薪资
GET bank/_search
{
"aggs": {
"group_by_age": {
"terms": {
"field": "age"
},
"aggs": {
"group_by_balance": {
"avg": {
"field": "balance"
}
}
}
}
},
"size": 0
}

Mapping

ElasticSearch中新建Mapping映射相当于MySQL中的建表和修改表,如果没有指定映射,则会在第一次添加数据的时候,ES会自动猜测每一个字段的类型,并且自动创建映射。新增和修改新增,相当于MySQL的添加字段和修改字段,建立Mapping有利于更精确的管理Elastic Search中的类型

字段数据类型

Elasticsearch 支持文档中字段的多种不同数据类型

参考文档:https://www.elastic.co/guide/en/elasticsearch/reference/7.4/mapping-types.html

类型 细分
核心数据类型
String text、keyword
Numeric long、integer、short、byte、double、float、half_float、scaled_float
Date date
Date nanoseconds date_nanos
Boolean boolean
Binary binary
Range integer_range、float_range、long_range、double_range、date_range
复杂数据类型
Object object:对于单个JSON对象
Nested nested:对于JSON对象数组
地理数据类型
Geo-point geo_point:对于纬度/经度点
Geo-shape geo_shape:对于多边形等复杂形状
专用数据类型
IP ip:IPv4 和 IPv6
Completion datatype completion to:provide auto-complete suggestions
Token count token_count:计算字符串中标记的数量
mapper-murmur3 murmur3:在索引时计算值的哈希值并将它们存储在索引中
mapper-annotated-text annotated-text:索引包含特殊标记的文本(通常用于识别命名实体)
Percolator 接受来自 query-dsl 的查询
Join 定义同一索引中文档的父/子关系
Rank feature 记录数字特征以提高查询时的命中率。
Rank features 记录数字特征以提高查询时的命中率。
Dense vector 记录浮点值的密集向量。
Sparse vector 记录浮点值的稀疏向量。
Search-as-you-type 为查询优化的类似文本的字段,以实现键入时完成
Alias 定义现有字段的别名。
Flattened 允许将整个 JSON 对象作为单个字段进行索引。
Shape shape:对于任意笛卡尔几何。

Mapping增删改查

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
# 格式
PUT /索引名称
{
"mappings": {
"properties": {
"属性名称": {
"type": "属性类型"
},
"属性名称": {
"type": "属性类型"
},
......
}
}
}

# 案例
PUT /my-index/_mapping
{
"mappings": {
"properties": {
"age": {
"type": "integer"
},
"email": {
"type": "keyword"
},
"name": {
"type": "text"
}
}
}
}

1
2
3
# mapping的删除就是索引的删除# 删除索引
# 格式:DELETE /索引
DELETE /test

修改映射只能添加属性,不能对原有的属性进行修改和删除,如果需要修改原有字段,只能创建新的映射然后进行数据迁移

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 /索引名称/_mapping
{
"mappings": {
"properties": {
"属性名称": {
"type": "属性类型"
},
"属性名称": {
"type": "属性类型"
},
......
}
}
}

PUT /my-index/_mapping
{
"mappings": {
"properties": {
"age": {
"type": "integer"
},
"email": {
"type": "keyword"
},
"name": {
"type": "text"
},
"gender": {
"type": "keyword"
}
}
}
}

数据迁移

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
# 创建一个新的mapping
PUT /new_bank
{
"mappings": {
"properties": {
"属性名称": {
"type": "属性类型"
}
......
}
}
}

# 数据迁移格式,迁移到新的索引的type,默认是 _doc
POST _reindex
{
"source": {
"index": "老索引名称",
"type" : "老索引的type"
},
"dest": {
"index": "新索引名称"
}
}

# 使用:有一个老的索引 bank,需要迁移到新的索引 new_bank
POST _reindex
{
"source": {
"index": "bank",
"type" : "_doc"
},
"dest": {
"index": "new_bank"
}
}

1
2
3
4
5
# 查看索引,格式
GET /索引名称/_mapping

# 使用
GET /bank/_mapping

ES插件安装

IK分词器

ES默认的分词只能对英文进行分词,对于中文的分词会将中文全部拆分成一个子,所以需要安装IK分词器对中文进行分词

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
# 未安装IK分词器进行分词
POST _analyze
{
"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
},
{
"token" : "人",
"start_offset" : 4,
"end_offset" : 5,
"type" : "<IDEOGRAPHIC>",
"position" : 4
}
]
}

下载 & 安装

7.4.2:https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip

将压缩包内的文件上传到映射的plugins下面的ik文件夹下后,对ik文件夹加权限chmod -R 777 /xiaofei/elasticsearch_742/plugins/ik

image-20230301202129825

使用IK分词器

使用analyzer指定IK分词器ik_smartik_max_word

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
# 使用 analyzer 指定ik分词器 ik_smart
POST _analyze
{
"analyzer": "ik_smart",
"text": "我是中国人"
}

# 结果
{
"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" : 5,
"type" : "CN_WORD",
"position" : 2
}
]
}
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
# 使用 analyzer 指定ik分词器 ik_max_word
POST _analyze
{
"analyzer": "ik_max_word",
"text": "我是中国人"
}

# 结果
{
"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" : 5,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "中国",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 3
},
{
"token" : "国人",
"start_offset" : 3,
"end_offset" : 5,
"type" : "CN_WORD",
"position" : 4
}
]
}

自定义词库

IK分词器的分词对于新出的词语不能进行分词,这时可以使用自定义词库,让IK来进行分词

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
POST _analyze
{
"analyzer": "ik_smart",
"text": "奥里给"
}

# 结果,对于新出的网络用词不能完成进行分词,需要自定义分词
{
"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
}
]
}

修改ik分词器挂载目录下面的conf下面的IKAnalyzer.cfg.xml文件,并且搭建nginx用于设置远程词库地址,词库为txt文件,每一行一个词

image-20230301205210655

词库内容

1
2
奥里给
乔碧罗
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
POST _analyze
{
"analyzer": "ik_smart",
"text": "奥里给"
}

# 结果,使用了自定义分词
{
"tokens" : [
{
"token" : "奥里给",
"start_offset" : 0,
"end_offset" : 3,
"type" : "CN_WORD",
"position" : 0
}
]
}

ES和Kibana设置用户名和密码

ElasticSearch配置文件

1
2
3
4
http.host: 0.0.0.0
xpack.security.enabled: true
xpack.license.self_generated.type: basic
xpack.security.transport.ssl.enabled: true

ES密码设置

1
2
3
4
5
6
7
8
9
10
11
# 进入容器
docker exec -it elasticsearch_742 /bin/bash

# 进入ES bin目录下,执行
./elasticsearch-setup-passwords interactive

# 用户名为[]中的值,例如 Enter password for [elastic]的用户名就是elastic

# 输入完密码,退出容器,重启容器
exit
docker restart elasticsearch_742

image-20230302093706022

Kibana配置文件

1
2
3
4
5
6
7
8
9
10
server.name: kibana
server.host: "0"
elasticsearch.hosts: [ "http://elasticsearch:9200" ]
xpack.monitoring.ui.container.elasticsearch.enabled: true
i18n.locale: "zh-CN"

# ES和kibana密码相关设置
elasticsearch.username: "elastic"
elasticsearch.password: "ES设置的密码"
xpack.security.encryptionKey: "something_at_least_32_characters"

Elasticsearch-Rest-Client

使用ES官方提供的API,在Java中操作ES

官方文档:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.4/java-rest-high.html

1
2
3
4
5
6
7
8
<properties>
<elasticsearch.version>7.4.2</elasticsearch.version>
</properties>

<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>

配置

1
2
3
4
5
6
es:
host: 主机
port: 端口
username: 用户名
password: 密码
scheme: http / https
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
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ElasticSearchConfig {

@Value("${es.host}")
private String host;

@Value("${es.port}")
private Integer port;

@Value("${es.username}")
private String username;

@Value("${es.password}")
private String password;

/**
* http / https
*/
@Value("${es.scheme}")
private String scheme;

public static final RequestOptions COMMON_OPTIONS;

static {
RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
COMMON_OPTIONS = builder.build();
}

@Bean
public RestHighLevelClient esRestClient() {

//设置ES用户名和密码
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));

return new RestHighLevelClient(
RestClient.builder(new HttpHost(host, port, scheme))
.setHttpClientConfigCallback(httpClientBuilder -> {
httpClientBuilder.disableAuthCaching();
return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
})
);
}
}