The Elastic Stack, 包括 Elasticsearch、Kibana、Beats 和 Logstash(也称为 ELK Stack)。能够安全可靠地获取任何来源、任何格式的数据,然后实时地对数据进行搜索、分析和可视化。Elaticsearch,简称为 ES,ES 是一个开源的高扩展的分布式全文搜索引擎,是整个 Elastic Stack 技术栈的核心。它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理 PB 级别的数据。
提示:以下是本篇文章正文内容,下面案例可供参考
Elasticsearch 的官方地址:https://www.elastic.co/cn/,Elasticsearch 最新的版本是 7.12.0(截止 2021.3.24),我们选择最新版本,下载地址:https://www.elastic.co/cn/downloads/past-releases#elasticsearch,前面一文我已经将Elasticsearch.7.12.0安装虚拟机docker上面了,这里就不说明了。这边用到测试接口的工具是Postman,Postman 官网:https://www.getpostman.com。Elasticsearch 是面向文档型数据库,一条数据在这里就是一个文档。为了方便大家理解,我们将 Elasticsearch 里存储文档数据和关系型数据库 MySQL 存储数据的概念进行一个类比:
ES 里的 Index 可以看做一个库,而 Types 相当于表,Documents 则相当于表的行。这里 Types 的概念已经被逐渐弱化,Elasticsearch 6.X 中,一个 index 下已经只能包含一个type,Elasticsearch 7.X 中, Type 的概念已经被删除了,官方文档还是基于ES2.x,有些方法已经过时了。
创建索引(示例):
在 Postman 中,向 ES 服务器发 PUT 请求 :http://192.168.28.129:9200/fangdada
{
"acknowledged": true, //响应结果
"shards_acknowledged": true, //分片结果
"index": "fangdada" //索引名称
}
# 注意:创建索引库的分片数默认 1 片,在 7.0.0 之前的 Elasticsearch 版本中,默认 5 片
上面我们已经创建一个fangdada的索引,现在我们尝试再次创建,会报错,错误信息显示已经存在了同名索引。
发送GET请求,http://192.168.28.129:9200/_cat/indices?v
_cat 表示查看的意思,indices 表示索引,所以整体含义就是查看当前 ES服务器中的所有索引,就好像 MySQL 中的 show tables 的感觉。
我们来看下各个返回参数的含义:
health 当前服务器健康状态:
green(集群完整) yellow(单点正常、集群不完整) red(单点不正常)
status 索引打开、关闭状态
index 索引名
uuid 索引统一编号
pri 主分片数量
rep 副本数量
docs.count 可用文档数量
docs.deleted 文档删除状态(逻辑删除)
store.size 主分片和副分片整体占空间大小
pri.store.size 主分片占空间大小
GET请求http://192.168.28.129:9200/fangdada,即ip+索引名
来分析下返回的json数据
{
"fangdada"【索引名】: {
"aliases"【别名】: {},
"mappings"【映射】: {},
"settings"【设置】: {
"index"【设置 - 索引】: {
"routing"【设置 - 路由】: {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards"【设置 - 索引 - 主分片数量】: "1",
"provided_name"【设置 - 索引 - 名称】: "fangdada",
"creation_date"【设置 - 索引 - 创建时间】: "1618753338924",
"number_of_replicas"【设置 - 索引 - 副分片数量】: "1",
"uuid"【设置 - 索引 - 唯一标识】: "5G-cwqkzQWikvicfRryIhQ",
"version""【设置 - 索引 - 版本】: {
"created": "7120099"
}
}
}
}
}
发送DELETE请求,http://192.168.28.129:9200/fangdada,执行完索引已经删除,再次访问,服务器放回响应:索引不存在。
向 ES 服务器发 POST 请求 :http://127.0.0.1:9200/fangdada/mobile
fangdada是索引名,type类型名为mobile
分析ES服务器返回的响应体:
{
"_index": "fangdada", //索引名称
"_type": "mobile", //类型
"_id": "03tS5XgBzIZ_cMxMK5B0", //主键ID,随机生成
"_version": 1, //版本
"result": "created", //返回结果created表示创建成功
"_shards": { // 分片
"total": 2, // 分片总数
"successful": 1, // 分片成功数
"failed": 0 // 分片失败数
},
"_seq_no": 0,
"_primary_term": 1
}
这里对于分片的概念可以查看link(elasticsearch 分片(Shards)的理解),如果我们想自定义一个唯一标识,需要在创建时指定:http://192.168.28.129:9200/fangdada/mobile/1001,此时返回的id是1001
向 ES 服务器发 GET 请求 :http://192.168.28.129:9200/fangdada/mobile/1001
向 ES 服务器发 POST 请求 :http://192.168.28.129:9200/fangdada/mobile/1001,这里需要注意的是返回结果是updated,id:1001的小米手机已经存在,所以现在是更新原来的数据。
修改数据时,也可以只修改指定一条数据的某个局部信息
向 ES 服务器发 POST 请求 :http://192.168.28.129:9200/fangdada/_update/1001
请求内容是:
{
"doc":{
"title": "华为手机",
"price": 5000.00
}
}
删除一个文档不会立即从磁盘上移除,它只是被标记成已删除(逻辑删除)。向 ES 服务器发 DELETE 请求 :http://192.168.28.129:9200/fangdada/mobile/1001
首先添加两条数据
{
"title":"小米手机",
"category":"小米",
"images":"http://www.gulixueyuan.com/xm.jpg",
"price":4000.00
}
{
"title":"华为手机",
"category":"华为",
"images":"http://www.gulixueyuan.com/hw.jpg",
"price":4000.00
}
向 ES 服务器发 POST 请求 :http://127.0.0.1:9200/shopping/_delete_by_query,类似批量删除,匹配到price为4000的全部删除,这里注意要使用POST请求,而且方法名是_delete_by_query。
{
"query": {
"match": {
"price": 4000.00
}
}
}
有了索引库,等于有了数据库中的 database。接下来就需要建索引库(index)中的映射了,类似于数据库(database)中的表结构(table)。创建数据库表需要设置字段名称,类型,长度,约束等;索引库也一样,需要知道这个类型下有哪些字段,每个字段有哪些约束信息,这就叫做映射(mapping)。
向 ES 服务器发 PUT 请求 :http://127.0.0.1:9200/student/_mapping,如果这里创建不成功,则需要先创建student索引,然后再创建应映射。
请求体内容:
{
"properties": {
"name": {
"type": "text",
"index": true
},
"sex": {
"type": "text",
"index": false
},
"age": {
"type": "long",
"index": false
}
}
}
映射数据说明
字段名:任意填写,下面指定许多属性,例如:title、subtitle、images、price
type:类型,Elasticsearch 中支持的数据类型非常丰富,说几个关键的
String 类型,又分两种 text:可分词 keyword:不可分词,数据会作为完整字段进行匹配
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:分词器,这里的 ik_max_word 即使用 ik 分词器
向 ES 服务器发 GET 请求 :http://192.168.28.129:9200/fangdada/_mapping
向 ES 服务器发 PUT 请求 :http://192.168.28.129:9200/student1
请求体内容:
{
"settings": {},
"mappings": {
"properties": {
"name": {
"type": "text",
"index": true
},
"sex": {
"type": "text",
"index": false
},
"age": {
"type": "long",
"index": false
}
}
}
}
Elasticsearch 提供了基于 JSON 提供完整的查询 DSL 来定义查询
首先我们来定义一些数据:
# POST http://192.168.28.129:9200/student/_doc/1001
{
"name": "zhangsan",
"nickname": "zhangsan",
"sex": "男",
"age": 30
}
# POST http://192.168.28.129:9200/student/_doc/1002
{
"name": "lisi",
"nickname": "lisi",
"sex": "男",
"age": 20
}
# POST http://192.168.28.129:9200/student/_doc/1003
{
"name": "wangwu",
"nickname": "wangwu",
"sex": "女",
"age": 40
}
# POST http://192.168.28.129:9200/student/_doc/1004
{
"name": "zhangsan1",
"nickname": "zhangsan1",
"sex": "女",
"age": 50
}
# POST http://192.168.28.129:9200/student/_doc/1005
{
"name": "zhangsan2",
"nickname": "zhangsan2",
"sex": "女",
"age": 30
}
向 ES 服务器发 GET 请求 :http://192.168.28.129:9200/student/_search
这边简单介绍下查询条件
{
"query":{
"match_all":{}
}
}
# "query":这里的 query 代表一个查询对象,里面可以有不同的查询属性
# "match_all":查询类型,例如:match_all(代表查询所有), match,term , range 等等
# {查询条件}:查询条件会根据类型的不同,写法也有差异
我们来分析下返回的响应体
{
"took【查询花费时间,单位毫秒】": 2,
"timed_out【是否超时】": false,
"_shards"【分片信息】: {
"total"【总数】: 1,
"successful"【成功】: 1,
"skipped"【忽略】: 0,
"failed"【失败】: 0
},
"hits"【搜索命中结果】: {
"total"【搜索条件匹配的文档总数】: {
"value" 【总命中计数的值】: 5,
"relation"【计数规则】: "eq" # eq 表示计数准确, gte 表示计数不准确
},
"max_score"【匹配度分值】: 1.0,
"hits"【命中结果集合】: []
}
}
match 匹配类型查询,会把查询条件进行分词,然后进行查询,多个词条之间是 or 的关系,在 Postman 中,向 ES 服务器发 GET 请求 :http://192.168.28.129:9200/student/_search
单个词条请求内容
{
"query":{
"match":{
"name": "lisi"
}
}
}
多个词条进行or查询,这里查询条件是匹配姓名是lisi或者zhangsan,多个词条切记需要用空格隔开。
multi_match 与 match 类似,不同的是它可以在多个字段中查询。向 ES 服务器发 GET 请求http://192.168.28.129:9200/student/_search
请求体内容,这个意思是匹配字段name或者nickname值为zhangsan的。
{
"query": {
"multi_match": {
"query": "zhangsan",
"fields": [
"name",
"nickname"
]
}
}
}
term 查询,精确的关键词匹配查询,不对查询条件进行分词。
在 Postman 中,向 ES 服务器发 GET 请求 :http://192.168.28.129:9200/student/_search
{
"query": {
"term": {
"name": {
"value": "zhangsan"
}
}
}
}
terms 查询和 term 查询一样,但它允许你指定多值进行匹配。
如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件,类似于 mysql 的 in查询,向 ES 服务器发 GET 请求 :http://192.168.28.129:9200/student/_search
{
"query": {
"terms": {
"name": [
"zhangsan",
"lisi"
]
}
}
}
默认情况下,Elasticsearch 在搜索的结果中,会把文档中保存在_source 的所有字段都返回。如果我们只想获取其中的部分字段,我们可以添加_source 的过滤,在 Postman 中,向 ES 服务器发 GET 请求 :http://192.168.28.129:9200/student/_search,这边只查询name和nickname字段,查询条件精确查询nickname=zhangsan的文档。
{
"_source": [
"name",
"nickname"
],
"query": {
"terms": {
"nickname": [
"zhangsan"
]
}
}
}
我们也可以通过:
includes:来指定想要显示的字段
excludes:来指定不想要显示的字段
向 ES 服务器发 GET 请求 :http://192.168.28.129:9200/student/_search
{
"_source": {
"includes": [
"name",
"nickname"
]
},
"query": {
"terms": {
"nickname": [
"zhangsan"
]
}
}
}
bool
把各种其它查询通过must
(必须 )、must_not
(必须不)、should
(应该)的方式进行组合。在 Postman 中,向 ES 服务器发 GET 请求 :http://192.168.28.129:9200/student/_search。
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "zhangsan"
}
}
],
"must_not": [
{
"match": {
"age": "40"
}
}
],
"should": [
{
"match": {
"sex": "男"
}
}
]
}
}
}
这里会发现报错了,因为设置映射时候我们把age和sex的index值设置了false,禁止索引,不能通过索引来查询,查询相关文档,映射无法删除。没有办法,只能删除student索引,再次创建时student索引和_mapping映射
。
range 查询找出那些落在指定区间内的数字或者时间。range 查询允许以下字符
操作符 说明
gt 大于>
gte 大于等于>=
lt 小于<
lte 小于等于<=
匹配年龄大于等于30小于等于35的数据
{
"query": {
"range": {
"age": {
"gte": 30,
"lte": 35
}
}
}
}
返回包含与搜索字词相似的字词的文档。编辑距离是将一个术语转换为另一个术语所需的一个字符更改的次数。这些更改可以包括:
更改字符(box → fox)
删除字符(black → lack)
插入字符(sic → sick)
转置两个相邻字符(act → cat)
为了找到相似的术语,fuzzy 查询会在指定的编辑距离内创建一组搜索词的所有可能的变体或扩展。然后查询返回每个扩展的完全匹配。通过 fuzziness 修改编辑距离。一般使用默认值 AUTO,根据术语的长度生成编辑距离。
{
"query": {
"fuzzy": {
"name": {
"value": "zhangsan"
}
}
}
}
sort 可以让我们按照不同的字段进行排序,并且通过 order 指定排序的方式。desc 降序,asc升序。mysql中我们使用select xxx from table where 条件 order by 字段 desc
,默认是asc排序,这边根据模糊查询zahngsan,并且根据age降序。
{
"query": {
"fuzzy": {
"name": "zhangsan"
}
},
"sort": [
{
"age": {
"order": "desc"
}
}
]
}
假定我们想要结合使用 age 和 _score 进行查询,并且匹配的结果首先按照年龄排序,然后按照相关性得分排序。向 ES 服务器发 GET 请求 :http://192.168.28.129:9200/student/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"age": {
"order": "desc"
}
},
{
"_score": {
"order": "desc"
}
}
]
}
在进行关键字搜索时,搜索出的内容中的关键字会显示不同的颜色,称之为高亮。
Elasticsearch 可以对查询内容中的关键字部分,进行标签和样式(高亮)的设置。
在使用 match 查询的同时,加上一个 highlight 属性:
pre_tags:前置标签
post_tags:后置标签
fields:需要高亮的字段
title:这里声明 title 字段需要高亮,后面可以为这个字段设置特有配置,也可以空
向 ES 服务器发 GET 请求 :http://192.168.28.129:9200/student/_search
{
"query": {
"match": {
"name": "lisi"
}
},
"highlight": {
"pre_tags": "<font color='red'>",
"post_tags": "font>",
"fields": {
"name": {}
}
}
}
from:当前页的起始索引,默认从 0 开始。 from = (pageNum - 1) * size,size:每页显示多少条,这边查询第一页,每页显示两条,年龄降序排序。
{
"query": {
"match_all": {}
},
"sort": [
{
"age": {
"order": "desc"
}
}
],
"from": 0,
"size": 2
}
聚合允许使用者对 es 文档进行统计分析,类似与关系型数据库中的 group by,当然还有很多其他的聚合,例如取最大值、平均值等等。
{
"aggs": {
"max_age": {
"max": {
"field": "age"
}
}
},
"size": 0
}
{
"aggs": {
"min_age": {
"min": {
"field": "age"
}
}
},
"size": 0
}
{
"aggs": {
"sum_age": {
"sum": {
"field": "age"
}
}
},
"size": 0
}
{
"aggs": {
"avg_age": {
"avg": {
"field": "age"
}
}
},
"size": 0
}
{
"aggs": {
"distinct_age": {
"cardinality": {
"field": "age"
}
}
},
"size": 0
}
{
"aggs": {
"stats_age": {
"stats": {
"field": "age"
}
}
},
"size": 0
}
桶聚和相当于 sql 中的 group by 语句
{
"aggs": {
"age_groupby": {
"terms": {
"field": "age"
}
}
},
"size": 0
}
{
"aggs": {
"age_groupby": {
"terms": {
"field": "age"
}
}
},
"size": 0
}