演示使用的数据:
```json
#创建student索引
PUT /student
#添加下面数据用于演示
POST /student/_doc/1001
{
"name": "zhangsan",
"nickname": "zhangsan",
"sex": "男",
"age": 30
}
POST /student/_doc/1002
{
"name": "lisi",
"nickname": "lisi",
"sex": "男",
"age": 20
}
POST /student/_doc/1003
{
"name": "wangwu",
"nickname": "wangwu",
"sex": "女",
"age": 40
}
POST /student/_doc/1004
{
"name": "zhangsan01",
"nickname": "zhangsan01",
"sex": "女",
"age": 50
}
POST /student/_doc/1005
{
"name": "zhangsan02",
"nickname": "zhangsan02",
"sex": "女",
"age": 30
}
```
**1. 查询一个索引的所有文档**
```json
#写法1
GET /student/_search
#写法2
GET /student/_search
{
"query": {
"match_all": {}
}
}
```
结果如下:
```json
{
"took" : 0, #查询花销的时间
"timed_out" : false, #是否超时,false-不超时
"_shards" : { #分片信息
"total" : 1, #分片总数
"successful" : 1, #分片成功次数
"skipped" : 0, #分片被忽略次数
"failed" : 0 #分片失败次数
},
"hits" : { #搜索命中结果
"total" : { #搜索条件匹配文档总数
"value" : 5, #条件命中条数
"relation" : "eq" #计数规则,eq-计数准确、gte-计数不准确
},
"max_score" : 1.0, #匹配度分值
"hits" : [ #命中结果集合
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1001",
"_score" : 1.0,
"_source" : {
"name" : "zhangsan",
"nickname" : "zhangsan",
"sex" : "男",
"age" : 30
}
},
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1002",
"_score" : 1.0,
"_source" : {
"name" : "lisi",
"nickname" : "lisi",
"sex" : "男",
"age" : 20
}
},
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1003",
"_score" : 1.0,
"_source" : {
"name" : "wangwu",
"nickname" : "wangwu",
"sex" : "女",
"age" : 40
}
},
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1004",
"_score" : 1.0,
"_source" : {
"name" : "zhangsan01",
"nickname" : "zhangsan01",
"sex" : "女",
"age" : 50
}
},
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1005",
"_score" : 1.0,
"_source" : {
"name" : "zhangsan02",
"nickname" : "zhangsan02",
"sex" : "女",
"age" : 30
}
}
]
}
}
```
**2. 匹配查询**
`match` 匹配类型查询,会把查询条件进行分词,然后进行查询,多个词条之间是 or 的关系。
```json
#写法1
GET /student/_search?q=name:zhangsan
#写法2
GET /student/_search
{
"query": {
"match": {
"name": "zhangsan"
}
}
}
```
结果如下:
```json
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.3862942,
"hits" : [
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1001",
"_score" : 1.3862942,
"_source" : {
"name" : "zhangsan",
"nickname" : "zhangsan",
"sex" : "男",
"age" : 30
}
}
]
}
}
```
**3. 字段匹配查询**
`multi_match` 与 `match` 类似,不同的是它可以在多个字段中查询,多个字段是 OR 关系。
```json
GET /student/_search
{
"query": {
"multi_match": {
"query": "zhangsan",
"fields": ["name", "nickname"] #name=zhangsan 或者 nickname=zhangsan
}
}
}
```
结果如下:
```json
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.540445,
"hits" : [
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1001",
"_score" : 1.540445,
"_source" : {
"name" : "zhangsan",
"nickname" : "zhangsan",
"sex" : "男",
"age" : 30
}
}
]
}
}
```
**4. 关键字精准查询**
`term` 查询,精确的关键词匹配查询,不对查询条件进行分词。
```json
GET /student/_search
{
"query": {
"term": {
"name": {
"value": "zhangsan"
}
}
}
}
```
结果如下:
```json
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.3862942,
"hits" : [
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1001",
"_score" : 1.3862942,
"_source" : {
"name" : "zhangsan",
"nickname" : "zhangsan",
"sex" : "男",
"age" : 30
}
}
]
}
}
```
**5. 多关键字精确查询**
`terms` 查询和 `term` 查询一样,但它允许你指定多值进行匹配。
```json
GET /student/_search
{
"query": {
"terms": {
"name": ["zhangsan", "lisi"] #name=zhangsan 或者name=lisi
}
}
}
```
解决如下:
```json
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1001",
"_score" : 1.0,
"_source" : {
"name" : "zhangsan",
"nickname" : "zhangsan",
"sex" : "男",
"age" : 30
}
},
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1002",
"_score" : 1.0,
"_source" : {
"name" : "lisi",
"nickname" : "lisi",
"sex" : "男",
"age" : 20
}
}
]
}
}
```
**6. 只查询指定字段**
默认情况下,Elasticsearch 在搜索的结果中,会把文档中保存在`_source` 的所有字段都返回。
如果我们只想获取其中的部分字段,我们可以添加`_source` 的过滤。
```json
#写法1
GET /student/_search?q=nickname:zhangsan&_source=name,nickname
#写法2
GET /student/_search
{
"_source": ["name", "nickname"], #只查询name和nickname字段
"query": {
"terms": {
"nickname": ["zhangsan"] #条件是nickname=zhangsan
}
}
}
```
结果如下:
```json
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.3862942,
"hits" : [
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1001",
"_score" : 1.3862942,
"_source" : {
"name" : "zhangsan",
"nickname" : "zhangsan"
}
}
]
}
}
```
**7. 过滤字段**
`includes`:来指定想要显示的字段。
`excludes`:来指定不想要显示的字段。
(1)只查询`includes`指定的字段。
```json
GET /student/_search
{
"_source": {
"includes": ["name", "nickname"] #只查询name、nickname字段
},
"query": {
"terms": {
"nickname": ["zhangsan", "lisi"] #条件是nickname=zhangsan 或者niciname=lisi
}
}
}
```
结果如下:
```json
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1001",
"_score" : 1.0,
"_source" : {
"name" : "zhangsan",
"nickname" : "zhangsan"
}
},
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1002",
"_score" : 1.0,
"_source" : {
"name" : "lisi",
"nickname" : "lisi"
}
}
]
}
}
```
(2)除了`excludes`指定的字段不查询,其他字段都查询。
```json
GET /student/_search
{
"_source": {
"excludes": ["name", "nickname"] #不查询name、nickname字段
},
"query": {
"terms": {
"nickname": ["zhangsan", "lisi"] #条件是nickname=zhangsan 或者nickname=lisi
}
}
}
```
结果如下:
```json
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1001",
"_score" : 1.0,
"_source" : {
"sex" : "男",
"age" : 30
}
},
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1002",
"_score" : 1.0,
"_source" : {
"sex" : "男",
"age" : 20
}
}
]
}
}
```
**8. 组合查询**
`bool`把各种其它查询通过`must`(必须 )、`must_not`(必须不)、`should`(应该)的方式进行组合。
```json
GET /student/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "zhangsan"
}
}
],
"must_not": [
{
"match": {
"age": 40
}
}
],
"should": [
{
"match": {
"sex": "男"
}
}
]
}
}
}
```
结果如下:
```json
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 2.261763,
"hits" : [
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1001",
"_score" : 2.261763,
"_source" : {
"name" : "zhangsan",
"nickname" : "zhangsan",
"sex" : "男",
"age" : 30
}
}
]
}
}
```
**9. 范围查询**
`range` 查询找出那些落在指定区间内的数字或者时间。`range` 查询允许以下字符。
```
gt 大于(>)
gte 大于等于(>=)
lt 小于(<)
lte 小于等于(<=)
```
```json
#查询age在[30, 35]区间的文档
GET /student/_search?q=age[30 TO 35]
#查询age>=30的文档
GET /student/_search?q=age:>=40
#查询age在[30, 35]区间的文档
GET /student/_search
{
"query": {
"range": {
"age": {
"gte": 30,
"lte": 35
}
}
}
}
```
查询age在[30, 35]区间结果如下:
```json
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1001",
"_score" : 1.0,
"_source" : {
"name" : "zhangsan",
"nickname" : "zhangsan",
"sex" : "男",
"age" : 30
}
},
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1005",
"_score" : 1.0,
"_source" : {
"name" : "zhangsan02",
"nickname" : "zhangsan02",
"sex" : "女",
"age" : 30
}
}
]
}
}
```
**10. 模糊查询**
返回包含与搜索字词相似的字词的文档。
编辑距离是将一个术语转换为另一个术语所需的一个字符更改的次数。这些更改可以包括:
* 更改字符(box → fox)
* 删除字符(black → lack)
* 插入字符(sic → sick)
* 转置两个相邻字符(act → cat)
为了找到相似的术语,`fuzzy` 查询会在指定的编辑距离内创建一组搜索词的所有可能的变体或扩展。然后查询返回每个扩展的完全匹配。
通过 `fuzziness` 修改编辑距离。一般使用默认值 AUTO,根据术语的长度生成编辑距离。
```json
GET /student/_search
{
"query": {
"fuzzy": {
"name": {
"value": "zhangsan"
}
}
}
}
GET /student/_search
{
"query": {
"fuzzy": {
"name": {
"value": "zhangsan"
, "fuzziness": 1
}
}
}
}
```
结果如下 :
```json
#########不指定编辑距离fuzziness的结果#########
{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 1.3862942,
"hits" : [
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1001",
"_score" : 1.3862942,
"_source" : {
"name" : "zhangsan",
"nickname" : "zhangsan",
"sex" : "男",
"age" : 30
}
},
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1004",
"_score" : 1.0397208,
"_source" : {
"name" : "zhangsan01",
"nickname" : "zhangsan01",
"sex" : "女",
"age" : 50
}
},
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1005",
"_score" : 1.0397208,
"_source" : {
"name" : "zhangsan02",
"nickname" : "zhangsan02",
"sex" : "女",
"age" : 30
}
}
]
}
}
#########指定编辑距离fuzziness为1的结果#########
{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.3862942,
"hits" : [
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1001",
"_score" : 1.3862942,
"_source" : {
"name" : "zhangsan",
"nickname" : "zhangsan",
"sex" : "男",
"age" : 30
}
}
]
}
}
```
**11. 字段排序**
```json
#先根据age降序排序,再根据_score字段降序排序
GET /student/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"age": {
"order": "desc" #desc是降序,asc是升序
}
},
{
"_score": {
"order": "desc"
}
}
]
}
```
结果如下:
```json
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1004",
"_score" : 1.0,
"_source" : {
"name" : "zhangsan01",
"nickname" : "zhangsan01",
"sex" : "女",
"age" : 50
},
"sort" : [
50,
1.0
]
},
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1003",
"_score" : 1.0,
"_source" : {
"name" : "wangwu",
"nickname" : "wangwu",
"sex" : "女",
"age" : 40
},
"sort" : [
40,
1.0
]
},
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1001",
"_score" : 1.0,
"_source" : {
"name" : "zhangsan",
"nickname" : "zhangsan",
"sex" : "男",
"age" : 30
},
"sort" : [
30,
1.0
]
},
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1005",
"_score" : 1.0,
"_source" : {
"name" : "zhangsan02",
"nickname" : "zhangsan02",
"sex" : "女",
"age" : 30
},
"sort" : [
30,
1.0
]
},
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1002",
"_score" : 1.0,
"_source" : {
"name" : "lisi",
"nickname" : "lisi",
"sex" : "男",
"age" : 20
},
"sort" : [
20,
1.0
]
}
]
}
}
```
**12. 高亮查询**
高亮查询由`highlight`属性提供,它由下面几个标签组合完成高亮查询。
```
pre_tags:前置标签
post_tags:后置标签
fields:需要高亮的字段
title:这里声明 title 字段需要高亮,后面可以为这个字段设置特有配置,也可以空
```
```json
GET /student/_search
{
"query": {
"match": {
"name": "zhangsan" #在这里指定name,则只有name字段可以高亮显示
}
},
"highlight": {
"pre_tags": "<font color='red'>",
"post_tags": "</font>",
"fields": {
"name": {}
}
}
}
```
结果如下:
```json
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.3862942,
"hits" : [
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1001",
"_score" : 1.3862942,
"_source" : {
"name" : "zhangsan",
"nickname" : "zhangsan",
"sex" : "男",
"age" : 30
},
"highlight" : {
"name" : [
"<font color='red'>zhangsan</font>"
]
}
}
]
}
}
```
返回json数据看不出高亮效果,我用Postman查询结果如下:
![](https://img.kancloud.cn/88/39/8839c823073c18d8470bcc47fe616524_1410x216.png)
**13. 分页查询**
`from`:当前页的起始索引,默认从 0 开始。 `$ from = (pageNum - 1) * size $`,pageNum为当前第几页。
`size`:每页显示多少条。
```json
GET /student/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"age": {
"order": "desc"
}
}
],
"from": 0,
"size": 2
}
```
结果如下:
```json
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1004",
"_score" : null,
"_source" : {
"name" : "zhangsan01",
"nickname" : "zhangsan01",
"sex" : "女",
"age" : 50
},
"sort" : [
50
]
},
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1003",
"_score" : null,
"_source" : {
"name" : "wangwu",
"nickname" : "wangwu",
"sex" : "女",
"age" : 40
},
"sort" : [
40
]
}
]
}
}
```
**14. 聚合查询**
聚合允许使用者对 es 文档进行统计分析,类似与关系型数据库中的 group by,当然还有很
多其他的聚合,例如取最大值、平均值等等。
(1)求最大值、最小值等。
```json
GET /student/_search
{
"aggs": {
"max_age": { #max_age是别名,自由定义即可
"max": { #对age字段求最大值
"field": "age"
}
}
},
"size": 0
}
GET /student/_search
{
"aggs": {
"min_age": {
"min": {"field": "age"} #对age字段求最小值
}
},
"size": 0
}
GET /student/_search
{
"aggs": {
"sum_age": {
"sum": {"field": "age"} #对age字段求和
}
},
"size": 0
}
GET /student/_search
{
"aggs": {
"avg_age": {
"avg": {"field": "age"} #对age字段求平均值
}
},
"size": 0
}
GET /student/_search
{
"aggs": {
"distinct_age": {
"cardinality": {"field": "age"} #对age字段的值进行去重之后再取总数
}
},
"size": 0
}
```
结果我这里就不写出来了。
(2)`state`聚合。对某个字段一次性返回 count,max,min,avg 和 sum 五个指标
```json
GET /student/_search
{
"aggs": {
"stats_age": {
"stats": {"field": "age"}
}
},
"size": 0
}
```
结果如下:
```json
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"stats_age" : {
"count" : 5,
"min" : 20.0,
"max" : 50.0,
"avg" : 34.0,
"sum" : 170.0
}
}
}
```
**15. 桶聚合查询**
桶聚和相当于 sql 中的 group by 语句。
(1)分组。
```json
GET /student/_search
{
"aggs": {
"age_groupby": {
"terms": {"field": "age"}
}
},
"size": 0
}
```
结果如下:
```json
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"age_groupby" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 30,
"doc_count" : 2 #30岁这一组的人数有两个
},
{
"key" : 20,
"doc_count" : 1
},
{
"key" : 40,
"doc_count" : 1
},
{
"key" : 50,
"doc_count" : 1
}
]
}
}
}
```
(2)分组聚合。
```json
#先分组,再对每一个组求和
GET /student/_search
{
"aggs": {
"age_groupby": {
"terms": {"field": "age"},
"aggs": {
"sum_age": {
"sum": {"field": "age"}
}
}
}
},
"size": 0
}
```
结果如下:
```json
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"age_groupby" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 30,
"doc_count" : 2,
"sum_age" : {
"value" : 60.0 #30岁这一组的求和结果为60
}
},
{
"key" : 20,
"doc_count" : 1,
"sum_age" : {
"value" : 20.0
}
},
{
"key" : 40,
"doc_count" : 1,
"sum_age" : {
"value" : 40.0
}
},
{
"key" : 50,
"doc_count" : 1,
"sum_age" : {
"value" : 50.0
}
}
]
}
}
}
```
- Elasticsearch是什么
- 全文搜索引擎
- Elasticsearch与Solr
- 数据结构
- 安装Elasticsearch
- Linux单机安装
- Windows单机安装
- 安装Kibana
- Linux安装
- Windows安装
- es基本语句
- 索引操作
- 文档操作
- 映射操作
- 高级查询
- es-JavaAPI
- maven依赖
- 索引操作
- 文档操作
- 高级查询
- es集群搭建
- Linux集群搭建
- Windows集群搭建
- 核心概念
- 索引(Index)
- 类型(Type)
- 文档(Document)
- 字段(Field)
- 映射(Mapping)
- 分片(Shards)
- 副本(Replicas)
- 分配(Allocation)
- 系统架构
- 分布式集群
- 单节点集群
- 故障转移
- 水平扩容
- 应对故障
- 路由计算
- 分片控制
- 写流程
- 读流程
- 更新流程
- 多文档操作流程
- 分片原理
- 倒排索引
- 文档搜索
- 动态更新索引
- 近实时搜索
- 持久化变更
- 段合并
- 文档分析
- 内置分析器
- 分析器使用场景
- 测试分析器
- 指定分析器
- 自定义分析器
- 文档处理
- 文档冲突
- 乐观并发控制
- 外部系统版本控制
- es优化
- 硬件选择
- 分片策略
- 合理设置分片数
- 推迟分片分配
- 路由选择
- 写入速度优化
- 批量数据提交
- 优化存储设备
- 合理使用合并
- 减少Refresh的次数
- 加大Flush设置
- 减少副本的数量
- 内存设置
- 重要配置
- es常见问题
- 为什么要使用Elasticsearch
- master选举流程
- 集群脑裂问题
- 索引文档流程
- 更新和删除文档流程
- 搜索流程
- ES部署在Linux时的优化方法
- GC方面ES需要注意的点
- ES对大数据量的聚合实现
- 并发时保证读写一致性
- 字典树
- ES的倒排索引
- Spring Data Elasticsearch
- 环境搭建
- 索引操作
- 文档操作