1.1.1. 官网 : Elasticsearch: 权威指南 | Elastic
1.1.2. 查考视频 : 黑马Elasticsearch
1. ES 介绍
1.1.1. 介绍
Elasticsearch 是一个高度可扩展的开源实时搜索和分析引擎,它允许用户在近实时的时间内执行全文搜索、结构化搜索、聚合、过滤等功能。Elasticsearch 基于 Lucene 构建,提供了强大的全文搜索功能,并且具有广泛的应用领域,包括日志和实时分析、社交媒体、电子商务等。
1.1.2. ElasticSearch的作用
- 虽然不是每个问题都是搜索问题,但Elasticsearch在各种用例中提供了处理数据的速度和灵活性:
- 为APP或网站增加搜索功能
- 存储和分析日志、指标和安全事件数据
- 使用机器学习实时自动建模数据的行为
- 使用Elasticsearch作为存储引擎自动化业务工作流
- 使用Elasticsearch作为地理信息系统(GIS)管理、集成和分析空间信息
- 使用Elasticsearch作为生物信息学研究工具存储和处理遗传数据
基本上,Elasticsearch已经渗透到了我们工作和生活的方方面面。我们打开电商网站搜索商品、打开APP查询资料,或者工作上使用EFK搭建日志系统等,这背后都有Elasticsearch的贡献。对了,GitHub的搜索功能也是基于Elasticsearch构建起来的。
1.1.3. 架构与工作原理
1.1.3.1. 架构概述
Elasticsearch 架构主要由三个组件构成:索引、分片和节点。
- 索引是文档的逻辑分组,类似于数据库中的表;
- 分片是索引的物理分区,用于提高数据分布和查询性能;
- 节点是运行 Elasticsearch 的服务器实例。
1.1.3.2. 工作原理
Elasticsearch 通过以下步骤完成搜索和分析任务:
- 接收用户查询请求:Elasticsearch 通过 RESTful API 或 JSON 请求接收用户的查询请求。
- 路由请求:接收到查询请求后,Elasticsearch 根据请求中的索引和分片信息将请求路由到相应的节点。
- 执行查询:节点执行查询请求,并在相应的索引中查找匹配的文档。
- 返回结果:查询结果以 JSON 格式返回给用户,包括匹配的文档和相关字段信息。
1.1.4. ELK技术栈
ElasticSearch结合kibana、Logstash、Beats,也就是elastic stack(ELK)。被广泛应用在日志数据分析、实时监控等领域- 而
ElasticSearch是elastic stack的核心,负责存储、搜索、分析数据
1.1.5. ElasticSearch和Lucene的关系
- ElasticSearch底层是基于Lucene来实现的
- Lucene是一个Java语言的搜索引擎类库,是Apache公司的顶级项目
- Lucene的优势是 易扩展高性能(基于倒排索引)
- Lucene的缺点 只限于Java语言开发 , 学习曲线陡峭 , 不支持水平扩展
- 相比于Lucene,ElasticSearch具备以下优势
-
- 支持分布式,可水平扩展
- 提供Restful接口,可以被任意语言调用
2. ES 的倒排索引
- 倒排索引的概念是基于MySQL这样的正向索引而言的
2.1. 概念
倒排索引中有两个非常重要的概念
- 文档(Document):用来搜索的数据,其中的每一条数据就是一个文档。例如一个网页、一个商品信息
- 词条(Term):对文档数据或用户搜索数据,利用某种算法分词,得到的具备含义的词语就是词条。例如:我最喜欢的FPS游戏是Apex,就可以分为我、我最喜欢、FPS游戏、最喜欢的FPS、Apex这样的几个词条
- 创建倒排索引是对正向索引的一种特殊处理,流程如下
-
- 将每一个文档的数据利用算法分词,得到一个个词条
- 创建表,每行数据包括词条、词条所在文档id、位置等信息
- 因为词条唯一性,可以给词条创建索引,例如hash表结构索引
| 词条(term) | 文档id |
|---|---|
| 小米 | 1,3,4 |
| 手机 | 1,2 |
| 华为 | 2,3 |
| 充电器 | 3 |
| 手环 | 4 |
例如
- 用户输入条件 华为手机,进行搜索。
- 对用户输入的内容分词,得到词条:华为、手机。
- 拿着词条在倒排索引中查找,可以得到包含词条的文档id为:1、2、3。
- 拿着文档id到正向索引中查找具体文档
虽然要先查询倒排索引,再查询正向索引,但是无论是词条还是文档id,都建立了索引,所以查询速度非常快,无需全表扫描
2.2. 对比
那么为什么一个叫做正向索引,一个叫做倒排索引呢?
-
正向索引是最传统的,根据id索引的方式。但是根据词条查询是,必须先逐条获取每个文档,然后判断文档中是否包含所需要的词条,是根据文档查找词条的过程- 而
倒排索引则相反,是先找到用户要搜索的词条,然后根据词条得到包含词条的文档id,然后根据文档id获取文档,是根据词条查找文档的过程
那么二者的优缺点各是什么呢?
正向索引
-
- 优点:可以给多个字段创建索引,根据索引字段搜索、排序速度非常快
- 缺点:根据非索引字段,或者索引字段中的部分词条查找时,只能全表扫描
倒排索引
-
-
优点:根据词条搜索、模糊搜索时,速度非常快
-
缺点:只能给词条创建索引,而不是字段,无法根据字段做排序
-
3. ES 基本概念
3.1. 文档与字段
- ElasticSearch是面向文档(Document)存储的,可以是数据库中的一条商品数据,一个订单信息。文档数据会被序列化为json格式后存储在ElasticSearch中 ,而Json文档中往往包含很多的字段(Field),类似于数据库中的列
3.1.1. 文档
文档是Elasticsearch中存储和检索的基本单位,它是序列化为JSON格式的数据结构。每个文档都有一个唯一的标识符,称为_id字段,用于唯一标识该文档。每个文档都存储在一个索引中,并且可以包含多个字段,这些字段可以是不同的数据类型,如文本、数字、日期等常间的基础字段
文档的属性包括_index、_type和_source等。_index表示文档所属的索引名称,_type表示文档所属的类型名称 , _source表示文档的原始JSON数据。
3.1.2. 字段( type )
3.1.2.1. 字符串
- Text: 用于全文搜索(分词)。
- Keyword: 用于精确匹配(不分词)。
3.1.2.2. 数值
- Long: 64 位有符号整数。
- Integer: 32 位有符号整数。
- Short: 16 位有符号整数。
- Byte: 8 位有符号整数。
- Double: 64 位双精度浮点数。
- Float: 32 位单精度浮点数。
- Boolean: 布尔值(
true/false)。
3.1.2.3. 日期
- Date: 日期字段,支持多种日期格式(ISO 8601)。
- Date_nanos: 纳秒精度的日期字段。
3.1.2.4. 其他
- Binary: 二进制数据(Base64 编码)。
- Object: 嵌套对象(键值对的集合)。
- Nested: 类似对象,但专门用于数组的嵌套文档,支持独立索引。
还包含 专用数据类型 : IP , Geo_point 等等 ,
3.2. 索引和映射
3.2.1. 索引(Index),就是相同类型的文档的集合 , 类似于数据库的表
例如 :所有用户文档,可以组织在一起,成为用户的索引
{
"id": 101,
"name": "张三",
"age": 39
}
{
"id": 102,
"name": "李四",
"age": 49
}
{
"id": 103,
"name": "王五",
"age": 69
}
- 因此,我们可以把索引当做是数据库中的表
3.2.2. 映射
数据库的表会有约束信息,用来定义表的结构、字段的名称、类型等信息。因此,索引库就有映射(mapping),是索引中文档的字段约束信息,类似于表的结构约束
映射定义了文档的结构及其字段的类型、行为和其他属性
包含具体字段及字段的具体约束 , 例如 是否索引、如何分词、存储格式等。
3.2.2.1. ****type 字段属性:
适用于大多数字段类型:
type: 指定字段类型(如text、keyword) 具体字段看上面。index: 是否对字段索引,默认值为true。
-
true: 可以搜索该字段。false: 仅存储,不支持搜索。
store: 是否单独存储字段(默认值为false,从_source提取)。null_value: 指定字段值为null时的替代值。doc_values: 是否为字段生成 doc_values(优化聚合和排序)。
适用于 text 类型字段:
analyzer: 指定分词器(如standard、whitespace)。search_analyzer: 搜索时的分词器。fields: 定义多字段映射,例如同时索引为text和keyword。
适用于数值类型(如 integer, float):
coerce: 是否自动转换数据类型(如字符串转整数),默认true。
适用于 date 和 date_nanos:
format: 定义日期格式,支持自定义格式和 ISO 8601。
还有地理元素专有的约束 , 动态映射配置等
3.2.2.2. analyzer :使用哪种分词器
3.2.2.3. index: 是否使用索引
3.2.2.4. properties:该字段的子字段
3.3. 与 Mysql 的对比
| Mysql | Elasticsearch | 说明 |
|---|---|---|
| Table | Index | 索引(index),就是文档的集合,类似数据库的表(Table) |
| Row | Document | 文档(Document),就是一条条的数据,类似数据库中的行(Row),文档都是JSON格式 |
| Column | Field | 字段(Field),就是JSON文档中的字段,类似数据库中的列(Column) |
| Schema | Mapping | Mapping(映射)是索引中文档的约束,例如字段类型约束。类似数据库的表结构(Schema) |
| SQL | DSL | DSL是elasticsearch提供的JSON风格的请求语句,用来操作elasticsearch,实现CRUD |
在企业中,往往是这二者结合使用
- 对安全性要求较高的写操作,使用MySQL实现
- 对查询性能个较高的搜索需求,使用ElasticSearch实现
- 二者再基于某种方式,实现数据的同步,保证一致性
4. 索引库操作
- 索引库就类似于数据库表,mapping映射就类似表的结构
- 我们要向es中存储数据,必须先创建
库和表
4.1.1. 查看健康状态
GET /_cat/health?v
4.1.2. 创建索引
- 基本语法
-
- 请求方式:
PUT - 请求路径:
/{索引库名},可以自定义 - 请求参数:
mapping映射
- 请求方式:
PUT /{索引库名}
{
"mappings": {
"properties": {
"字段名1": {
"type": "text ",
"analyzer": "standard"
},
"字段名2": {
"type": "text",
"index": true
},
"字段名3": {
"type": "text",
"properties": {
"子字段1": {
"type": "keyword"
},
"子字段2": {
"type": "keyword"
}
}
}
}
}
}
示例 :
PUT /test001
{
"mappings": {
"properties": {
"info": {
"type": "text",
"analyzer": "ik_smart"
},
"email": {
"type": "keyword",
"index": false
},
"name": {
"type": "object",
"properties": {
"firstName": {
"type": "keyword"
},
"lastName": {
"type": "keyword"
}
}
}
}
}
}
4.1.3. 查询索引库
- 基本语法
-
- 请求方式:
GET - 请求路径:
/{索引库名}
- 请求方式:
GET /{索引库名}
GET /_cat/indices?v
4.1.4. 修改索引库
- 基本语法
-
- 请求方式:
PUT - 请求路径:
/{索引库名}/_mapping - 请求参数:
mapping映射
- 请求方式:
PUT /{索引库名}/_mapping
{
"properties": {
"新字段名":{
"type": "integer"
}
}
}
注意
- 倒排索引结构虽然不复杂,但是一旦数据结构改变(比如改变了分词器),就需要重新创建倒排索引,这简直是灾难。因此索引库
一旦创建,就无法修改mapping - 虽然无法修改mapping中已有的字段,但是却允许添加新字段到mapping中,因为不会对倒排索引产生影响
- 如果强行改,则会报错
4.1.5. 删除索引库
基本语法:
- 请求方式:
DELETE - 请求路径:
/{索引库名}
DELETE /{索引库名}
4.1.6.
5. 文档操作
5.1.1. 创建文档
将 JSON 文档添加到指定的数据流或索引并使其可搜索。如果目标是索引并且文档已经存在,则请求更新文档并递增其版本。
PUT /<target>/_doc/<_id>
POST /<target>/_doc/
PUT /<target>/_create/<_id>
POST /<target>/_create/<_id>
POST /{索引库名}/_doc/{文档id}
{
"字段1": "值1",
"字段2": "值2",
"字段3": {
"子属性1": "值3",
"子属性2": "值4"
},
// ...
}
示例
POST /review-1/_create/1
{
"id":1,
"userID":147982601,
"score":5,
"status":2,
"publishTime":"2023-09-09T16:07:42.499144+08:00",
"content":"这是一个好评!",
"tags":[
{
"code":1000,
"title":"好评"
},
{
"code":2000,
"title":"物超所值"
},
{
"code":3000,
"title":"有图"
}
]
}
5.1.2. 判断文档是否存在
HEAD /review-1/_doc/1
如果存在,Elasticsearch 返回 200 - OK的响应状态码,如果不存在则返回404 - Not Found。
5.1.3. 获取文档
GET /review-1/_doc/1
返回整个文档的内容,包括元数据。
5.1.4. 获取文档指定数据
GET /review-1/_source/1
返回数据 _source 内容
# 可以在查询时指定查询的具体字段。
GET /review-1/_source/1?_source=content,score
返回数据 _source 内容的 content 和 score
5.1.5. 更新文档
更新文档的请求格式如下:
POST /<index>/_update/<_id>
例如,下面的命令用于更新_id为1的文档。
POST /review-1/_update/1
{
"doc": {
"content": "这是修改过的好评!"
}
}
返回 updated 表示更新成功。
5.1.6. 批量获取
命令格式:
GET /_mget
GET /<index>/_mget
GET /review-1/_mget
{
"docs":[
{
"_id":"1"
},
{
"_id":"2"
}
]
}
5.1.7. 删除文档
DELETE /review-1/_doc/1
6. DSL 搜索语句
6.1. 简单分类
ElasticSearch提供了基于DSL来定义查询。常见的查询类型包括
查询所有:查询出所有数据,一般测试用。例如
-
- match_all
全文检索(full text):利用分词器对用户输入的内容分词,然后去倒排索引库中匹配。例如
-
- match_query
- multi_match_query
精确查询:根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段。例如
-
- ids
- range
- term
地理查询(geo):根据经纬度查询。例如
-
- geo_distance
- geo_bounding_box
复合查询(compound):复合查询可以将上述各种查询条件组合起来,合并查询条件。例如
-
- bool
- function_score
6.2. 普通查询
语法
GET /indexname/_search
{
"query": {
"查询类型": {
"查询条件": "条件值"
}
}
}
这里以查询所有为例
- 查询类型为
match_all - 没有查询条件
GET /indexName/_search
{
"query": {
"match_all": {
}
}
}
- 其他的无非就是
查询类型和查询条件的变化
6.3. 全文检索
6.3.1. 语法
- 常见的全文检索包括
-
- match查询:单字段查询
- multi_match查询:多字段查询,任意一个字段符合条件就算符合查询条件
- match查询语法如下
GET /indexName/_search
{
"query": {
"match": {
"FIELD": "TEXT"
}
}
}
- multi_match语法如下
GET /indexName/_search
{
"query": {
"multi_match": {
"fields": ["FIELD1", "FIELD2"]
}
}
}
6.3.2. 示例
- 查询上海外滩的酒店数据
-
- 以match查询示例,这里的
all字段是之前由name、city、business这三个字段拷贝得来的
- 以match查询示例,这里的
GET /hotel/_search
{
"query": {
"match": {
"all": "上海外滩"
}
}
}
- 以multi_match查询示例
GET /hotel/_search
{
"query": {
"multi_match": {
"query": "上海外滩",
"fields": ["brand", "city", "business"]
}
}
}
6.4. 精准搜索
- 精确查询一般是查找keyword、数值、日期、boolean等类型字段。所以不会对搜索条件分词。常见的有
-
term:根据词条精确值查询range:根据值的范围查询
- term查询:根据词条精确匹配,一般搜索keyword类型、数值类型、布尔类型、日期类型字段
- range查询:根据数值范围查询,可以使数值、日期的范围
6.4.1. term 语法
GET /indexName/_search
{
"query": {
"term": {
"FIELD": {
"value": "VALUE"
}
}
}
}
- 示例:查询北京的酒店数据
GET /hotel/_search
{
"query": {
"term": {
"city": {
"value": "北京"
}
}
}
}
6.4.2. range语法
- 范围查询,一般应用在对数值类型做范围过滤的时候。例如做价格范围的过滤
GET /hotel/_search
{
"query": {
"range": {
"FIELD": {
"gte": 10, //这里的gte表示大于等于,gt表示大于
"lte": 20 //这里的let表示小于等于,lt表示小于
}
}
}
}
- 示例:查询酒店价格在1000~3000的酒店
GET /hotel/_search
{
"query": {
"range": {
"price": {
"gte": 1000,
"lte": 3000
}
}
}
}
6.5. 地理坐标查询
- 所谓地理坐标查询,其实就是根据经纬度查询,官方文档:www.elastic.co/guide/en/el…
- 常见的使用场景包括
-
- 携程:搜索附近的酒店
- 滴滴:搜索附近的出租车
- 微信:搜索附近的人
6.5.1.1. 矩形范围查询
- 矩形范围查询,也就是geo_bounding_box查询,查询坐标落在某个矩形范围内的所有文档
- 查询时。需指定矩形的左上、游戏啊两个点的坐标,然后画出一个矩形,落在该矩形范围内的坐标,都是符合条件的文档
- 基本语法
GET /indexName/_search
{
"query": {
"geo_bounding_box": {
"FIELD": {
"top_left": { // 左上点
"lat": 31.1, // lat: latitude 纬度
"lon": 121.5 // lon: longitude 经度
},
"bottom_right": { // 右下点
"lat": 30.9, // lat: latitude 纬度
"lon": 121.7 // lon: longitude 经度
}
}
}
}
}
- 示例
GET /hotel/_search
{
"query": {
"geo_bounding_box": {
"location": {
"top_left": {
"lat": 31.1,
"lon": 121.5
},
"bottom_right": {
"lat": 30.9,
"lon": 121.7
}
}
}
}
}
6.5.1.2. 附近查询
- 附近查询,也叫做举例查询(geo_distance):查询到指定中心点的距离小于等于某个值的所有文档
- 换句话说,也就是以指定中心点为圆心,指定距离为半径,画一个圆,落在圆内的坐标都算符合条件
- 语法说明
GET /indexName/_search
{
"query": {
"geo_distance": {
"distance": "3km", // 半径
"location": "39.9, 116.4" // 圆心
}
}
}
- 示例:查询我附近3km内的酒店文档
GET /hotel/_search
{
"query": {
"geo_distance": {
"distance": "3km",
"location": "39.9, 116.4"
}
}
}
6.6. 复合查询
复合(compound)查询:复合查询可以将其他简单查询组合起来,实现更复杂的搜索逻辑,常见的有两种
- function score:算分函数查询,可以控制文档相关性算分,控制文档排名(例如搜索引擎的排名,第一大部分都是广告)
- bool query:布尔查询,利用逻辑关系组合多个其他的查询,实现复杂搜索
6.6.1. 相关性算分
6.6.1.1. 自带打算算法
- 当我们利用match查询时,文档结果会根据搜索词条的关联度打分(_score),返回结果时按照分值降序排列
- 例如我们搜索虹桥如家,结果如下
[ { "_score" : 17.850193, "_source" : { "name" : "虹桥如家酒店真不错", } }, { "_score" : 12.259849, "_source" : { "name" : "外滩如家酒店真不错", } }, { "_score" : 11.91091, "_source" : { "name" : "迪士尼如家酒店真不错", } }]
5.1 版本后打算算法为 BM25 算法 , 公式如下
6.6.1.2. 算分函数查询
- 根据相关度打分是比较合理的需求,但是合理的并不一定是产品经理需要的
- 以某搜索引擎为例,你在搜索的结果中,并不是相关度越高就越靠前,而是谁掏的钱多就让谁的排名越靠前
- 要想控制相关性算分,就需要利用ES中的
function score查询了 - 语法说明
GET /indexName/_search
{
"query": {
"function_score": {
"query": {
"match": {
"all": "外滩"
}
},
"functions": [
{
"filter": {
"term": {
"id": "1"
}
},
"weight": 10
}
],
"boost_mode": "multiply"
}
}
}
function score查询中包含四部分内容
- 原始查询条件:query部分,基于这个条件搜索文档,并且基于BM25算法给文档打分,原始算分(query score)
- 过滤条件:filter部分,符合该条件的文档才会被重新算分
- 算分函数:符合filter条件的文档要根据这个函数做运算,得到函数算分(function score),有四种函数
-
- weight:函数结果是常量
- field_value_factor:以文档中的某个字段值作为函数结果
- random_score:以随机数作为函数结果
- script_score:自定义算分函数算法
- 运算模式:算分函数的结果、原始查询的相关性算分,二者之间的运算方式,包括
-
- multiply:相乘
- replace:用function score替换query score
- 其他,例如:sum、avg、max、min
function score的运行流程如下
- 根据
原始条件查询搜索文档,并且计算相关性算分,称为原始算法(query score) - 根据
过滤条件,过滤文档 - 符合
过滤条件的文档,基于算分函数运算,得到函数算分(function score) - 将
原始算分(query score)和函数算分(function score)基于运算模式做运算,得到最终给结果,作为相关性算分
因此,其中的关键点是:
- 过滤条件:决定哪些文档的算分被修改
- 算分函数:决定函数算分的算法
- 运算模式:决定最终算分结果
{% note info no-icon %}
需求:给如家这个品牌的酒店排名靠前一点
思路:过滤条件为"brand": "如家",算分函数和运算模式我们可以暴力一点,固定算分结果相乘
{% endnote %}
- 对应的DSL语句如下,我们搜索外滩的酒店,对如家品牌过滤,最终的运算结果是10倍的原始算分
GET /hotel/_search
{
"query": {
"function_score": {
"query": {
"match": {
"all": "外滩"
}
},
"functions": [
{
"filter": {
"term": {
"brand": "如家"
}
},
"weight": 10
}
],
"boost_mode": "multiply"
}
}
}
6.6.2. 布尔查询(*)
6.6.2.1. 语法
- 布尔查询是一个或多个子查询的组合,每一个子句就是一个子查询。子查询的组合方式有
-
must:必须匹配每个子查询,类似与should:选择性匹配子查询,类似或must_not:必须不匹配,不参与算分,类似非filter:必须匹配,不参与算分
- 每一个不同的字段,其查询条件、方式都不一样,必须是多个不同的查询,而要组合这些查询,就需要用到布尔查询了
{% note warning no-icon %}
需要注意的是,搜索时,参与打分的字段越多,查询的性能就越差,所以在多条件查询时 - 搜索框的关键字搜索,是全文检索查询,使用must查询,参与算分
- 其他过滤条件,采用filter和must_not查询,不参与算分
{% endnote %}
{% note info no-icon %}
6.6.2.2. 案例
- 需求:搜索名字中包含
如家,价格不高于400,在坐标39.9, 116.4周围10km范围内的酒店
分析: - 名称搜索,属于全文检索查询,应该参与算分,放到
must中 - 价格不高于400,用range查询,属于过滤条件,不参与算分,放到
must_not中 - 周围10km范围内,用geo_distance查询,属于过滤条件,放到
filter中
{% endnote %}
GET /hotel/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"name": {
"value": "如家"
}
}
}
],
"must_not": [
{
"range": {
"price": {
"gt": 400
}
}
}
],
"filter": [
{
"geo_distance": {
"distance": "10km",
"location": {
"lat": 39.9,
"lon": 116.4
}
}
}
]
}
}
}
{% note info no-icon %}
需求:搜索城市在上海,品牌为皇冠假日或华美达,价格不低于500,且用户评分在45分以上的酒店
{% endnote %}
GET /hotel/_search
{
"query": {
"bool": {
"must": [
{"term": {
"city": {
"value": "上海"
}
}}
],
"should": [
{"term": {
"brand": {
"value": "皇冠假日"
}
}},
{"term": {
"brand": {
"value": "华美达"
}
}}
],
"must_not": [
{"range": {
"price": {
"lte": 500
}
}}
],
"filter": [
{"range": {
"score": {
"gte": 45
}
}}
]
}
}
}
{% note warning no-icon %}
- 如果细心一点,就会发现这里的should有问题,must和should一起用的时候,should会不生效,结果中会查询到除了
皇冠假日和华美达之外的品牌。 - 对于DSL语句的解决方案比较麻烦,需要在must里再套一个bool,里面再套should,但是对于Java代码来说比较容易修改
{% endnote %}
7. 搜索结果的处理
- 搜索的结果可以按照用户指定的方式去处理或展示
7.1. 排序
- ES默认是根据相关度算分(_score)来排序,但是也支持自定义方式对搜索结果排序。可以排序的字段有:keyword类型、数值类型、地理坐标类型、日期类型等
7.1.1. 普通字段查询
- keyword、数值、日期类型排序的语法基本一致
GET /hotel/_search
{
"query": {
"match_all": {
}
},
"sort": [
{
"FIELD": {
"order": "desc"
},
"FIELD": {
"order": "asc"
}
}
]
}
排序条件是一个数组,也就是可以写读个排序条件。按照声明顺序,当第一个条件相等时,再按照第二个条件排序,以此类推
7.1.1.1. 示例
需求:酒店数据按照用户评价(score)降序排序,评价相同再按照价格(price)升序排序
GET /hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"score": {
"order": "desc"
},
"price": {
"order": "asc"
}
}
]
}
7.1.2. 地理坐查询
7.1.2.1. 语法说明
GET /indexName/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"_geo_distance": {
"FIELD": {
"lat": 40,
"lon": -70
},
"order": "asc",
"unit": "km"
}
}
]
}
这个查询的含义是
- 指定一个坐标,作为目标点
- 计算每一个文档中,指定字段(必须是geo_point类型)的坐标,到目标点的距离是多少
- 根据距离排序
- 需求:实现酒店数据按照到你位置坐标的距离升序排序
7.2. 分页
- ES默认情况下只返回
top10的数据。而如果要查询更多数据就需要修改分页参数了。 - ES中通过修改from、size参数来控制要返回的分页结果
-
from:从第几个文档开始size:总共查询几个文档
- 类似于mysql中的
limit ?, ?
7.2.1. 基本的分页
- 分页的基本语法如下
GET /indexName/_search
{
"query": {
"match_all": {}
},
"from": 0,
"size": 20
}
7.2.2. 深度分页问题
....
7.3. 关键词高亮
7.3.1. 高亮原理
- 什么是高亮呢?
- 我们在百度搜索时,关键字会变成红色,比较醒目,这就叫高亮显示
- 高亮显示的实现分为两步
-
- 给文档中的所有关键字都添加一个标签,例如
<em>标签 - 页面给
<em>标签编写CSS样式
- 给文档中的所有关键字都添加一个标签,例如
7.3.2. 高亮语法
GET /indexName/_search
{
"query": {
"match": {
"FIELD": "TEXT"
}
},
"highlight": {
"fields": {
"FIELD": {
"pre_tags": "<em>",
"post_tags": "</em>"
}
}
}
}
注意:
- 高亮是对关键词高亮,因此搜索条件必须带有关键字,而不能是范围这样的查询
- 默认情况下,高亮的字段,必须与搜索指定的字段一致,否则无法高亮
- 如果要对非搜索字段高亮,则需要添加一个属性:
required_field_match=false
7.3.2.1. 示例
GET /hotel/_search
{
"query": {
"match": {
"all": "上海如家"
}
},
"highlight": {
"fields": {
"name": {
"pre_tags": "<em>",
"post_tags": "</em>",
"require_field_match": "false"
}
}
}
}
- 但默认情况下就是加的
<em>标签,所以我们也可以省略
GET /hotel/_search
{
"query": {
"match": {
"all": "上海如家"
}
},
"highlight": {
"fields": {
"name": {
"require_field_match": "false"
}
}
}
}
8. Linux 部署 ES
8.1. 无密码 http 快速部署
version: "3.7"
services:
elasticsearch:
container_name: elasticsearch
image: docker.elastic.co/elasticsearch/elasticsearch:8.9.2
environment:
- node.name=elasticsearch
- ES_JAVA_OPTS=-Xms512m -Xmx512m
- discovery.type=single-node
- xpack.security.enabled=false # 取消安全策略
volumes:
- ./data/es/data:/usr/share/elasticsearch/data
- ./data/es/plugins:/usr/share/elasticsearch/plugins
- ./data/es/config:/usr/share/elasticsearch/config
ports:
- 9200:9200
- 9300:9300
networks:
- elastic
kibana:
image: docker.elastic.co/kibana/kibana:8.9.2
container_name: kibana
ports:
- 5601:5601
networks:
- elastic
depends_on:
- elasticsearch
networks:
elastic:
name: elastic
8.1.1. 创建文件夹
mkdir ./data/es
mkdir ./data/es/config ./data/es/data
8.1.2. 注释调 config 和 data 的数据卷挂载
启动 compose 文件做数据卷映射
docker cp elasticsearch:/usr/share/elasticsearch/data ./data/es
docker cp elasticsearch:/usr/share/elasticsearch/config ./data/es
chmod 777 ./data/es/*
8.1.3. 取消注释重新启动即可
8.2. 配置密码 http 配置
version: "3.7"
services:
elasticsearch:
container_name: elasticsearch
image: docker.elastic.co/elasticsearch/elasticsearch:8.9.2
environment:
- node.name=elasticsearch
- cluster.name=cluster_elasticsearch
- ES_JAVA_OPTS=-Xms512m -Xmx512m
- discovery.type=single-node
volumes:
- ./data/es/data:/usr/share/elasticsearch/data
- ./data/es/plugins:/usr/share/elasticsearch/plugins
- ./data/es/config:/usr/share/elasticsearch/config
ports:
- 9200:9200
- 9300:9300
networks:
- elastic
kibana:
image: docker.elastic.co/kibana/kibana:8.9.2
container_name: kibana
ports:
- 5601:5601
volumes:
- ./data/kibana/config:/usr/share/kibana/config
networks:
- elastic
depends_on:
- elasticsearch
networks:
elastic:
name: elastic
8.2.1. 创建文件夹
mkdir ./data/es
mkdir ./data/es/config ./data/es/data
mkdir ./data/kibana
mkdir ./data/kibana/config ./data/kibana/config
8.2.2. 注释调 config 和 data 的数据卷挂载 并启动容器
8.2.3. 复制数据卷
docker cp elasticsearch:/usr/share/elasticsearch/data ./data/es
docker cp elasticsearch:/usr/share/elasticsearch/config ./data/es
docker cp kibana:/usr/share/kibana/data ./data/kibana
docker cp kibana:/usr/share/kibana/config ./data/kibana
chmod 777 ./data/es/* ./data/kibana/*
8.2.4. 自定义重置 es 和 kibana 密码
docker exec -it elasticsearch /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic -i
docker exec -it elasticsearch /usr/share/elasticsearch/bin/elasticsearch-reset-password -u kibana_system -i
8.2.5. 追加配置内容
.data/es/config/elasticsearch.yml文件内容
nano ./data/es/config/elasticsearch.yml
# 修改安全配置 关闭 证书校验
xpack.security.http.ssl:
enabled: false
xpack.security.transport.ssl:
enabled: false
2. .data/kibana/config/kibana.yml 文件内容
cat <<EOL >> ./data/kibana/config/kibana.yml
i18n.locale: zh-CN # 中文
elasticsearch.username: "kibana_system"
elasticsearch.password: "123456" # 密码为刚刚设置的
EOL
8.2.6. 取消注释重新启动即可
8.3. 配置 https 自签证书连接
参考文档ElasticSearch和Kibana的安全设置以及https设置_kibana 和 elasticsearch 之间启用传输层安全-CSDN博客
设置步骤如下:
8.3.1. 进入容器 docker exec -it --user root elasticsearch /bin/bash
8.3.2. 在启动 Elasticsearch 之前, 使用bin目录下的elasticsearch-certutil 工具为生成 CA
elasticsearch-certutil ca
# 会出现如下提示
Please enter the desired output file [elastic-stack-ca.p12]: # 回车就行 默认文件名elastic-stack-ca.p12
Enter password for elastic-stack-ca.p12 : # 设置CA的密码
8.3.3. 用上面生成的CA生成证书和私钥
elasticsearch-certutil cert --ca elastic-stack-ca.p12
# 会出现如下提示
Enter password for CA (elastic-stack-ca.p12) : # 输入使用CA的密码 上面设置的
Please enter the desired output file [elastic-certificates.p12]: # 回车就行 默认证书的名字
Enter password for elastic-certificates.p12 : # 设置证书的密码
8.3.4. 把生成的证书拷贝到配置文件夹config中
mv ./elastic-certificates.p12 ./config
mv ./elastic-stack-ca.p12 ./config
8.3.5. 修改es的配置
xpack.security.enabled: true
xpack.security.enrollment.enabled: true
# Enable encryption and mutual authentication between cluster nodes
xpack.security.transport.ssl:
enabled: true
verification_mode: certificate
keystore.path: elastic-certificates.p12
truststore.path: elastic-certificates.p12
8.3.6. 在容器李将CA和证书密码存储在Elasticsearch密钥库,使用bin目录下的elasticsearch-keystore工具
elasticsearch-keystore add xpack.security.transport.ssl.keystore.secure_password
elasticsearch-keystore add xpack.security.transport.ssl.truststore.secure_passwor
8.3.7. 加密 Elasticsearch 的 HTTP 客户端通信(https)
通过运行 Elasticsearch HTTP 证书工具elasticsearch-certutil以生成证书签名请求 (CSR)
# 输入指令
elasticsearch-certutil http
# 会出现如下提示
Generate a CSR? [y/N]n
Use an existing CA? [y/N]y # 是否使用已经存在的ca,基本安全设置已经生成过了
CA Path: elastic-stack-ca.p12 # 输入ca的相对配置文件夹的路径,应该把ca复制到config中
Password for elastic-stack-ca.p12: # ca的密码
You may enter the validity period in years (e.g. 3Y), months (e.g. 18M), or days (e.g. 90D)
For how long should your certificate be valid? [5y] 5y # 证书有效期
Generate a certificate per node? [y/N]n # 是否为每个节点生成,单节点就n 集群的话就y 根据情况而定
Enter all the hostnames that you need, one per line.
When you are done, press <ENTER> once more to move on to the next step # 输入可以可以颁发(通过)证书的域名 直接回车
Is this correct [Y/n]y
Enter all the IP addresses that you need, one per line.
When you are done, press <ENTER> once more to move on to the next step. # 输入可以可以颁发(通过)证书的IP 直接回车
Is this correct [Y/n]y
Do you wish to change any of these options? [y/N]n # 是否修改上述信息
Provide a password for the "http.p12" file: [<ENTER> for none] # 设置http证书请求的密码
What filename should be used for the output zip file? [C:\cvzhanshi\environment\elasticsearch-8.15.0\elasticsearch-ssl-http.zip] # 生成的文件名 回车默认
8.3.8. 把生成的文件解压 unizp elasticsearch-ssl-http.zip
elasticsearch
|_ README.txt
|_ http.p12
|_ sample-elasticsearch.yml
/kibana
|_ README.txt
|_ elasticsearch-ca.pem
|_ sample-kibana.yml
8.3.9. 把http.p12复制到es的config中,elasticsearch-ca.pem复制到kibana的config中
8.3.10. 修改es的配置文件末尾追加
# Enable encryption for HTTP API client connections, such as Kibana, Logstash, and Agents
xpack.security.http.ssl:
enabled: true
keystore.path: http.p12
truststore.path: http.p12
8.3.11. 进入容器将私钥的密码添加到 Elasticsearch 中的安全设置中
elasticsearch-keystore add xpack.security.http.ssl.keystore.secure_password
elasticsearch-keystore add xpack.security.http.ssl.truststore.secure_password
8.3.12. 加密 Kibana 和 Elasticsearch 之间的通信 ,将上面的elasticsearch-ca.pem复制到kibana的config中
# ** THIS IS AN AUTO-GENERATED FILE **
#
# Default Kibana configuration for docker target
server.host: "0.0.0.0"
server.shutdownTimeout: "5s"
elasticsearch.hosts: [ "https://elasticsearch:9200" ]
monitoring.ui.container.elasticsearch.enabled: true
i18n.locale: zh-CN # 中文
elasticsearch.username: "kibana_system"
elasticsearch.password: "Fuck0668!"
elasticsearch.ssl.certificateAuthorities: /usr/share/kibana/config/elasticsearch-ca.pem
8.3.13. 加密浏览器和 Kibana 之间的通信
8.3.13.1. 通过es的工具elasticsearch-certutil为 Kibana 生成服务器证书和私钥。
# 进入容器输入
elasticsearch-certutil csr -name kibana-server -dns example.com,www.example.com
得到一个文件解压目录如下:
/kibana-server
|_ kibana-server.csr
|_ kibana-server.key
8.3.14. 解压csr-bundle.zip文件,获取kibana-server.csr未签名安全证书和kibana-server.key未加密私钥
8.3.15. 将 kibana-server.csr 证书签名请求发送到您的内部 CA 或受信任的 CA 进行签名,以获得签名证书。
# 可以使用命令 生成kibana-server.crt证书
openssl x509 -req -in ./kibana-server.csr -signkey ./kibana-server.key -out ./kibana-server.crt
1
2
8.3.16. 把证书拷贝到config目录下
添加 kibana 配置文件
server.ssl.enabled: true
server.ssl.certificate: C:/cvzhanshi/environment/kibana-8.15.0/config/kibana-server.crt
server.ssl.key: ...
8.4. 配置分词器
注意 : 尽量保持与 es 同版本的 ik 分词器
8.4.1. 离线下载
8.4.1.1. 线下访问并安装 github.com/infinilabs/…
8.4.1.2. 解压并到数据卷目录
mkdir ./data/es/plugins/ik
chmod 777 ./data/es/plugins/ik
unzip elasticsearch-analysis-ik-8.9.2.zip ./data/es/plugins/ik/
8.4.1.3. 重启 docker-compose
8.4.2. 在线下载
# 进入容器内部
docker exec -it elasticsearch /bin/bash
# 在线下载并安装
./bin/elasticsearch-plugin install https://github.com/infinilabs/analysis-ik/releases/download/v8.9.2/elasticsearch-analysis-ik-8.9.2.zip
#退出
exit
#重启容器
docker-compose up -d