Elasticsearch(8.20)
一、数据类型
ES 支持多种数据类型,主要包括以下几类:
- 字符串类型:
text、keyword - 数值类型:
byte、short、integer、long、float、double、half_float、scaled_float - 日期类型:
date - 布尔类型:
boolean - 二进制类型:
binary - 复杂类型:
array(数组)、object(对象)、nested(嵌套对象) - 地理类型:
geo_point(经纬度点)、geo_shape(地理形状) - 专用类型:
ip(IP地址)、completion(自动补全)、token_count(词项计数)
重点解析:
1、text 与 keyword
在 ES 中,处理字符串主要依赖 text 和 keyword 两种类型,它们的核心区别在于是否分词以及使用场景。
(1)text 类型(全文检索)
- 定义:当一个字段需要被全文搜索时(例如文章内容、产品描述、邮件正文),应使用
text类型。 - 分词机制:
- 当数据写入
text字段时,ES 会调用指定的分词器将字符串拆解成一个个独立的词项。 - 如果未指定分词器,默认使用 Standard Analyzer(标准分词器)。对于英文,它会按空格和标点拆分并转小写;对于中文,标准分词器效果较差(通常逐字拆分),实际开发中常配合 IK 分词器(
ik_max_word或ik_smart)使用。 - 分词后的结果会被建立倒排索引,用于高效的模糊搜索和相关性评分。
- 当数据写入
- 特点:
- 支持模糊查询(如
match查询)。 - 不支持聚合(Aggregation)和排序(Sorting),因为分词后的数据是碎片化的,无法代表原始值的整体逻辑。
- 支持模糊查询(如
(2)keyword 类型(精确匹配)
- 定义:适用于索引结构化内容的字段,例如 ID、电子邮件地址、主机名、状态码、标签等。
- 存储机制:
- 不分词。整个字符串作为一个完整的词项被索引。
- 数据原样存储,不做任何拆分或转换。
- 特点:
- 支持精确匹配(如
term查询)。 - 支持过滤(Filter)、排序(Sort)和聚合(Aggregation)。
- 只有当搜索词与存储值完全一致时才能命中。
- 支持精确匹配(如
2、关于 ES 的分布式 ID 与 UUID
(1)为什么不用自增 ID?
ES 是分布式的,数据分散在多个节点上。自增 ID 需要所有节点共享一个计数器,每次生成 ID 都要跨节点协调,性能开销大、网络延迟高,违背了 ES 高并发写入的设计初衷。
(2)为什么用 UUID?
UUID 各节点可以独立生成,不需要中心化协调,天然保证全局唯一,不会冲突。
二、文档基本操作
1、文档添加
①PUT 添加文档(指定 ID)
语法:PUT /索引/_doc/文档ID
- 索引 :指定ES库名
- _doc:以前是指定表名,而ES7以后只是 API 为了兼容旧版本保留的一个占位符,表示“我要操作的是文档”
- 文档ID:ES的元数据,用于定位文档;与JSON中业务的id不是一个概念。
特点:幂等操作。如果 ID 已存在则整体更新,不存在则创建。
PUT /stu/_doc/1
{
"id": 301,
"name": "张三",
"age": 25,
"intro": "这是一个测试学生"
}
②POST 添加文档(自动生成 ID)
语法:POST /索引/_doc
特点:
- ES 会自动生成一个类似 UUID 的随机字符串作为
_id。 - 非幂等。每次执行都会创建一个新文档,即使内容完全一样,生成的
_id也不同。
POST /stu/_doc
{
"id": 302,
"name": "李四",
"age": 25,
"intro": "这是一个测试学生"
}
注意:
1、当索引/映射不存在时,会使用默认设置自动添加。但是自动添加的文档中的类型和分词器可能不是我们想要的。
2、ES中的数据一般是从别的数据库中导入的,所以文档ID会沿用原数据库中的ID。
3、操作时,如果指定文档ID,并且索引库中已经存在,则执行更新操作;否则执行添加。
4、不指定id的添加,es会指定添加一个字符集类型id
2、文档更新
①PUT 全量更新文档(指定 ID)
语法:PUT /索引/_doc/文档ID
PUT /stu/_doc/1
{
"id": 301,
"name": "张三123",
"age": 25,
"intro": "这是一个测试学生"
}
注意:
1、如果不指定id,操作失败。
2、更新是指全量更新。
② POST 部分更新文档(指定 ID)
语法:POST /索引/_update/文档ID
POST /stu/_update/1
{
"doc": {
"name": "张三123"
}
}
注意:
- 必须使用
doc关键字包裹:更新的字段内容必须放在doc对象内部。 - 更新是指增量更新:只会修改
doc中指定的字段,原文档中未提及的字段(如age、intro)会保持原样,不会被覆盖或删除。
3、文档查看
① GET 根据 ID 查询文档(精确查找)
语法:GET /索引/_doc/文档ID
GET /stu/_doc/1
注意:
- 直接返回文档内容:响应体中的
_source字段即为存储的原始 JSON 数据。 - 不存在则报错:如果 ID 不存在,不会返回空结果,而是返回
404 Not Found状态码。
② GET 查询所有文档(基本搜索)
语法:GET /索引/_search
GET /stu/_search
注意:
- 默认分页:如果不指定
from和size,默认返回前 10 条数据。 - 结果元数据解释:
took:查询耗时(毫秒)。_shards:分片执行信息。hits:命中结果统计,包含总数、最高分等)。hits.total.value:命中文档总数。hits.max_score:最高相关性评分。hits.hits:具体文档列表。
4、文档删除
DELETE 根据 ID 删除文档
语法:DELETE /索引/_doc/文档ID
DELETE /stu/_doc/1
注意:
- 逻辑删除机制:执行
DELETE时并不会立即从磁盘上擦除数据,而是在内部的.del文件中将该文档标记为“已删除”,后续查询会自动过滤掉它(即逻辑删除,此时磁盘空间未释放);只有当 ES 后台执行段合并(Segment Merge)时,才会将多个小段文件合并重写为一个新段,合并过程中只保留有效数据、丢弃被标记删除的文档,旧段文件随之删除,此时磁盘空间才真正被回收。 - 幂等性:如果删除一个不存在的 ID,ES 依然会返回
200 OK(或404取决于版本配置),但result字段会显示not_found,表示操作已执行但未找到目标。
三、全文搜索
全文搜索是 Elasticsearch 最核心的功能,它不仅仅是简单的关键词匹配,而是一套完整的文本处理与检索体系。
1、搜索语句
①term 精确值查询
语法:GET /索引/_search
GET product/_search
{
"query": {
"term": {
"price": {
"value": 15299
}
}
}
}
注意:
不分词机制
term 查询用于精确值匹配,它不会对输入的查询词进行分词处理,而是将查询词作为一个完整的整体,直接去倒排索引中查找完全一致的文档。简单来说:match 是“先拆词,再匹配”,term 是“不拆词,直接匹配”。
②terms 多值精确查询
语法:GET /索引/_search
GET product/_search
{
"query": {
"terms": {
"price": [
"15299",
"6199",
10
]
}
}
}
注意:
多值匹配机制
terms 查询是 term 查询的升级版,类似于 SQL 中的 IN 操作符。它允许你指定多个值,只要文档字段的值等于数组中的任意一个,该文档就会被检索到。与 term 一样,它也是不分词的精确匹配。
③ range 范围查询
语法:GET /索引/_search
gt:>gte:>=lt:<lte:<=
GET product/_search
{
"query": {
"range": {
"price": {
"gt": 8000,
"lt": 20000
}
}
}
}
④match 全文检索查询
语法:GET /索引/_search
GET product/_search
{
"query": {
"match": {
"title": "游戏手机"
}
}
}
注意:
1.分词处理:match 属于全文检索。当你输入 "游戏手机" 时,Elasticsearch 不会直接拿这四个字去匹配,而是先调用分词器(如 IK 分词器)将其拆解。
- 拆解过程:
"游戏手机"->["游戏", "手机"](假设分词结果)。 - 匹配逻辑:只要文档的
title字段中包含“游戏”或者包含“手机”,该文档就会被匹配到。
2.补充说明:虽然 match 主要用于全文检索,但如果字段类型是 keyword(不分词),或者查询词本身就是一个不可拆分的整体(如数字、ID),match 也能起到类似精确查询的效果,但通常建议对精确值使用 term 以获得更好的性能。
⑤ multi_match 多字段全文检索查询
语法:GET /索引/_search
GET product/_search
{
"query": {
"multi_match": {
"query": "蓝牙指纹双卡",
"fields": ["title", "intro"]
}
}
}
注意:
1.多字段匹配机制:multi_match 是 match 查询的扩展版。当你不确定关键词具体出现在哪个字段(如标题还是简介)时,可以使用它同时在多个字段中进行搜索。
- 拆解过程:
"蓝牙指纹双卡"->["蓝牙", "指纹", "双卡"](假设分词结果)。 - 匹配逻辑:Elasticsearch 会分别在
title和intro这两个字段中查找包含“蓝牙”、“指纹”或“双卡”的文档。只要任意一个字段匹配成功,该文档就会被返回。
2.适用场景与补充:
- 全局搜索:适用于类似电商搜索框或网站全站搜索的场景,用户输入一个词,系统自动在标题、描述、标签等多个维度进行检索。
- 权重控制:虽然示例中两个字段地位平等,但在实际使用中,可以通过
^符号给字段加权(例如"title^2"),让标题中匹配到的结果排名更靠前。
⑥bool 复合查询(must 与 must_not)
语法:GET /索引/_search
GET product/_search
{
"query": {
"bool": {
"must": [
{
"range": {
"price": {
"gt": 5000
}
}
},
{
"match": {
"title": "pro"
}
}
]
}
}
}
注意:
1.布尔逻辑组合:bool 查询用于将多个查询条件组合在一起。 must 子句,代表逻辑“与”(AND)的关系。
- 必须满足:文档必须同时满足
must数组中的所有条件才会被返回。 - 本例逻辑:筛选出价格大于 5000 并且标题中包含 "pro" 的商品。
2.条件嵌套:bool 查询本身不直接匹配数据,而是作为一个容器,内部可以嵌套之前学过的任意查询类型(如 term, match, range 等)。
3.补充说明(should 与 must_not):
should:代表逻辑“或”(OR),满足其中任意一个条件即可(或者用于提高相关性得分)。must_not:代表逻辑“非”(NOT),必须不满足该条件。
⑦ 排序与分页查询 (Sort & Pagination)
语法:GET /索引/_search
GET product/_search
{
"query": {
"range": {
"price": {
"lt": 5000
}
}
},
"sort": [
{
"price": {
"order": "desc"
}
}
],
"from": 0,
"size": 3
}
注意:
1.排序机制 (sort):sort 用于指定结果集的排序规则。被排序的字段(如本例中的 price)必须是可排序的类型(如 number, date, keyword)。如果是 text 类型,通常需要开启 fielddata 或使用多字段映射(.keyword)才能排序。order 参数支持 asc(升序,默认值)和 desc(降序)。
2.分页逻辑 (from & size):
-
size:表示每页显示的文档数量(类似 SQL 的LIMIT)。 -
from:表示从第几条数据开始返回(偏移量,从 0 开始计数,类似 SQL 的OFFSET)。 -
计算公式:查询第 N 页,每页 S条数据的公式为:
$$
from = (N - 1) * S
$$- 示例中:查询第 1 页,每页 3 条 ->
from: 0,size: 3。 - 若查第 2 页:->
from: 3,size: 3。
- 示例中:查询第 1 页,每页 3 条 ->
3.性能提示:深度分页(即 from 的值非常大时)会消耗大量内存和计算资源,生产环境中建议使用 search_after 或 Scroll API 来替代传统的 from/size 分页。
2、中文分词器
语法:GET /索引/_analyze
# 1. 默认分词器(通常按单字拆分)
GET product/_analyze
{
"text": "中华人民共和国人民大会堂"
}
# 2. IK 粗粒度分词 (ik_smart)
GET product/_analyze
{
"text": "中华人民共和国人民大会堂",
"analyzer": "ik_smart"
}
# 3. IK 细粒度分词 (ik_max_word)
GET product/_analyze
{
"text": "中华人民共和国人民大会堂",
"analyzer": "ik_max_word"
}
注意:
1.默认分词机制:若不指定 analyzer,Elasticsearch 对中文通常使用 standard 分词器,逻辑简单粗暴,将每个汉字作为独立 Token。
2.IK 分词器模式对比:
ik_smart(粗粒度):做最粗粒度拆分,组合成有意义的长词(如["中华人民共和国", "人民大会堂"])。适用场景:索引创建时(节省空间)。ik_max_word(细粒度):做最细粒度拆分,穷举所有可能词语(如["中华", "华人", "人民", ..."])。适用场景:搜索查询时(提高命中率)。
3.IK 分词词库位置:
自定义词典文件通常位于插件目录下的 config 文件夹中。
- 路径:
plugins/ik/config/ - 核心文件:
custom/mydict.dic(自定义扩展词)或IKAnalyzer.cfg.xml(配置入口)。
3、倒排索引

(1)索引构建(写入)
将 MySQL 数据序列化为 JSON 文档写入 ES。ES 对文本字段分词,将每个词条映射到文档 ID:
- 新词:创建词条,记录文档 ID(如
Apache->[1]) - 已有词:追加文档 ID(如
Tomcat->[1, 2]) - 停用词:分词器可配置过滤掉
in、on等无意义词
(2)检索匹配(查询)
对搜索词分词后,直接在倒排索引中定位词条,无需遍历原始文档:
tomcat->[1, 2, 3]run->[2, 3]- AND 逻辑(默认):取交集 ->
[2, 3] - OR 逻辑:取并集 ->
[1, 2, 3]
(3)性能优化与删除机制
- 分片并行(Sharding):
索引被物理拆分为多个分片(Shard),每个分片是一个独立的 Lucene 索引。查询时,请求会分发到所有相关分片并行执行,最后汇总结果,从而突破单机性能瓶颈。 - 懒删除与合并(Lazy Delete & Merge):
- 逻辑删除:执行 Delete 时,ES 不会立即从磁盘擦除数据(因为倒排索引不可变,修改成本极高)。它只是在内存或提交点中给该文档打上一个
delete标记。 - 查询过滤:检索时,系统会正常匹配到该文档,但在返回结果前会检查标记,将已删除的文档过滤掉。
- 物理清理:真正的数据移除发生在后台的 Segment Merge(段合并)阶段。当多个小段合并为大段时,被标记为删除的文档会被直接丢弃,不再写入新段,从而释放磁盘空间。
- 逻辑删除:执行 Delete 时,ES 不会立即从磁盘擦除数据(因为倒排索引不可变,修改成本极高)。它只是在内存或提交点中给该文档打上一个
4、高亮显示
语法:GET /索引/_search
GET product/_search
{
"query": {
"multi_match": {
"query": "蓝牙指纹双卡",
"fields": ["title", "intro"]
}
},
"highlight": {
"fields": {
"title": {},
"intro": {}
},
"pre_tags": "<span style='color:red'>",
"post_tags": "</span>"
}
}
注意:
-
结果独立存储:ES 返回的高亮数据存储在
highlight属性中,不会自动替换_source(原始文档)中的内容。highlight中的intro和title是包含 HTML 标签的片段,而_source中依然是纯净的原文。 -
前端/后端手动替换:在页面展示时,需要编写代码逻辑进行“拼装”。即先获取
hits中的原始数据,再检查是否存在highlight对象。若存在,则用highlight中的字段值覆盖原始数据中的对应字段,从而实现关键词变色效果。 -
业务落地场景(ES + MySQL 联合查询):
-
痛点:ES 擅长检索和高亮,但页面展示往往需要更多字段(如实时库存、销量、关联信息),这些数据可能只存在于 MySQL 中。如果全量存入 ES 会导致数据同步困难且冗余。
-
解决方案:
采用“ES 精准定位 + MySQL 详情渲染”模式。
- 查 ES:利用 ES 进行全文检索并获取高亮片段(Highlight)及文档 ID。
- 查 MySQL:拿着 ID 列表去 MySQL 批量查询完整的业务详情数据。
- 内存组装:在应用层将 ES 返回的“高亮标题/简介”替换到 MySQL 查出的“完整对象”中,最后返回给前端。这样既保证了搜索体验,又确保了数据的完整性和实时性。
-
四、Spring Data 命名规范
Spring Data 命名规范是指在使用 Spring Data JPA(或 Spring Data MongoDB、Spring Data Redis 等)时,通过方法名的命名规则来自动生成对应的数据库查询语句,而无需手动编写 SQL 或 JPQL。

核心结构:方法名 = 前缀 + 属性名 + 条件关键字
- 前缀:如
findBy、readBy、countBy、existsBy、deleteBy,表示操作类型。 - 属性名:实体类中的字段名,首字母大写,如
Name、Age。 - 条件关键字:如
And、Or、GreaterThan、Like、Between等,用于构建 WHERE 条件。
例如:
List<User> findByAgeGreaterThanAndNameLike(Integer age, String name);
等价于 SQL:
SELECT * FROM user WHERE age > ? AND name LIKE ?
浙公网安备 33010602011771号