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"
  }
}

注意:

  1. 必须使用 doc 关键字包裹:更新的字段内容必须放在 doc 对象内部。
  2. 更新是指增量更新:只会修改 doc 中指定的字段,原文档中未提及的字段(如 age、intro)会保持原样,不会被覆盖或删除。

3、文档查看

① GET 根据 ID 查询文档(精确查找)

语法:GET /索引/_doc/文档ID

GET /stu/_doc/1

注意:

  1. 直接返回文档内容:响应体中的 _source 字段即为存储的原始 JSON 数据。
  2. 不存在则报错:如果 ID 不存在,不会返回空结果,而是返回 404 Not Found 状态码。

② GET 查询所有文档(基本搜索)

语法:GET /索引/_search

GET /stu/_search

注意:

  1. 默认分页:如果不指定 from 和 size,默认返回前 10 条数据。
  2. 结果元数据解释:
    • took:查询耗时(毫秒)。
    • _shards:分片执行信息。
    • hits:命中结果统计,包含总数、最高分等)。
      • hits.total.value:命中文档总数。
      • hits.max_score:最高相关性评分。
      • hits.hits:具体文档列表。

4、文档删除

DELETE 根据 ID 删除文档

语法:DELETE /索引/_doc/文档ID

DELETE /stu/_doc/1

注意:

  1. 逻辑删除机制:执行 DELETE 时并不会立即从磁盘上擦除数据,而是在内部的 .del 文件中将该文档标记为“已删除”,后续查询会自动过滤掉它(即逻辑删除,此时磁盘空间未释放);只有当 ES 后台执行段合并(Segment Merge)时,才会将多个小段文件合并重写为一个新段,合并过程中只保留有效数据、丢弃被标记删除的文档,旧段文件随之删除,此时磁盘空间才真正被回收。
  2. 幂等性:如果删除一个不存在的 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。

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、倒排索引

image-20260822174339498

(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(段合并)阶段。当多个小段合并为大段时,被标记为删除的文档会被直接丢弃,不再写入新段,从而释放磁盘空间。

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>"
  }
}

注意:

  1. 结果独立存储:ES 返回的高亮数据存储在 highlight 属性中,不会自动替换 _source(原始文档)中的内容。highlight 中的 intro 和 title 是包含 HTML 标签的片段,而 _source 中依然是纯净的原文。

  2. 前端/后端手动替换:在页面展示时,需要编写代码逻辑进行“拼装”。即先获取 hits 中的原始数据,再检查是否存在 highlight 对象。若存在,则用 highlight 中的字段值覆盖原始数据中的对应字段,从而实现关键词变色效果。

  3. 业务落地场景(ES + MySQL 联合查询):

    • 痛点:ES 擅长检索和高亮,但页面展示往往需要更多字段(如实时库存、销量、关联信息),这些数据可能只存在于 MySQL 中。如果全量存入 ES 会导致数据同步困难且冗余。

    • 解决方案:

      采用“ES 精准定位 + MySQL 详情渲染”模式。

      1. 查 ES:利用 ES 进行全文检索并获取高亮片段(Highlight)及文档 ID。
      2. 查 MySQL:拿着 ID 列表去 MySQL 批量查询完整的业务详情数据。
      3. 内存组装:在应用层将 ES 返回的“高亮标题/简介”替换到 MySQL 查出的“完整对象”中,最后返回给前端。这样既保证了搜索体验,又确保了数据的完整性和实时性。

四、Spring Data 命名规范

​ Spring Data 命名规范是指在使用 Spring Data JPA(或 Spring Data MongoDB、Spring Data Redis 等)时,通过方法名的命名规则来自动生成对应的数据库查询语句,而无需手动编写 SQL 或 JPQL。

wemeet image_20260814092800219

核心结构:方法名 = 前缀 + 属性名 + 条件关键字

  • 前缀:如 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 ?
posted on 2026-08-23 20:07  冬冬咚  阅读(31)  评论(0)    收藏  举报