一、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 对比
RDMS | Elasticsearch |
---|
数据库(database) | 索引(index) |
表(table) | 类型(type) |
行(row) | 文档(document) |
列(column) | 字段(field) |
表结构(Schema) | 映射(Mapping) |
索引 | 全文索引 |
SQL | 查询 DSL |
SELECT * from tablename | GET http://….. |
UPDATE table SET | PUT http:// |
DELETE | DELETE http:// |
二、Elasticsearch 核心概念
1、索引 index
文档存储的地方,类似于 MySQL 数据库中的数据库概念
2、类型 type
如果按照关系型数据库中的对应关系,还应该有表的概念。而 ES 中没有表的概念,这是 ES 和数据库的一个区别。
在我们建立索引之后,可以直接往索引中写入文档。
3、域 Field
相当于是数据表的字段,字段在 ES 中可以理解为 JSON 数据的键,下面的 JSON 数据中,name 就是一个字段。
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、判断文档是否存在
如果我们只需要判断文档是否存在,而不是查询文档内容,那么可以这样:
存在返回 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 索引
重新创建索引,此时分词器使用 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 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": "深圳" } } } }
|