二、环境搭建与HTTP指令
2.1 环境搭建
本人学习时,使用的是Windows版的7.8.0的解压版,解压即可使用。
目录 | 含义 |
---|---|
bin | 可执行脚本目录 |
config | 配置目录 |
jdk | 内置 JDK 目录 |
lib | 类库 |
logs | 日志目录 |
modules | 模块目录 |
plugins | 插件目录 |
启动文件为/bin/elasticsearch.bat,双击该文件即可启动服务。
打开浏览器,输入地址:http://localhost:9200,测试结果是否启动成功。
ES的Http访问默认端口是9200
ES7.8.0需要JDK1.8及以上的版本才可以使用。
2.2 倒排索引
2.2.1 ES中的数据结构
在ES中,一条数据就一个文档,它是一个面向文档型数据库。
我们可以类比一下MySQL来理解ES是如何存储数据的。
ES 里的 Index 可以看做一个库,而 Types 相当于表,Documents 则相当于表的行。
注意:这里 Types 的概念已经被逐渐弱化,Elasticsearch 6.X 中,一个 index 下已经只能包含一个
type,Elasticsearch 7.X 中, Type 的概念已经被删除了。
在ES中,存储的数据是Json格式。
{
"name":"张三",
"age":18,
"tel":13111111111
}
2.2.2 什么是倒排索引
在ES中,为了实现能够快速准确的查询文档的内容,ES使用了一个特殊的概念:倒排索引。
当然,与之对应的就是我们经常使用的正排索引。
接下来,我们结合例子来理解什么是倒排索引,假设,有以下两组数据
{
"id":1001,
"content":"This is a test case"
}
{
"id":1002,
"content":"This is a case"
}
当我们使用正排索引时,我们会将数据按照下述方式进行存储
id | content |
---|---|
1001 | This is a test case |
1002 | This is a case |
此时,id作为主键,建立了主键索引,因此我们可以根据id快速的查找对应编号的数据。那么,当我们需要查询内容包含case的数据时,就没有索引可用了,因此效率就会比较慢。
因此我们可以使用倒排索引来优化查询,我们可以将关键字(keyword)和id进行关联。
keyword | id |
---|---|
case | 1001,1002 |
This | 1001,1002 |
test | 1001 |
此时,当我们需要查找内容包含case的数据时,可以先通过keyword索引,查到包含case数据的id,再通过id关联查找到对应的内容。
由上述的介绍,我们其实可以看出,所谓的正排索引就是根据key找到value,倒排索引就是根据value找到key。
2.3 HTTP指令
通过前面章节的介绍,我们知道,ES是一个RESTful风格的全文搜素引擎,因此我们可以直接通过HTTP指令,对ES进行操作。
2.3.1 索引
2.3.1.1 创建索引
向ES服务的9200端口,发送如下PUT请求:http://localhost:9200/索引名,即可创建索引
返回值注释:
{
"acknowledged": true, // 响应结果
"shards_acknowledged": true, // 分片结果
"index": "test" // 索引名称
}
注意:创建索引库的分片数默认 1 片,在 7.0.0 之前的 Elasticsearch 版本中,默认 5 片,且不能重复创建相同名称的索引,否则会报错,报错如下
2.3.1.2 查看索引
-
查看所有索引
向ES服务的9200端口,发送如下GET请求:http://localhost:9200/_cat/indices?v,即可查看当前ES服务中的所有索引。
其中返回值的字段解释如下表所示。
字段 | 含义 |
---|---|
health | 当前服务器健康状态:green(集群完整) yellow(单点正常、集群不完整) red(单点不正常) |
status | 索引打开、关闭状态 |
index | 索引名 |
uuid | 索引编号 |
pri | 主分片数量 |
rep | 副本数量 |
docs.count | 可用文档数量 |
docs.deleted | 处于删除状态的文档(逻辑删除) |
store.size | 主分片和副分片整体占空间大小 |
pri.store.size | 主分片占空间大小 |
-
查看指定索引
向ES服务的9200端口,发送如下GET请求:http://localhost:9200/索引名,即可查看指定的索引
返回值注释:
{
"test": {
// 索引名
"aliases": {
}, // 别名
"mappings": {
}, // 映射
"settings": {
// 设置
"index": {
// 设置-索引
"creation_date": "1678625395428", // 设置-索引-创建时间
"number_of_shards": "1", // 设置-索引-主分片数量
"number_of_replicas": "1", // 设置-索引-副分片数量
"uuid": "lKhZuv6ISHqnSfRCheVg1A", // 设置-索引-唯一标识
"version": {
// 设置-索引-版本
"created": "7080099"
},
"provided_name": "test" // 设置-索引-名称
}
}
}
}
2.3.1.3 删除索引
向ES服务的9200端口,发送如下DELETE请求:http://localhost:9200/索引名,即可删除指定的索引
2.3.2 文档
2.3.2.1 创建文档
通过上述的学习我们知道,在ES中文档可以类比关系型数据库中的表数据,且ES中存储的文档是JSON格式的。因此,我们在向索引中添加文档时,也需要添加JSON格式的数据。
-
不指定主键
向ES服务的9200端口,发送如下POST请求:http/localhost:9200/索引名/_doc,且在请求体中添加如下数据
{ "title":"iPhone 14 pro 256G 玫瑰金", "category":"苹果", "price":10999.00 }
返回值注释:
{
"_index": "test", // 索引名
"_type": "_doc", // 类型-文档
"_id": "aoP21YYBkGp7UZAjbSvt", // 唯一标识,由于添加时没有指定,因此ES会随机生成
"_version": 1, // 版本
"result": "created", // 结果,created表示创建成功
"_shards": {
// 分片
"total": 2, // 分片-总数
"successful": 1, // 分片-成功
"failed": 0 // 分片-失败
},
"_seq_no": 0,
"_primary_term": 1
}
-
指定主键
向ES服务的9200端口,发送如下PUT请求:http/localhost:9200/索引名/_doc/主键号,且在请求体中添加如下数据
{ "title":"iPhone 14 pro 256G 暗夜紫", "category":"苹果", "price":10999.00 }
此时主键id的值就是我们指定的1001
2.3.2.2 查看文档
向ES服务的9200端口,发送如下GET请求:http/localhost:9200/索引名/_doc/主键号,即可查看指定ID的文档
返回值注释:
{
"_index": "test", // 索引名称
"_type": "_doc", // 类型-文档
"_id": "1001", // 主键号
"_version": 1, // 版本
"_seq_no": 13,
"_primary_term": 1,
"found": true, // 查询结果,true为已找到,false为未找到
"_source": {
// 文档源信息
"title": "iPhone 14 pro 256G 暗夜紫",
"category": "苹果",
"price": 10999.00
}
}
2.3.2.3 修改文档
修改文档分为全量修改和局部修改
-
全量修改
所谓的全量修改,就是指,使用新的数据将原有数据覆盖(包括结构)。
全量修改的请求与创建文档-指定主键的请求路径是一样的。
向ES服务的9200端口,发送如下POST请求:http/localhost:9200/索引名/_doc/主键编号,且在请求体中添加如下数据
{ "title":"iPhone 14", "category":"苹果", "price":6999.00 }
返回值中的result为updated就表示本次修改成功,注意,这里的版本号变为了2。
-
局部修改
所谓的局部修改,就是指,只修改原有文档中的某个字段的值。
向ES服务的9200端口,发送如下POST请求:http/localhost:9200/索引名/_update/主键号,且在请求体中添加如下数据
{ "doc":{ "title":"iPhone 14 pro 256G 暗夜紫", "price":10999 } }
返回值中的result为updated就表示本次修改成功,注意,这里的版本号变为了3。
2.3.2.4 删除文档
-
根据主键号删除文档
向ES服务的9200端口,发送如下DELETE请求:http/localhost:9200/索引名/_doc/主键号,即可删除指定id的文档。
返回值中的result为deleted就表示本次删除成功,注意,这里的版本号变为了4,这表示删除一个文档不会立即从磁盘上移除,它只是被标记成已删除(逻辑删除)。
-
根据指定条件删除文档
向ES服务的9200端口,发送如下POST请求:http/localhost:9200/索引名/_delete_by_query,且在请求体中加入以下内容
{ "query":{ "match":{ "price":6999.00 } } }
上述条件表示,删除满足price字段的值为6999.00的文档,上述的条件格式会在下面的高级查询章节进行讲解。
返回值注释:
{
"took": 228, // 本次操作耗时,单位为ms
"timed_out": false, // 是否超时
"total": 4, // 总数
"deleted": 4, // 删除的数量
"batches": 1,
"version_conflicts": 0,
"noops": 0,
"retries": {
"bulk": 0,
"search": 0
},
"throttled_millis": 0,
"requests_per_second": -1.0,
"throttled_until_millis": 0,
"failures": []
}
2.3.3 映射
映射在ES中承担的角色,相当于MySQL里的表结构。
类似于数据库中的表结构,创建数据库表需要设置字段名称,类型,长度,约束等;索引也一样,需要知道这个类型下有哪些字段,每个字段有哪些约束信息,这就叫做映射。
2.3.3.1 创建映射
向ES服务的9200端口,发送如下PUT请求:http/localhost:9200/索引名/_mapping,且在请求体中加入以下内容
{
"properties":{
"name":{
// 类型为text,会分词
"type":"text",
// 该字段是否可以被索引
"index":true
},
"sex":{
// 类型为keyword,不会分词
"type":"keyword",
"index":true
},
"tel":{
"type":"keyword",
"index":false
},
"age":{
// 类型为数值类型
"type":"integer"
}
}
}
映射数据说明:
-
字段名:任意填写,下面指定许多属性,例如:title、subtitle、images、price
-
type:类型,Elasticsearch 中支持的数据类型非常丰富,以下为几个关键的类型
-
String 类型,分为两种,text(可分词)和keyword(不可分词)
类型为text的字段,会被ES根据指定的分析器进行分词处理
类型为keyword的字段,不会被ES分词
-
Numerical 数值类型,分为两类
- 基本数据类型:long、integer、short、byte、double、float、half_float
- 浮点数的高精度类型:scaled_float
-
Date:日期类型
-
Array:数组类型
-
Object:对象
-
-
index:是否索引,默认为 true,如果不进行任何配置,所有字段都会被索引。
- true:字段会被索引,则可以用来进行搜索
- false:字段不会被索引,不能用来搜索
-
store:是否将数据进行独立存储,默认为 false
-
原始的文本会存储在_source 里面,默认情况下其他提取出来的字段都不是独立存储
的,是从_source 里面提取出来的。当然也可以独立的存储某个字段,只要设置
“store”: true 即可,获取独立存储的字段要比从_source 中解析快得多,但是也会占用
更多的空间,所以要根据实际业务需求来设置。
-
-
analyzer:指定分析器,关于分析器,后面会有专门的章节进行讲解。
2.3.3.2 查看映射
向ES服务的9200端口,发送如下GET请求:http/localhost:9200/索引名/_mapping,即可查询映射
2.3.4 高级查询
Elasticsearch 提供了基于 JSON 提供完整的查询 DSL 来定义查询。
我们先在测试索引中添加一些如下数据
{
"name":"ZhangSan",
"sex":"man",
"tel":"13111111111",
"age":22
}
{
"name":"ZhangSan1",
"sex":"woman",
"tel":"13111111111",
"age":24
}
{
"name":"ZhangSan2",
"sex":"woman",
"tel":"13111111111",
"age":24
}
{
"name":"LiSi",
"sex":"man",
"tel":"13111111115",
"age":22
}
{
"name":"WangWu",
"sex":"woman",
"tel":"13111111114",
"age":23
}
{
"name":"ZhaoLiu",
"sex":"woman",
"tel":"13111111112",
"age":24
}
2.3.4.1 查询所有文档
向ES服务的9200端口,发送如下GET请求:http/localhost:9200/索引名/_search,且在请求体中添加如下内容
{
"query": {
"match_all": {
}
}
}
"query"表示一个查询对象,里面可以有不同的查询属性
"match_all"表示查询的类型,例如:match_all(表示查询所有),还有match,term,range等类型,会在后面小节进行介绍。
“match_all”:{},{}中跟着的是查询条件,由于这里是查询所有,因此不需要附加查询条件
返回信息注释:
{
"took": 0, // 查询花费时间,单位毫秒
"timed_out": false, // 是否超时
"_shards": {
// 分片信息
"total": 1, // 分片总数
"successful": 1, // 成功
"skipped": 0, // 忽略
"failed": 0 // 失败
},
"hits": {
// 搜索命中结果
"total": {
// 搜索条件匹配的文档总数
"value": 6, // 总命中计数的值
"relation": "eq" // 计数规则,eq 表示计数准确, gte 表示计数不准确
},
"max_score": 1.0, // 匹配度分值
"hits": [ // 命中结果集合
{
"_index": "test", // 索引名
"_type": "_doc", // 类型是文档
"_id": "WeMF24YBubtchCWfjp6V", // 唯一标识
"_score": 1.0, // 该条记录匹配得分
"_source": {
// 记录数据源
"name": "ZhangSan",
"sex": "man",
"tel": "13111111111",
"age": 22
}
},
...
]
}
}
2.3.4.2 匹配查询
match 匹配类型查询,会把查询条件进行分词,然后进行查询,多个词条之间是 or 的关系
向ES服务的9200端口,发送如下GET请求:http/localhost:9200/索引名/_search,且在请求体中添加如下内容
{
"query":{
"match":{
"name":"zhangsan"
}
}
}
上面的查询条件表示,查询name字段中包含zhangsan的数据
可以看到,返回的结果中只匹配到了一条数据
2.3.4.3 多字段匹配查询
multi_match 与 match 类似,不同的是它可以在多个字段中查询。
向ES服务的9200端口,发送如下GET请求:http/localhost:9200/索引名/_search,且在请求体中添加如下内容
{
"query":{
"multi_match":{
"query":"woman",
"fields":["name","sex"]
}
}
}
上面的查询条件表示,查询name字段或者sex字段中包含woman的数据
2.3.4.4 关键字精确查询
term 查询,精确的关键词匹配查询,不对查询条件进行分词。
向ES服务的9200端口,发送如下GET请求:http/localhost:9200/索引名/_search,且在请求体中添加如下内容
{
"query":{
"term":{
"age":22
}
}
}
或者
{
"query":{
"term":{
"age":{
"value":22
}
}
}
}
上面的查询条件表示,查询age字段值为22的数据
2.3.4.5 多关键字精确匹配
terms 查询和 term 查询一样,但它允许指定多值进行匹配。如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件,相当于MySQL中的in关键字
向ES服务的9200端口,发送如下GET请求:http/localhost:9200/索引名/_search,且在请求体中添加如下内容
{
"query":{
"terms":{
"age":[22,23]
}
}
}
上面的查询条件表示,查询age字段值是22或者23的数据
2.3.4.6 组合查询
在ES中,使用bool关键字,结合must、must_not、should关键字把多个查询组合在一起,形成较为复杂的查询条件。
must表示必须同时满足,相当于AND
must_not表示必须同时不满足,相当于NOT
should表示应该,相当于OR
-
must示例
向ES服务的9200端口,发送如下GET请求:http/localhost:9200/索引名/_search,且在请求体中添加如下内容
{ "query":{ // 条件 "bool":{ // 多个条件必须同时成立,即 AND "must":[ { "match":{ "name":"ZhangSan" } }, { "match":{ "age":22 } } ] } } }
上面的查询条件表示,查询name字段中包含ZhangSan且age字段为22的数据
-
must_not示例
向ES服务的9200端口,发送如下GET请求:http/localhost:9200/索引名/_search,且在请求体中添加如下内容
{ "query":{ // 条件 "bool":{ // 多个条件必须同时不成立,即 NOT "must_not":[ { "match":{ "age":"22" } }, { "match":{ "age":"24" } } ] } } }
上面的查询条件表示,查询age字段不是24且不是22的数据
-
should示例
向ES服务的9200端口,发送如下GET请求:http/localhost:9200/索引名/_search,且在请求体中添加如下内容
{ "query":{ // 条件 "bool":{ // 多个条件其中有一个成立即可,即 OR "should":[ { "match":{ "age":"22" } }, { "match":{ "age":"24" } } ] } } }
上面的查询条件表示,查询age字段是24或者是22的数据
2.3.4.7 范围查询
range查询,查询找出那些落在指定区间内的数字或者时间。
range关键字会搭配以下字符进行使用。
字符 | 说明 |
---|---|
gt | 大于(>) |
gte | 大于等于(>=) |
lt | 小于(<) |
lte | 小于等于(<=) |
向ES服务的9200端口,发送如下GET请求:http/localhost:9200/索引名/_search,且在请求体中添加如下内容
{
"query":{
"range":{
"age":{
"gt":22,
"lt":24
}
}
}
}
上面的查询条件表示,查询age字段的值大于22并且小于24的数据
2.3.4.8 查询指定字段
在默认的情况下,ES的查询结果中,会将命中的数据放在_source中返回给用户。
我们可以在查询参数中指定要查询的字段,以及不想查询的字段。
向ES服务的9200端口,发送如下GET请求:http/localhost:9200/索引名/_search,且在请求体中添加如下内容
{
"_source":["name","age"],
"query":{
"match":{
"name":"ZhangSan"
}
}
}
上面的查询条件表示,查询name字段包含ZhangSan的数据,并且返回的数据只包含name和age字段
我们除了可以在"_source"中简单的指定想要查询的字段,也可以使用includes和excludes来分别指定想要查询以及不想查询的字段。
上面的例子等价于下面的查询
{
"_source":{
"includes":["name","age"],
"excludes":["sex","tel"]
},
"query":{
"match":{
"name":"ZhangSan"
}
}
}或者
{
"_source":{
"excludes":["sex","tel"]
},
"query":{
"match":{
"name":"ZhangSan"
}
}
}
2.3.4.9 排序查询
sort关键字可以让我们按照不同的字段进行排序,并且通过 order 关键字指定排序的方式。desc 降序,asc
升序。
向ES服务的9200端口,发送如下GET请求:http/localhost:9200/索引名/_search,且在请求体中添加如下内容
{
// 指定查询字段,方便观察结果
"_source":["age"],
"query":{
// 表示查询全部数据
"match_all":{
}
},
// 设置排序规则
"sort":{
// 排序的字段
"age":{
// 正序or倒序
"order":"desc"
}
}
}
上面的查询表示,按照age字段倒序排序查询
在ES中,支持多字段排序查询,可以满足根据某个字段降序,再根据某个字段升序查询的需求。
向ES服务的9200端口,发送如下GET请求:http/localhost:9200/索引名/_search,且在请求体中添加如下内容
{
// 指定查询字段,方便观察结果
"_source":["age"],
"query":{
// 表示查询全部数据
"match_all":{
}
},
// 设置排序规则
"sort":{
// 排序的字段
"age":{
// 正序or倒序
"order":"desc"
},
"_score":{
"order":"asc"
}
}
}
上面的查询条件表示,按照age字段的值降序,然后再按照相关性得分升序。
2.3.4.10 分页查询
在ES中,使用from和size关键字完成分页查询。
from表示当前页的起始索引,默认从0开始。计算公式为 (当前页码 - 1) * 页大小
size表示页大小,即每页显示多少条数据。
向ES服务的9200端口,发送如下GET请求:http/localhost:9200/索引名/_search,且在请求体中添加如下内容
{
"query":{
// 表示查询全部数据
"match_all":{
}
},
// 从第几条数据开始,下标以0开始,计算公式:(页码 - 1) * 页大小,例如查第二页,则是(2 - 1) * 2 = 2
"from":0,
// 页大小
"size":2
}
2.3.4.11 聚合查询
在ES中,可以使用关键字aggs结合聚合关键字如terms、max、avg等对文档进行统计分析。
terms表示分组统计,类似MySQL中的group by,注意,这里的terms是使用在aggs中的,要与terms查询关键字区分开来。
max表示求最大值
avg表示求平均值
sum表示求和
cardinality表示去重后求总数
stats会一次性返回count,max,min,avg 和 sum 五个指标
向ES服务的9200端口,发送如下GET请求:http/localhost:9200/索引名/_search,且在请求体中添加如下内容
{
// 聚合操作
"aggs":{
// 统计结果的名称
"age_group":{
// 分组
"terms":{
// 分组字段
"field":"age"
}
},
// 统计结果的名称
"age_avg":{
// 平均值
"avg":{
// 取平均值字段
"field":"age"
}
},
"age_max":{
// 最大值
"max":{
"field":"age"
}
},
"age_min":{
// 最小值
"min":{
"field":"age"
}
},
"age_sum":{
// 求和
"sum":{
"field":"age"
}
},
"age_distinct":{
// 去重后求总数
"cardinality":{
"field":"age"
}
},
"age_stats":{
// stats 聚合,对某个字段一次性返回 count,max,min,avg 和 sum 五个指标
"stats":{
"field":"age"
}
}
}
}
2.3.4.12 高亮查询
Elasticsearch 可以对查询内容中的关键字部分,进行标签和样式(高亮)的设置。
样式可以由开发者自定义
在使用 match 查询的同时,加上一个 highlight 属性,即可实现高亮查询。
- pre_tags:前置标签
- post_tags:后置标签
- fields:需要高亮的字段
- title:这里声明 title 字段需要高亮,后面可以为这个字段设置特有配置,也可以空。
向ES服务的9200端口,发送如下GET请求:http/localhost:9200/索引名/_search,且在请求体中添加如下内容
{
"query":{
"match":{
"name":"ZhangSan"
}
},
"highlight":{
"pre_tags":"<font color='red'>",
"post_tags":"</font>",
"fields":{
"name":{
}
}
}
}
上面的查询条件表示,查询name字段包含ZhangSan且返回值中关键词显示为红色。
参考