Elasticsearch (ES) 教程:初学者入门指南


Elasticsearch (ES) 教程:初学者入门指南 (超详细版)

摘要

Elasticsearch (简称 ES) 是一个基于 Apache Lucene 构建的开源、分布式、RESTful 风格的搜索和数据分析引擎。它以其强大的全文搜索能力、近乎实时的搜索和分析性能、高可用性和可扩展性而闻名。本指南旨在为初学者提供一个全面而详细的入门教程,涵盖 ES 的核心概念、安装部署、基本操作、查询技巧以及一些进阶概念,帮助您快速上手并理解 Elasticsearch 的强大之处。

目录

  1. Elasticsearch 是什么?
    • 定义与核心优势
    • 常见应用场景
  2. 核心概念解析
    • 近实时 (Near Real-Time, NRT)
    • 集群 (Cluster)
    • 节点 (Node)
    • 索引 (Index)
    • 类型 (Type) - (历史概念,需注意)
    • 文档 (Document)
    • 字段 (Field)
    • 映射 (Mapping)
    • 分片 (Shard)
    • 副本 (Replica)
  3. 安装与配置
    • 环境准备 (Java 环境)
    • 下载与安装 Elasticsearch
    • 运行 Elasticsearch
    • 安装 Kibana (可视化工具)
    • 使用 Docker 快速部署 (推荐)
  4. 基本操作 (CRUD & Bulk API)
    • 与 ES 交互:RESTful API
    • 创建索引 (Index)
    • 添加/更新文档 (Indexing Documents)
      • PUT /index/_doc/id
      • POST /index/_doc (自动生成 ID)
    • 获取文档 (Retrieving Documents)
    • 删除文档 (Deleting Documents)
    • 更新文档 (Updating Documents) - 部分更新
    • 批量操作 (Bulk API)
  5. 核心功能:搜索 (Searching)
    • 搜索基础:_search API
    • URI 搜索 vs 请求体搜索
    • 查询 DSL (Query DSL) 入门
      • match_all 查询:匹配所有文档
      • match 查询:标准全文搜索
      • term 查询:精确匹配 (未分词)
      • terms 查询:匹配多个精确值
      • 组合查询 (bool query)
        • must: 必须匹配 (AND)
        • should: 应该匹配 (OR)
        • must_not: 必须不匹配 (NOT)
        • filter: 过滤子句 (不计算得分,性能更优)
      • 范围查询 (range query)
    • 排序 (sort)
    • 分页 (from, size)
    • 高亮 (highlighting)
  6. 映射 (Mapping) 详解
    • 什么是映射?
    • 动态映射 (Dynamic Mapping)
    • 显式映射 (Explicit Mapping)
    • 核心数据类型 (text, keyword, integer, long, float, double, boolean, date 等)
    • text vs keyword 的区别
    • 定义和更新映射
  7. 文本分析 (Analysis) 简介
    • 分析器 (Analyzer) 的作用
    • 分析过程:字符过滤器 -> 分词器 -> Token 过滤器
    • 内置分析器 (standard, simple, whitespace, keyword 等)
    • 自定义分析器 (概述)
  8. 聚合 (Aggregations) 入门
    • 聚合的概念 (类似 SQL 的 GROUP BY + 聚合函数)
    • 指标聚合 (Metrics Aggregations): sum, avg, min, max, stats, value_count
    • 桶聚合 (Bucket Aggregations): terms, range, date_histogram, histogram
    • 聚合的基本使用示例
  9. 进阶话题与后续学习
    • 分布式特性深入:分片路由、故障转移
    • 性能调优:索引优化、查询优化、硬件选择
    • 安全配置:用户认证、授权、TLS/SSL 加密
    • 插件生态
    • 与其他大数据工具集成 (Logstash, Beats, Spark等)
  10. 总结

1. Elasticsearch 是什么?

定义与核心优势

Elasticsearch 是一个基于 Apache Lucene 库构建的开源、分布式、RESTful 风格的搜索和数据分析引擎。你可以把它想象成一个功能极其强大的 NoSQL 数据库,但它的核心优势在于搜索分析

核心优势:

  • 速度快: 基于 Lucene 构建,对全文索引和搜索进行了深度优化,查询通常能在毫秒级别返回结果。
  • 分布式: 天生支持分布式架构,可以轻松横向扩展到数百甚至数千台服务器,处理 PB 级别的数据。
  • 易用性 (RESTful API): 通过标准的 HTTP 方法 (GET, POST, PUT, DELETE) 和 JSON 进行交互,学习曲线相对平缓,与各种编程语言都能轻松集成。
  • 高可用: 通过副本机制,确保部分节点宕机时数据不丢失,服务不中断。
  • 功能丰富: 不仅仅是全文搜索,还提供强大的结构化搜索、地理空间搜索、聚合分析、度量计算等功能。
  • 近实时: 数据写入后,通常在秒级内即可被搜索到 (NRT)。

常见应用场景

Elasticsearch 的应用场景非常广泛,包括但不限于:

  • 站内搜索: 为网站或应用程序提供强大的内容搜索功能 (如电商网站的商品搜索、博客的文章搜索)。
  • 日志分析 (ELK/Elastic Stack): 作为 Elastic Stack (Elasticsearch, Logstash, Kibana, Beats) 的核心,用于收集、存储、搜索和分析大量的日志数据,进行监控和故障排查。
  • 应用性能监控 (APM): 收集和分析应用程序的性能指标、追踪请求链路。
  • 商业智能 (BI): 对业务数据进行复杂的聚合分析和可视化,发现商业洞察。
  • 安全信息和事件管理 (SIEM): 实时分析安全日志,检测威胁。
  • 地理空间数据分析: 存储和查询地理位置信息,实现基于位置的服务 (LBS)。

2. 核心概念解析

理解以下核心概念是掌握 Elasticsearch 的基础:

  • 近实时 (Near Real-Time, NRT): 数据从索引操作完成到能够被搜索到,存在一个短暂的延迟(通常是 1 秒,可配置),而不是严格意义上的实时。这是因为 ES 需要执行一个轻量级的 refresh 操作,使新数据对搜索可见。
  • 集群 (Cluster): 一个或多个节点(服务器)的集合,它们共同持有你的完整数据,并提供跨节点的联合索引和搜索能力。一个集群有一个唯一的名称(默认为 "elasticsearch"),节点通过集群名称加入。
  • 节点 (Node): 集群中的一个服务器实例。节点负责存储数据、参与集群的索引和搜索操作。每个节点默认也会有一个唯一的名称。节点有不同的角色(Master 节点、Data 节点、Ingest 节点、Coordinating 节点等),可以根据需求配置。
  • 索引 (Index): 类似于关系数据库中的“数据库” (Database)。它是具有相似特征的文档的集合。例如,可以有一个 products 索引存储所有商品信息,一个 logs 索引存储所有日志数据。索引名称必须小写。
  • 类型 (Type):重要提示: 在 ES 6.x 及以后版本中,一个索引只允许包含一个类型 _doc。在 ES 7.x 中类型被完全弃用,在 8.x 中已移除)。在早期版本中,类型类似于关系数据库中的“表” (Table),用于在一个索引内对文档进行逻辑分类。现在,推荐将不同“类型”的数据存储在不同的索引中。
  • 文档 (Document): 类似于关系数据库中的“行” (Row)。它是可以被索引的基本信息单元,以 JSON (JavaScript Object Notation) 格式表示。每个文档都存储在一个索引中,并拥有一个唯一的 ID。
  • 字段 (Field): 类似于关系数据库中的“列” (Column)。文档由字段组成,每个字段都有其数据类型(如文本、数字、日期、布尔值等)。
  • 映射 (Mapping): 定义索引中文档及其字段如何存储和索引的过程。类似于关系数据库中的“模式” (Schema)。映射定义了每个字段的数据类型以及它们应如何被 Lucene 索引(例如,是否需要分词、使用哪种分词器等)。
  • 分片 (Shard): 由于单个节点存储能力和处理能力的限制,ES 允许将一个索引水平分割成多个部分,称为分片。每个分片都是一个功能齐全且独立的“索引”,可以托管在集群中的任何节点上。分片使得:
    • 水平扩展: 允许存储超过单节点容量的数据。
    • 并行处理: 查询可以在多个分片上并行执行,提高性能。
    • 创建索引时需要指定主分片 (Primary Shard) 的数量,此数量一旦设定后不能更改
  • 副本 (Replica): 每个主分片可以有一个或多个副本分片 (Replica Shard)。副本是主分片的一个精确拷贝。副本提供:
    • 高可用性: 如果主分片所在的节点宕机,副本分片可以提升为新的主分片,保证数据不丢失。
    • 读性能扩展: 搜索请求可以同时在主分片和副本分片上处理,提高查询吞吐量。
    • 副本分片的数量可以在索引创建后动态调整。副本分片不会与其主分片分配在同一个节点上。

关系类比 (不完全精确,仅供理解):

  • Elasticsearch Cluster ≈ RDBMS Instance
  • Index ≈ Database
  • ~~Type (Deprecated) ≈ Table~~ (现在推荐一个 Index ≈ 一个 Table 的概念)
  • Document ≈ Row
  • Field ≈ Column
  • Mapping ≈ Schema
  • Shard ≈ Table Partition (水平分区)
  • Replica ≈ Table Partition Replica (分区副本)

3. 安装与配置

环境准备

Elasticsearch 是用 Java 编写的,需要 Java 运行环境 (JRE)。请确保安装了 ES 版本兼容的 JDK 或 JRE。可以从 Oracle官网 或 OpenJDK 社区下载。安装后,检查 Java 版本:
bash
java -version

下载与安装 Elasticsearch

访问 Elastic 官网 (https://www.elastic.co/downloads/elasticsearch) 下载适合你操作系统的 ES 安装包(如 .tar.gz, .zip, .deb, .rpm)。

以 Linux/macOS 下使用 .tar.gz 包为例:
```bash

下载 (请替换为最新版本链接)

wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.x.y-linux-x86_64.tar.gz
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.x.y-linux-x86_64.tar.gz.sha512

校验下载文件 (可选但推荐)

shasum -a 512 -c elasticsearch-8.x.y-linux-x86_64.tar.gz.sha512

解压

tar -xzf elasticsearch-8.x.y-linux-x86_64.tar.gz

进入目录

cd elasticsearch-8.x.y/
```

运行 Elasticsearch

进入解压后的目录,执行启动脚本:
bash
./bin/elasticsearch

首次启动时,ES 会进行一些初始化设置,并可能在控制台打印出重要的信息,例如集群名称、节点名称、默认密码 (如果启用了安全特性) 以及 Kibana 的注册 Token。请务必留意这些信息。

默认情况下,ES 监听 localhost:9200。你可以通过 curl 或浏览器访问 http://localhost:9200 来检查 ES 是否成功运行。如果启用了安全特性,访问时需要提供用户名和密码(通常是 elastic 和启动时生成的密码)。

```bash

检查 ES 是否运行 (如果需要认证,添加 -u elastic:PASSWORD)

curl http://localhost:9200

或者 (带认证)

curl -u elastic:YOUR_GENERATED_PASSWORD -k https://localhost:9200 # 注意:新版本默认启用 HTTPS 和安全特性,-k 忽略证书验证
成功会返回类似以下的 JSON 信息:json
{
"name" : "your_node_name",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "...",
"version" : { ... },
"tagline" : "You Know, for Search"
}
```

注意: ES 8.x 版本默认启用了安全特性 (HTTPS、用户认证)。初学者如果想简化体验,可以在 config/elasticsearch.yml 文件中禁用安全特性(不推荐在生产环境这样做!):
yaml
xpack.security.enabled: false
xpack.security.http.ssl.enabled: false
xpack.security.transport.ssl.enabled: false

修改配置后需要重启 ES。

安装 Kibana (可视化工具)

Kibana 是 Elasticsearch 的官方可视化工具,提供了强大的数据探索、可视化和仪表盘功能,以及方便的开发工具 (Dev Tools) 来与 ES API 交互。

下载并安装 Kibana (确保版本与 ES 兼容):
```bash

下载 (请替换为最新版本链接)

wget https://artifacts.elastic.co/downloads/kibana/kibana-8.x.y-linux-x86_64.tar.gz
wget https://artifacts.elastic.co/downloads/kibana/kibana-8.x.y-linux-x86_64.tar.gz.sha512
shasum -a 512 -c kibana-8.x.y-linux-x86_64.tar.gz.sha512
tar -xzf kibana-8.x.y-linux-x86_64.tar.gz
cd kibana-8.x.y/
修改 Kibana 配置文件 `config/kibana.yml`,指定 Elasticsearch 的地址:yaml

如果 ES 在本机且端口为 9200 (且禁用了安全特性)

elasticsearch.hosts: ["http://localhost:9200"]

如果 ES 启用了安全特性 (默认)

elasticsearch.hosts: ["https://localhost:9200"]

需要配置用户名密码或 Token 等进行连接,参考官方文档

运行 Kibana:bash
./bin/kibana
``
Kibana 默认运行在
http://localhost:5601`。打开浏览器访问该地址。首次访问可能需要配置连接 ES 的信息(如使用 ES 启动时生成的 Kibana Token)。

使用 Docker 快速部署 (推荐)

对于学习和开发,使用 Docker 是最快捷方便的方式。

单节点 ES + Kibana (无安全特性,仅供学习):
bash
docker network create elastic
docker run -d --name es01 --net elastic -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e "xpack.security.enabled=false" docker.elastic.co/elasticsearch/elasticsearch:8.x.y
docker run -d --name kib01 --net elastic -p 5601:5601 -e "ELASTICSEARCH_HOSTS=http://es01:9200" docker.elastic.co/kibana/kibana:8.x.y

8.x.y 替换为你想要使用的具体版本号。等待容器启动后,即可通过 http://localhost:9200 访问 ES,http://localhost:5601 访问 Kibana。

单节点 ES + Kibana (带安全特性,推荐接近生产的方式):
```bash
docker network create elastic
docker run -d --name es01 --net elastic -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e "ELASTIC_PASSWORD=YourStrongPassword" docker.elastic.co/elasticsearch/elasticsearch:8.x.y

启动后查看容器日志获取 Kibana Token: docker logs es01

或者使用设置的密码 elastic / YourStrongPassword

docker run -d --name kib01 --net elastic -p 5601:5601 -e "ELASTICSEARCH_HOSTS=https://es01:9200" -e "ELASTICSEARCH_USERNAME=elastic" -e "ELASTICSEARCH_PASSWORD=YourStrongPassword" -e "ELASTICSEARCH_SSL_VERIFICATIONMODE=none" docker.elastic.co/kibana/kibana:8.x.y

访问 https://localhost:9200 和 http://localhost:5601

浏览器访问 https://localhost:9200 可能需要接受自签名证书

登录 Kibana 使用 elastic / YourStrongPassword

```

4. 基本操作 (CRUD & Bulk API)

与 ES 交互:RESTful API

Elasticsearch 主要通过 HTTP RESTful API 进行交互。常用的 HTTP 方法包括:

  • GET: 获取信息(如获取文档、搜索、获取集群状态)。
  • POST: 创建资源(如创建文档时自动生成 ID)或执行操作(如搜索、批量操作)。
  • PUT: 创建或更新资源(如创建索引、创建/更新指定 ID 的文档)。
  • DELETE: 删除资源(如删除文档、删除索引)。

请求和响应体通常使用 JSON 格式。推荐使用 Kibana Dev Tools 或 curl 工具,或者各种编程语言的 ES 客户端库来与 ES 交互。以下示例主要使用 curl 格式,你可以在 Kibana Dev Tools 中直接运行这些命令(去掉 curl -X... 部分,保留方法和路径,以及 JSON 体)。

(假设 ES 运行在 localhost:9200 且禁用了安全特性,或已正确配置认证)

创建索引 (Index)

创建一个名为 my_first_index 的索引:
bash
curl -X PUT "localhost:9200/my_first_index"

如果成功,ES 会返回:
json
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "my_first_index"
}

也可以在创建时指定分片和副本数量(默认为 1 个主分片,1 个副本分片):
bash
curl -X PUT "localhost:9200/my_custom_index" -H 'Content-Type: application/json' -d'
{
"settings": {
"index": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}
}
'

添加/更新文档 (Indexing Documents)

向索引中添加数据称为“索引文档”。

方法一:PUT /index/_doc/id (指定文档 ID)

如果 ID 已存在,则会更新文档(替换整个文档);如果 ID 不存在,则会创建新文档。
bash
curl -X PUT "localhost:9200/my_first_index/_doc/1" -H 'Content-Type: application/json' -d'
{
"user": "Alice",
"message": "Hello Elasticsearch!",
"post_date": "2024-01-01T10:00:00"
}
'

响应会包含索引、ID、版本号等信息:
json
{
"_index" : "my_first_index",
"_id" : "1",
"_version" : 1,
"result" : "created", // 或 "updated"
"_shards" : { ... },
"_seq_no" : 0,
"_primary_term" : 1
}

方法二:POST /index/_doc (让 ES 自动生成 ID)

每次执行都会创建新文档。
bash
curl -X POST "localhost:9200/my_first_index/_doc" -H 'Content-Type: application/json' -d'
{
"user": "Bob",
"message": "Learning ES is fun",
"post_date": "2024-01-02T11:30:00"
}
'

响应中 _id 会是一个 ES 自动生成的唯一字符串。

获取文档 (Retrieving Documents)

通过 GET /index/_doc/id 获取指定 ID 的文档:
bash
curl -X GET "localhost:9200/my_first_index/_doc/1"

响应中 _source 字段包含原始 JSON 文档:
json
{
"_index" : "my_first_index",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"user" : "Alice",
"message" : "Hello Elasticsearch!",
"post_date" : "2024-01-01T10:00:00"
}
}

如果文档不存在,found 会是 false

删除文档 (Deleting Documents)

通过 DELETE /index/_doc/id 删除指定 ID 的文档:
bash
curl -X DELETE "localhost:9200/my_first_index/_doc/1"

响应会告知操作结果。

更新文档 (Updating Documents) - 部分更新

使用 POST /index/_update/id API 可以只更新文档的部分字段,而不是替换整个文档。
bash
curl -X POST "localhost:9200/my_first_index/_update/1" -H 'Content-Type: application/json' -d'
{
"doc": {
"message": "Updated message!",
"tags": ["elasticsearch", "tutorial"]
}
}
'

这会将 ID 为 1 的文档的 message 字段更新,并添加 tags 字段。如果文档不存在,会报错。也可以使用 upsert 参数实现在文档不存在时插入新文档。

批量操作 (Bulk API)

当需要执行大量索引、更新或删除操作时,逐条发送请求效率很低。_bulk API 允许在一个请求中执行多个操作。

Bulk 请求体格式比较特殊,每两行为一个操作单元:
第一行:描述操作类型(index, create, update, delete)和元数据(如 _index, _id)。
第二行:对于 index, create, update 操作,是文档数据或更新脚本(delete 操作没有第二行)。

注意:请求体最后必须有一个换行符!

```bash
curl -X POST "localhost:9200/_bulk" -H 'Content-Type: application/x-ndjson' -d'
{ "index" : { "_index" : "my_first_index", "_id" : "3" } }
{ "user" : "Charlie", "message" : "Bulk indexing example 1" }
{ "index" : { "_index" : "my_first_index", "_id" : "4" } }
{ "user" : "David", "message" : "Bulk indexing example 2" }
{ "update" : {"_id" : "1", "_index" : "my_first_index"} }
{ "doc" : {"views" : 100} }
{ "delete" : { "_index" : "my_first_index", "_id" : "2" } }
'

注意最后有一个换行符

```
响应会包含每个操作的结果。使用 Bulk API 可以显著提高写入性能。

5. 核心功能:搜索 (Searching)

搜索是 Elasticsearch 的核心能力。

搜索基础:_search API

所有搜索请求都通过 _search 端点进行。可以针对特定索引 (/index_name/_search)、多个索引 (/index1,index2/_search) 或所有索引 (/_search) 进行搜索。

URI 搜索 vs 请求体搜索

  • URI 搜索: 将查询参数直接放在 URL 中。简单方便,但功能有限。
    bash
    # 查找 message 字段包含 "elasticsearch" 的文档
    curl -X GET "localhost:9200/my_first_index/_search?q=message:elasticsearch"
  • 请求体搜索 (Request Body Search): 将查询逻辑放在请求体 (JSON) 中,使用 Query DSL。功能极其强大和灵活,是推荐的方式。
    bash
    curl -X GET "localhost:9200/my_first_index/_search" -H 'Content-Type: application/json' -d'
    {
    "query": {
    // 查询语句放在这里
    }
    // 其他选项如排序、分页、高亮等也放在这里
    }
    '

查询 DSL (Query DSL) 入门

Query DSL (Domain Specific Language) 是一种基于 JSON 定义查询的灵活方式。主要包含两种类型的子句:

  • 叶子查询子句 (Leaf Query Clauses): 用于在特定字段中查找特定值,如 match, term, range
  • 复合查询子句 (Compound Query Clauses): 用于组合其他叶子或复合查询,以实现更复杂的逻辑,如 bool 查询。

一些常用的查询类型:

  • match_all 查询:匹配所有文档
    json
    {
    "query": {
    "match_all": {}
    }
    }

  • match 查询:标准全文搜索
    这是进行全文搜索的标准查询。它会先对查询字符串进行分词,然后查找包含这些词项的文档。
    json
    {
    "query": {
    "match": {
    "message": "learning elasticsearch fun" // 会被分词为 "learning", "elasticsearch", "fun"
    }
    }
    }

    默认情况下,match 查询是 OR 逻辑(包含任何一个词项即可),可以通过 operator 参数改为 and

  • term 查询:精确匹配 (未分词)
    用于查找未经分词的精确值。通常用于 keyword 类型的字段、数字、日期或布尔值。
    json
    {
    "query": {
    "term": {
    "user": "Alice" // 假设 user 字段是 keyword 类型或未分词的 text
    }
    }
    }

    重要: 如果对一个标准分词的 text 字段使用 term 查询,通常需要查询具体的词项(小写,去除标点等),而不是原始短语。例如,对于 "Hello Elasticsearch!",term 查询 message: "Hello Elasticsearch!" 不会匹配,但 term 查询 message: "hello"message: "elasticsearch" 可能会匹配(取决于分词结果)。

  • terms 查询:匹配多个精确值
    允许指定一个值的列表,匹配字段值是列表中任何一个的文档。
    json
    {
    "query": {
    "terms": {
    "user": ["Alice", "Bob"] // 匹配 user 是 "Alice" 或 "Bob" 的文档
    }
    }
    }

  • 组合查询 (bool query)
    用于组合多个查询子句,是最常用的复合查询。有四种布尔逻辑:

    • must: 子句必须匹配。贡献得分。相当于逻辑 AND。
    • should: 子句应该匹配。匹配的越多,得分越高。相当于逻辑 OR。如果 bool 查询中没有 must 子句,则至少需要匹配一个 should 子句。
    • must_not: 子句必须不匹配。在 filter 上下文中执行,不贡献得分。相当于逻辑 NOT。
    • filter: 子句必须匹配,但在过滤上下文中执行。不计算得分,速度更快,且结果可被缓存。非常适合用于精确匹配、范围查询等只需要判断“是/否”的场景。

    json
    {
    "query": {
    "bool": {
    "must": [
    { "match": { "message": "elasticsearch" } }
    ],
    "filter": [
    { "term": { "status": "published" } },
    { "range": { "post_date": { "gte": "2024-01-01" } } }
    ],
    "should": [
    { "match": { "tags": "tutorial" } }
    ],
    "must_not": [
    { "match": { "author": "spam_user" } }
    ]
    }
    }
    }

    这个查询要求 message 必须包含 "elasticsearch",并且 status 必须是 "published",post_date 必须大于等于 2024-01-01,同时不能是 spam_user 写的,如果 tags 包含 "tutorial" 会获得更高的相关性得分。

  • 范围查询 (range query)
    查找字段值落在指定范围内的文档。适用于数字、日期等类型。
    json
    {
    "query": {
    "range": {
    "views": {
    "gte": 100, // Greater than or equal to
    "lt": 1000 // Less than
    }
    }
    }
    }

    常用操作符:gte (>=), gt (>), lte (<=), lt (<)。

排序 (sort)

默认情况下,搜索结果按相关性得分 (_score) 降序排列。可以使用 sort 参数按一个或多个字段排序。
json
{
"query": { "match_all": {} },
"sort": [
{ "post_date": { "order": "desc" } }, // 按 post_date 降序
{ "_score": { "order": "desc" } } // 日期相同则按得分降序
]
}

分页 (from, size)

  • size: 指定每页返回多少条结果(默认为 10)。
  • from: 指定从第几条结果开始返回(默认为 0)。相当于偏移量。

json
{
"query": { "match": { "message": "elasticsearch" } },
"from": 10, // 跳过前 10 条
"size": 5 // 返回第 11 到 15 条
}

注意: 使用 fromsize 进行深度分页(from 值很大时)会消耗大量资源。对于深度分页,推荐使用 search_after 参数或滚动查询 (Scroll API)。

高亮 (highlighting)

在搜索结果中高亮显示匹配的查询词。
json
{
"query": {
"match": { "message": "elasticsearch fun" }
},
"highlight": {
"fields": {
"message": {} // 对 message 字段进行高亮
},
"pre_tags": ["<em>"], // 高亮起始标签
"post_tags": ["</em>"] // 高亮结束标签
}
}

高亮结果会出现在每个 hithighlight 字段中。

6. 映射 (Mapping) 详解

什么是映射?

映射 (Mapping) 定义了 Elasticsearch 如何处理索引中的文档及其包含的字段。它规定了:

  • 哪些字段被视为全文 (text)?哪些是精确值 (keyword, 数字, 日期等)?
  • 字段的数据类型 (如 string, integer, date, boolean, object, nested 等)。
  • 字段值在索引前应如何处理(例如,使用哪个分析器 (Analyzer) 对文本进行分词)。
  • 其他高级选项(是否索引、是否存储 _source、格式化等)。

动态映射 (Dynamic Mapping)

当你索引一个文档到一个新索引(或向现有索引添加一个新字段)而没有预先定义映射时,Elasticsearch 会自动猜测字段类型并创建映射,这就是动态映射

优点: 上手简单,无需预先定义结构。
缺点:
* 类型猜测可能不准确(例如,字符串可能被错误地映射为 text 而不是 keyword,或者数字可能被错误地映射为 long 而不是 float)。
* 可能导致不期望的分词行为。
* 可能导致 "映射爆炸" (mapping explosion),即过多唯一字段名导致集群元数据过大。

动态映射规则可以通过动态模板 (Dynamic Templates) 进行定制。

显式映射 (Explicit Mapping)

在生产环境中,强烈建议使用显式映射,即在索引第一个文档之前手动定义索引的映射。

优点:
* 对数据结构有完全的控制。
* 确保字段被正确地索引和分析。
* 优化存储和搜索性能。

定义映射: 在创建索引时通过 mappings 参数指定。

bash
curl -X PUT "localhost:9200/my_products" -H 'Content-Type: application/json' -d'
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"product_id": { "type": "keyword" },
"name": {
"type": "text",
"analyzer": "standard",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"description": { "type": "text", "analyzer": "english" },
"price": { "type": "float" },
"stock_count": { "type": "integer" },
"on_sale": { "type": "boolean" },
"tags": { "type": "keyword" },
"created_at": { "type": "date", "format": "yyyy-MM-dd HH:mm:ss||epoch_millis" }
}
}
}
'

这个例子定义了 my_products 索引的映射:
* product_idtagskeyword 类型,用于精确匹配、聚合和排序。
* nametext 类型,用于全文搜索(使用标准分词器)。同时,通过 fields 定义了一个名为 keyword多字段 (multi-field),允许对 name 字段也进行精确匹配和聚合(使用 name.keyword)。
* descriptiontext 类型,使用英文分词器。
* price, stock_count, on_sale, created_at 分别定义为浮点数、整数、布尔值和日期类型。created_at 指定了接受的日期格式。

核心数据类型

  • text: 用于索引全文(如文章内容、邮件正文)。这些字段会被分析(分词),以便进行全文搜索。不直接用于排序或聚合。
  • keyword: 用于索引结构化内容(如邮箱地址、主机名、状态码、标签、商品 ID)。这些字段不被分析(作为一个整体索引),适用于精确匹配、范围查询(如果值有序)、排序和聚合。
  • 数字类型: long, integer, short, byte, double, float, half_float, scaled_float.
  • date: 用于日期和时间。可以指定多种格式。内部以毫秒时间戳存储。
  • boolean: 接受 truefalse
  • binary: Base64 编码的二进制值。
  • range: 范围类型 (integer_range, float_range, long_range, double_range, date_range).
  • 地理类型: geo_point (经纬度点), geo_shape (复杂形状如多边形)。
  • 复合类型: object (默认的 JSON 对象), nested (用于索引对象数组,保持对象内字段关联性)。

text vs keyword 的区别

这是初学者最容易混淆的地方:

  • text 字段:

    • 主要用途:全文搜索。
    • 处理方式:内容会被分析器 (Analyzer) 处理(如分词、转小写、去除停用词等),生成多个词项 (term)
    • 存储:存储的是分析后的词项列表(倒排索引)。
    • 查询:通常使用 match 查询。
    • 排序/聚合:默认不开启 (fielddata 默认禁用,因为它消耗大量内存)。如果需要,需要显式开启 fielddata=true (不推荐) 或使用多字段 (fields) 定义一个 keyword 子字段。
    • 示例:"Quick brown fox" -> quick, brown, fox
  • keyword 字段:

    • 主要用途:精确匹配、过滤、排序、聚合。
    • 处理方式:内容不被分析,整个字符串作为一个单一的词项被索引。
    • 存储:存储的是原始的完整字符串。
    • 查询:通常使用 term, terms, prefix, wildcard, regexp 查询。
    • 排序/聚合:天然支持
    • 示例:"Quick brown fox" -> Quick brown fox

选择建议:
* 如果字段需要进行全文搜索(像搜索文章内容),用 text
* 如果字段用于精确匹配(如用户 ID、状态码)、排序或聚合,用 keyword
* 如果一个字段既需要全文搜索,又需要精确匹配/排序/聚合,使用 text 类型,并通过 fields 定义一个 keyword 子字段。

定义和更新映射

  • 定义: 在创建索引时通过 mappings 指定。
  • 更新: 只能向现有映射中添加新字段不能修改现有字段的类型或其索引方式。如果需要修改现有字段,必须创建一个新索引(使用正确的映射),然后将数据从旧索引迁移到新索引(使用 Reindex API)。
    bash
    # 向现有索引 my_products 添加一个新字段 last_updated
    curl -X PUT "localhost:9200/my_products/_mapping" -H 'Content-Type: application/json' -d'
    {
    "properties": {
    "last_updated": {
    "type": "date"
    }
    }
    }
    '

7. 文本分析 (Analysis) 简介

文本分析是将文本(如一句话或一篇文章)转换为可供搜索的词项 (term) 的过程。这是 text 字段实现全文搜索的核心机制。

分析器 (Analyzer) 的作用

分析器是执行文本分析任务的组件。一个分析器通常由三部分按顺序组成:

  1. 字符过滤器 (Character Filters): 在文本被分词之前对其进行预处理。例如,去除 HTML 标签、替换字符等。(0 个或多个)
  2. 分词器 (Tokenizer): 将文本流分割成独立的词项 (token)。例如,按空格或标点符号分割。(必须有 1 个)
  3. Token 过滤器 (Token Filters): 对分词器输出的词项进行处理。例如,转换为小写、删除停用词(如 "a", "the", "is")、添加同义词、词干提取(将词语还原为词根,如 "running" -> "run")等。(0 个或多个)

内置分析器

Elasticsearch 提供了许多内置的分析器,可以直接使用:

  • standard Analyzer: 默认分析器。基于 Unicode 文本分割算法,按词分割,转小写。对大多数西方语言效果不错。
  • simple Analyzer: 按非字母字符分割,然后转小写。
  • whitespace Analyzer: 仅按空白字符分割。不转小写。
  • keyword Analyzer: “无操作”分析器,将整个输入字符串作为一个单独的词项输出。
  • english, french, german 等语言分析器: 针对特定语言优化,包含该语言的停用词和词干提取规则。

可以使用 _analyze API 测试分析器的效果:
```bash
curl -X POST "localhost:9200/_analyze" -H 'Content-Type: application/json' -d'
{
"analyzer": "standard",
"text": "Elasticsearch is FUN!"
}
'

响应会显示生成的 tokens: "elasticsearch", "is", "fun"

curl -X POST "localhost:9200/_analyze" -H 'Content-Type: application/json' -d'
{
"analyzer": "english",
"text": "Elasticsearch is FUN!"
}
'

响应会显示生成的 tokens: "elasticsearch", "fun" ("is" 被作为停用词移除)

```

自定义分析器 (概述)

当内置分析器无法满足需求时(例如,需要特定的分词逻辑、同义词处理、或组合使用不同的过滤器),可以在索引设置中定义自定义分析器。这涉及到组合选择字符过滤器、分词器和 Token 过滤器。

8. 聚合 (Aggregations) 入门

聚合是 Elasticsearch 的另一个强大功能,允许你对数据进行分组和统计分析,类似于 SQL 中的 GROUP BY 和聚合函数 (SUM, COUNT, AVG 等)。聚合是实时计算的,速度非常快。

聚合的概念

聚合框架可以让你从数据中获得分析性的信息。聚合操作在查询匹配的文档集上执行。

聚合主要分为两大类:

  • 指标聚合 (Metrics Aggregations): 对字段值进行统计计算,如求和 (sum)、平均值 (avg)、最大值 (max)、最小值 (min)、统计信息 (stats - 同时返回 count, min, max, avg, sum)、计数值 (value_count)、基数统计 (cardinality - 统计唯一值数量) 等。这些聚合输出一个数值结果
  • 桶聚合 (Bucket Aggregations): 将满足特定条件的文档划分到不同的“桶” (Bucket) 中。每个桶关联一个键和一个文档集合。类似于 SQL 的 GROUP BY。常见的桶聚合有:
    • terms:按字段的唯一值分组。
    • range:按数值或日期的范围分组。
    • date_histogram / histogram:按固定的日期或数值间隔分组。
    • filters:按多个过滤条件分组。
    • nested / reverse_nested:用于处理嵌套文档的聚合。

聚合可以嵌套,即在一个桶聚合的结果内部再进行指标聚合或其他桶聚合,实现复杂的多维度分析。

聚合的基本使用示例

聚合请求与查询一起放在 _search API 的请求体中。size: 0 通常用于只关心聚合结果,不关心具体的文档 hits

示例 1:计算所有产品的平均价格 (指标聚合)
json
{
"size": 0, // 不需要返回具体的文档
"aggs": { // "aggs" 或 "aggregations" 都可以
"average_price": { // 给这个聚合起个名字
"avg": { // 聚合类型:平均值
"field": "price" // 在哪个字段上计算
}
}
}
}

响应中的 aggregations 部分会包含名为 average_price 的结果。

示例 2:按标签分组,统计每个标签下的产品数量 (桶聚合)
json
{
"size": 0,
"aggs": {
"products_per_tag": { // 聚合名称
"terms": { // 聚合类型:按词项分组
"field": "tags" // 分组依据字段 (通常是 keyword 类型)
}
}
}
}

响应会列出每个标签(桶的 key)以及该标签下的文档数量(doc_count)。

示例 3:按标签分组,并计算每个标签下产品的平均价格 (桶聚合 + 嵌套指标聚合)
json
{
"size": 0,
"aggs": {
"tags_agg": { // 外层桶聚合名称
"terms": { "field": "tags" },
"aggs": { // 在每个桶内进行嵌套聚合
"avg_price_per_tag": { // 内层指标聚合名称
"avg": { "field": "price" }
}
}
}
}
}

响应会显示每个标签及其对应的文档数,以及该标签下所有产品的平均价格。

聚合功能非常强大,是 Elasticsearch 进行数据分析和构建仪表盘的基础。

9. 进阶话题与后续学习

本指南涵盖了 Elasticsearch 的入门基础。要深入掌握 ES,可以继续学习以下方面:

  • 分布式特性深入: 理解分片如何路由请求、集群如何发现节点、故障检测和恢复机制。
  • 性能调优:
    • 索引优化: 合理设置分片数、选择合适的硬件、调整 refresh interval、使用 Bulk API、优化映射。
    • 查询优化: 理解查询如何执行、使用 filter 上下文、避免脚本查询、优化聚合、使用 profile API 分析查询性能。
    • JVM 调优: 合理配置堆内存大小。
  • 安全配置: 学习配置用户认证 (密码、证书、SAML、OIDC)、基于角色的访问控制 (RBAC)、字段级和文档级安全、启用 TLS/SSL 加密通信。
  • 插件生态: 了解常用的插件,如 ICU Analysis (增强分词)、Mapper Attachments (已废弃,替代方案是 Ingest Attachment Processor) 等。
  • Elastic Stack (ELK): 学习如何结合 Logstash (数据收集和转换)、Beats (轻量级数据收集器) 和 Kibana (可视化) 构建完整的日志分析、监控或其他解决方案。
  • 客户端库: 学习使用官方或社区提供的各种编程语言客户端库(Java, Python, JavaScript, Go, Ruby, PHP 等)与 ES 交互。
  • 索引生命周期管理 (ILM): 自动管理索引(如基于时间创建新索引、将旧索引转为只读、迁移到冷节点、最终删除),常用于日志和时序数据。
  • 跨集群复制 (CCR) 和跨集群搜索 (CCS): 实现多数据中心部署或数据共享。

10. 总结

Elasticsearch 是一个功能极其强大且灵活的搜索和分析引擎。通过本指南,您应该已经了解了它的核心概念(集群、节点、索引、文档、分片、副本、映射),掌握了基本的安装、配置和 CRUD 操作,并初步接触了核心的搜索 (Query DSL)、文本分析和聚合功能。

要真正精通 Elasticsearch,关键在于实践。尝试搭建环境,索引自己的数据,不断试验各种查询和聚合,并深入理解映射和分析对搜索结果的影响。利用 Kibana Dev Tools 可以方便地进行实验。

Elasticsearch 的学习曲线可能有些陡峭,但其带来的强大功能和性能回报是巨大的。希望本指南能为您开启探索 Elasticsearch 世界的大门!祝您学习愉快!

THE END