一、Elasticsearch 的文件存储

Elasticsearch 是面向文档型数据库,一条数据就是一个文档,用 JSON 作为文档序列化的格式

比如:

1
2
3
4
5
6
7
{
"name": "John",
"sex": "Male",
"age": 18,
"birthDate": "1998/05/01",
"interests": ["sports", "music"]
}

Elasticsearch 可以看成是一个数据库,只是和关系型数据库比起来数据格式和功能不一样

ElasticSearch 和 RDMS 对比

RDMSElasticsearch
数据库(database)索引(index)
表(table)类型(type)
行(row)文档(document)
列(column)字段(field)
表结构(Schema)映射(Mapping)
索引全文索引
SQL查询 DSL
SELECT * from tablenameGET http://…..
UPDATE table SETPUT http://
DELETEDELETE http://

二、Elasticsearch 核心概念

1、索引 index

文档存储的地方,类似于 MySQL 数据库中的数据库概念

2、类型 type

如果按照关系型数据库中的对应关系,还应该有的概念。而 ES 中没有的概念,这是 ES 和数据库的一个区别。

在我们建立索引之后,可以直接往索引中写入文档

3、域 Field

相当于是数据表的字段,字段在 ES 中可以理解为 JSON 数据的键,下面的 JSON 数据中,name 就是一个字段。

1
2
3
{
"name": "jack"
}

4、映射 mapping

映射是对文档中每个字段的类型进行定义,每一种数据类型都有对应的使用场景。

每个文档都有映射,但是在大多数使用场景中,我们并不需要显示的创建映射,因为 ES 中实现了动态映射。

我们在索引中写入一个 JSON 文档,在动态映射的作用下,name会映射成text类型,age会映射成long类型。

1
2
3
4
{
"name": "jack",
"age": 18
}

5、文档 document

文档 在 ES 中相当于传统数据库中的行的概念,ES 中的数据都以 JSON 的形式来表示

在 MySQL 中插入一行数据和 ES 中插入一个 JSON 文档是一个意思。下面的 JSON 数据表示,一个包含 3 个字段的文档。

1
2
3
4
5
{
"name": "jack",
"age": 18,
"gender": 1
}

一个文档不只有数据。它还包含了元数据(metadata)——关于文档的信息。三个必须的元数据节点是:

节点说明
_index文档存储的地方
_type文档代表的对象的类
_id文档的唯一标识

三、ElasticSearch 的接口语法

操作索引 index

四、使用 kibana 操作

1、操作映射

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
# 创建索引
PUT person
# 查询索引
GET person
# 删除索引
DELETE person

# 查询映射
GET person/_mapping

# 添加映射
PUT person/_mapping
{
"properties":{
"name":{
"type":"keyword"
},
"age":{
"type":"integer"
}
}
}

# 创建索引并添加映射
PUT person
{
"mappings": {
"properties": {
"name":{
"type": "keyword"
},
"age":{
"type":"integer"
}
}

}
}

# 索引库中添加字段
PUT person/_mapping
{
"properties":{
"address":{
"type":"text"
}
}
}

2、操作文档 document

  • 添加文档
  • 查询文档
  • 修改文档
  • 删除文档
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
# 添加文档,指定id
PUT person/_doc/1
{
"name":"张三",
"age":20,
"address":"深圳宝安区"
}

# 查询文档
GET person/_doc/1

# 添加文档,不指定id
POST person/_doc/
{
"name":"李四",
"age":20,
"address":"深圳南山区"
}

# 查询文档
GET person/_doc/i8pcFHoBcmT0mJx5lxnM

# 查询所有文档
GET person/_search

# 删除文档
DELETE person/_doc/1

# 修改文档 根据id,id存在就是修改,id不存在就是添加
PUT person/_doc/2
{
"name":"王五",
"age":20,
"address":"深圳福田"
}

3、全文查询-match 查询

全文查询会分析查询条件,先将查询条件进行分词,然后查询,求并集

1
2
3
4
5
6
7
8
9
# match 先会对查询的字符串进行分词,在查询,求交集
GET person/_search
{
"query": {
"match": {
"address": "深圳"
}
}
}

4、查询文档-term 查询

词条查询不会分析查询条件,只有当词条和查询字符串完全匹配时才匹配搜索

请求体:

1
2
3
4
5
6
7
8
9
10
11
# 查询 深开头的数据
GET person/_search
{
"query": {
"term": {
"address": {
"value": "深"
}
}
}
}

5、关键字搜索数据

1
2
# 查询地址中包含深圳的数据
GET person/_search?q=address="深圳"

6、DSL 查询

url 地址请求体,多添加几条数据,方便做查询

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  test/_doc/1001
{
"id":"1001",
"name":"张三",
"age":20,
"sex":"男"
}

PUT test/_doc/1002
{
"id":"1002",
"name":"李四",
"age":25,
"sex":"女"
}

PUT test/_doc/1003
{
"id":"1003",
"name":"王五",
"age":30,
"sex":"女"
}

PUT test/_doc/1004
{
"id":"1004",
"name":"赵六",
"age":30,
"sex":"男"
}

GET test/_search

DSL 查询

① 根据年龄查询

请求体

1
2
3
4
5
6
7
8
POST test/_doc/_search
{
"query":{
"match":{
"age":20
}
}
}

② 查询年龄大于 20 岁的女性用户

请求体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
GET  test/_search
{
"query":{
"bool":{
"filter":{
"range":{
"age":{
"gt":20
}
}
},
"must":{
"match":{
"sex":"女"
}
}
}
}
}

③ 全文搜索

请求体

1
2
3
4
5
6
7
8
GET test/_search
{
"query": {
"match": {
"name": "李四 张三 王五"
}
}
}

7、高亮显示

1
2
3
4
5
6
7
8
9
10
11
12
13
GET test/_search
{
"query": {
"match": {
"name": "李四 张三 王五"
}
},
"highlight": {
"fields": {
"name": {}
}
}
}

8、聚合

在 Elasticsearch 中,支持聚合操作,类似 SQL 中的 group by 操作。

请求体

1
2
3
4
5
6
7
8
9
10
GET  test/_search
{
"aggs": {
"all_interests": {
"terms": {
"field": "age"
}
}
}
}

9、指定响应字段

在响应的数据中,如果我们不需要全部的字段,可以指定某些需要的字段进行返回

1
GET test/_doc/1001?_source=id,name

10、判断文档是否存在

如果我们只需要判断文档是否存在,而不是查询文档内容,那么可以这样:

1
HEAD test/_doc/1001

存在返回 200 - OK

不存在返回 404

11、批量操作

有些情况下可以通过批量操作以减少网络请求。如:批量查询、批量插入数据。

① 批量查询

1
2
3
4
POST  test/_doc/_mget
{
"ids" : [ "1001", "1003" ]
}

如果,某一条数据不存在,不影响整体响应,需要通过 found 的值进行判断是否查询到数据。

1
2
3
4
POST  test/_doc/_mget
{
"ids" : [ "1001", "1006" ]
}

返回结果中关于 1006 的:

1
2
3
4
5
6
{
"_index": "test",
"_type": "_doc",
"_id": "1006",
"found": false
}

12、_bulk 操作

在 Elasticsearch 中,支持批量的插入、修改、删除操作,都是通过_bulk 的 api 完成的。

请求格式如下:

1
2
3
4
{ action: { metadata }}
{ request body }
{ action: { metadata }}
{ request body }

批量插入数据:

1
2
3
4
5
6
7
POST _bulk
{"create":{"_index":"test","_id":2001}}
{"id":2001,"name":"name2001","age":20,"sex":"男"}
{"create":{"_index":"test","_id":2002}}
{"id":2001,"name":"name2002","age":30,"sex":"男"}
{"create":{"_index":"test","_id":2003}}
{"id":2003,"name":"name2001","age":40,"sex":"男"}

批量删除:

1
2
3
4
POST _bulk
{"delete":{"_index":"test","_id":2001}}
{"delete":{"_index":"test","_id":2002}}
{"delete":{"_index":"test","_id":2003}}

由于 delete 没有请求体,所以,action 的下一行直接就是下一个 action。

13、分页

和 SQL 使用 LIMIT 关键字返回只有一页的结果一样,Elasticsearch 接受 from 和 size 参数:

size: 结果数,默认 10
from: 跳过开始的结果数,默认 0

请求路径

1
GET test/_search?size=1&from=1

14、terms 查询

terms 跟 term 有点类似,但 terms 允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去做匹配

1
2
3
4
5
6
7
8
9
10
11
POST test/_search
{
"query": {
"terms": {
"age": [
"20",
"30"
]
}
}
}

15、range 查询

range 过滤允许我们按照指定范围查找一批数据:

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

1
2
3
4
5
6
7
8
9
10
11
POST test/_search
{
"query": {
"range": {
"age": {
"gte": 20,
"lte": 40
}
}
}
}

16、exists 查询

exists 查询可以用于查找文档中是否包含指定字段或没有某个字段,类似于 SQL 语句中的 IS_NULL 条件

1
2
3
4
5
6
7
8
9
# "exists":  必须包含
POST test/_search
{
"query": {
"exists": {
"field": "age"
}
}
}

17、分词

分词就是指将一个文本转化成一系列单词的过程,也叫文本分析,在 Elasticsearch 中称之为 Analysis。

使用 IK 分词器测试

IK 提供了两个分词算法 ik_smart 和 ik_max_word

其中 ik_smart 为最少切分,ik_max_word 为最细粒度划分

① 最小切分

1
2
3
4
5
POST _analyze
{
"analyzer": "ik_smart",
"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
{
"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
POST _analyze
{
"analyzer": "ik_max_word",
"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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
"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
}
]
}

18、修改索引映射 mapping

删除原有 person 索引

1
DELETE person

重新创建索引,此时分词器使用 ik_max_word

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 创建索引,添加映射,指定使用ik分词器
PUT person
{
"mappings": {
"properties": {
"name":{
"type": "keyword"
},
"age":{
"type":"integer"
},
"address":{
"type":"text",
"analyzer": "ik_max_word"
}
}
}
}

查询索引

1
GET person
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"person" : {
"aliases" : { },
"mappings" : {
"properties" : {
"address" : {
"type" : "text",
"analyzer" : "ik_max_word"
},
"age" : {
"type" : "integer"
},
"name" : {
"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
# 添加文档
PUT person/_doc/1
{
"name":"张三",
"age":30,
"address": "北京"
}

# 添加文档
PUT person/_doc/2
{
"name":"李四",
"age":20,
"address": "深圳"
}

# 添加文档
PUT person/_doc/3
{
"name":"张三",
"age":29,
"address": "上海"
}

# 查询所有数据
GET person/_search

再次测试 term,可以查询到了

1
2
3
4
5
6
7
8
9
10
11
# 使用term查询 深圳开头的数据
GET person/_search
{
"query": {
"term": {
"address": {
"value": "深圳"
}
}
}
}