Elasticsearch 入门指南:开启你的数据搜索与分析之旅
在当今这个信息爆炸的时代,我们面临着海量的数据。如何高效地存储、搜索、分析和利用这些数据,成为了许多企业和开发者亟需解决的问题。关系型数据库在处理结构化数据方面表现出色,但在应对全文搜索、海量非结构化或半结构化数据的快速查询和实时分析时,往往显得力不从心。
正是在这样的背景下,像 Elasticsearch 这样的分布式搜索和分析引擎应运而生。它以其出色的性能、强大的功能和灵活的扩展性,迅速成为了全文搜索、日志分析、APM (应用性能监控)、安全信息和事件管理 (SIEM) 等领域的首选解决方案。
如果你是初次接触 Elasticsearch,可能会觉得它有些复杂。但请放心,本指南将从零开始,带你逐步了解 Elasticsearch 的核心概念、工作原理,并指导你完成安装和基本的增删改查及搜索操作。通过阅读本文,你将掌握入门所需的关键知识,为进一步深入学习打下坚实的基础。
本文篇幅较长,旨在提供尽可能详细的入门信息。请耐心阅读,并最好能跟着文章的步骤进行实践。
第一部分:认识 Elasticsearch – 它是什么?为什么选择它?
1. Elasticsearch 是什么?
简单来说,Elasticsearch 是一个基于 Lucene 的开源分布式搜索和分析引擎。它以其强大的搜索能力、水平扩展性、易用性以及实时分析能力而闻名。
- 基于 Lucene: Elasticsearch 底层使用了 Apache Lucene 这个高性能的全文搜索库。Lucene 提供了核心的索引和搜索功能,而 Elasticsearch 在此基础上构建了一个分布式、易于使用的层。
- 分布式: Elasticsearch 设计之初就是分布式的。这意味着它可以轻松地扩展到数百甚至数千台服务器,处理 PB 级别的数据。数据会自动分布到集群中的不同节点上,提高了可用性和容错性。
- 搜索与分析: Elasticsearch 不仅是一个搜索引擎,它还具备强大的数据分析能力。你可以通过其聚合 (Aggregations) 功能,对数据进行分组、计算统计指标(如求和、平均值、最大值、最小值)、构建各种图表等。
- 实时: Elasticsearch 提供近乎实时 (Near Real-Time, NRT) 的数据检索能力。一旦文档被索引,它很快就可以被搜索到(通常在几秒钟之内)。
- RESTful API: Elasticsearch 通过标准的 RESTful API 进行交互,这使得开发者可以使用几乎任何编程语言与之通信,极大地提高了开发的便捷性。
- Schema-less (初期): 虽然 Elasticsearch 内部有严格的数据类型处理,但在索引文档时,它可以通过动态映射 (Dynamic Mapping) 自动检测字段类型,无需提前定义严格的模式,这为处理半结构化数据带来了极大的灵活性。
2. 为什么选择 Elasticsearch?
你可能会问,既然有关系型数据库或者其他的 NoSQL 数据库,为什么还需要 Elasticsearch 呢? Elasticsearch 的优势主要体现在以下几个方面:
- 强大的全文搜索能力: 这是 Elasticsearch 的核心优势。它提供了丰富的搜索功能,包括模糊查询、短语查询、高亮显示、自动补全等,能够处理复杂的文本搜索需求。
- 极高的搜索速度: 基于倒排索引 (Inverted Index) 的数据结构以及优化的搜索算法,使得 Elasticsearch 能够在海量数据中实现毫秒级的搜索响应。
- 卓越的水平扩展性: 轻松通过添加节点来扩展集群的处理能力和存储容量,无需停机。分布式特性确保了高可用性。
- 实时数据分析: 通过聚合功能,可以方便地对数据进行实时的数据分析和可视化,这对于日志监控、业务智能等场景非常重要。
- 灵活的数据模型: 支持非结构化和半结构化数据,无需提前定义复杂的表结构,通过动态映射可以快速开始使用。
- 生态系统丰富: Elastic Stack (ELK Stack 或 Elastic Stack) 包括 Elasticsearch, Kibana (可视化工具), Logstash (数据收集和处理) 和 Beats (轻量级数据采集器),提供了一整套完整的数据采集、处理、存储、分析和可视化解决方案。
典型应用场景:
- 电商网站搜索: 提供快速、精准的产品搜索。
- 日志和指标分析: 收集、存储、分析和可视化服务器日志、应用日志、监控指标等。
- 企业内部文档搜索: 索引和搜索企业内部的各种文档、邮件等。
- 地理位置搜索: 基于地理信息进行搜索和过滤。
- 安全分析: SIEM 系统通常使用 Elasticsearch 来存储和分析安全事件数据。
- APM: 监控应用程序的性能数据。
第二部分:核心概念解析 – 理解 Elasticsearch 的基石
在开始动手实践之前,理解 Elasticsearch 的几个核心概念至关重要。它们是理解 Elasticsearch 工作原理的基础。
1. 索引 (Index)
- 类比: 关系型数据库中的“数据库”或“表”。
- 定义: 索引是 Elasticsearch 中存储相关文档的集合。每个索引都有自己的映射 (Mapping) 和设置 (Settings)。它是进行文档管理(增删改查)和搜索操作的基本单位。例如,你可以创建一个用于存储产品信息的索引
products
,一个用于存储用户日志的索引user_logs
。 - 特点: 一个 Elasticsearch 集群可以包含多个索引。索引的名字必须是小写。
2. 文档 (Document)
- 类比: 关系型数据库中的“行”。
- 定义: 文档是 Elasticsearch 中可被索引的最小单元。它以 JSON (JavaScript Object Notation) 格式表示。文档包含了一系列的字段 (Fields)。例如,一个产品文档可能包含
name
,description
,price
,category
等字段。 - 特点: 每个文档在索引中都有一个唯一的 ID。尽管 JSON 是无模式的,但 Elasticsearch 会根据文档内容创建或使用预定义的映射来确定每个字段的数据类型。
3. 字段 (Field)
- 类比: 关系型数据库中的“列”。
- 定义: 字段是文档中的键值对,表示文档的某个属性。例如,产品文档中的
name
或price
就是字段。 - 特点: 每个字段都有其特定的数据类型,例如
text
,keyword
,integer
,float
,boolean
,
date,
geo_point` 等。字段的类型决定了其如何被索引和搜索。
4. 映射 (Mapping)
- 类比: 关系型数据库中的“表结构定义”。
- 定义: 映射描述了文档及其包含的字段如何被存储和索引。它定义了每个字段的数据类型以及如何处理字段的值(例如,一个文本字段是否需要进行分词处理)。
- 特点:
- 动态映射 (Dynamic Mapping): 当你索引一个新文档时,如果之前没有为某个字段定义映射,Elasticsearch 会尝试根据字段值的类型自动推断其映射。这使得入门和处理半结构化数据变得简单。
- 显式映射 (Explicit Mapping): 你也可以提前手动定义索引的映射,精确控制每个字段的数据类型和索引方式。这在需要更精确的搜索和分析时非常重要,例如指定文本字段使用特定的分词器,或者将字符串既索引为可搜索全文的
text
类型,又索引为精确匹配的keyword
类型。
5. 集群 (Cluster)
- 类比: 关系型数据库中的“数据库服务器集群”。
- 定义: 一个集群由一个或多个节点组成,这些节点协同工作,共享数据和提供索引与搜索能力。集群提供容错性和扩展性。
- 特点: 每个集群都有一个唯一的名称(默认是
elasticsearch
)。确保集群中的所有节点都使用相同的集群名称以便互相发现和加入。
6. 节点 (Node)
- 类比: 集群中的“单个数据库服务器实例”。
- 定义: 节点是 Elasticsearch 集群中的一个运行实例。当启动一个 Elasticsearch 实例时,它就是一个节点。通过配置相同的集群名称,节点可以自动发现并加入同一个集群。
- 特点: 节点可以扮演不同的角色,最常见的角色包括:
- Master-eligible Node: 可以被选为集群的主节点,负责管理集群的状态、处理节点加入/离开、分配分片等重要任务。
- Data Node: 存储数据并执行数据相关的操作,如索引、搜索和聚合。
- Ingest Node: 用于在索引文档之前对文档进行预处理(如转换、丰富数据)。
- Machine Learning Node: 用于执行机器学习相关任务。
- 在小型集群或入门场景中,一个节点通常会扮演多种角色。
7. 分片 (Shard)
- 类比: 数据库中的“分区”或“分片”。
- 定义: 分片是索引被分解成的更小的、可管理的部分。它是 Elasticsearch 分布式特性的基石。当索引数据量很大时,单个节点可能无法容纳,或者处理搜索请求的效率会下降。通过将索引分成多个分片,每个分片可以存储在集群的不同节点上,从而实现数据的水平分布和并行处理。
- 特点:
- 主分片 (Primary Shard): 每个文档都属于一个主分片。索引中的主分片数量在索引创建时就确定,且之后通常不能修改(除非重建索引)。主分片的数量决定了索引最大可存储的数据量和并行处理能力。
- 副本分片 (Replica Shard): 副本分片是主分片的拷贝。它们提供了数据冗余(提高可用性,防止数据丢失)和负载均衡(搜索请求可以由主分片或副本分片处理)。一个索引可以有零个或多个副本分片。副本分片的数量可以在索引创建后动态调整。
8. 副本 (Replica)
- 定义: 副本是主分片的精确复制。它们部署在与主分片不同的节点上,以提供高可用性和提高搜索吞吐量。
- 特点: 如果主分片所在的节点发生故障,一个副本分片可以被提升为新的主分片。搜索请求可以分发到主分片和所有副本分片上,从而分散负载。
9. 近实时 (Near Real-Time, NRT)
- 定义: Elasticsearch 是近实时的。这意味着从索引一个文档到它变得可搜索之间会有一个短暂的延迟(通常在几秒钟)。
- 原因: Elasticsearch 不是在每次索引操作后立即将数据写入永久存储并刷新到可搜索状态。它会批量处理这些操作,以提高性能。通过刷新 (Refresh) 操作,最新的数据会被写入到文件系统缓存中,使其可被搜索。这个刷新操作默认每秒发生一次(可以在索引设置中调整)。
理解了这些核心概念,你就能更好地把握 Elasticsearch 的工作方式,以及如何在分布式环境中管理和操作数据。
第三部分:安装与启动 – 让 Elasticsearch 跑起来
现在,让我们动手将 Elasticsearch 安装并运行起来。最简单的入门方式是在你的本地机器上下载并启动一个单节点集群。
1. 准备工作:安装 Java
Elasticsearch 是使用 Java 开发的,因此你需要安装 Java Development Kit (JDK)。Elasticsearch 通常要求特定版本的 Java,请查阅你计划安装的 Elasticsearch 版本的官方文档,确认兼容的 JDK 版本。通常,最新维护的 JDK LTS (长期支持) 版本都是兼容的(如 JDK 11, JDK 17)。
你可以从 Oracle 官网或其他 OpenJDK 提供商(如 AdoptOpenJDK, Azul Zulu, Amazon Corretto 等)下载并安装 JDK。安装完成后,确保 Java 的 bin
目录已经添加到系统的 PATH 环境变量中。
打开终端或命令提示符,运行以下命令检查 Java 是否安装成功:
bash
java -version
如果显示了 Java 版本信息,说明安装成功。
2. 下载 Elasticsearch
访问 Elastic 官方网站的下载页面 (https://www.elastic.co/cn/downloads/elasticsearch)。选择适合你操作系统的版本进行下载(提供了 .tar.gz
用于 Linux/macOS, .zip
用于 Windows, 以及 .deb
/.rpm
包)。对于入门,下载 .tar.gz
或 .zip
包是最方便的,无需安装到系统目录,解压即可用。
选择一个相对较新的版本(例如 7.x 或 8.x 版本)。请注意不同版本之间可能存在一些配置或 API 差异,但核心概念是相同的。
3. 解压并配置(可选)
将下载的文件解压到你选择的目录。例如:
- Linux/macOS:
bash
tar -xzf elasticsearch-<version>-linux-x86_64.tar.gz
cd elasticsearch-<version> - Windows: 使用解压工具(如 WinRAR, 7-Zip)将
.zip
文件解压到一个目录。然后通过文件浏览器进入该目录。
解压后,你会看到以下重要的子目录:
bin
: 包含可执行文件(如elasticsearch
启动脚本)。config
: 包含配置文件(最重要的配置文件是elasticsearch.yml
和jvm.options
)。lib
: 包含 Elasticsearch 运行所需的 Java 库。logs
: 存放日志文件。data
: 存放索引数据(初次启动时会自动创建)。
对于入门,你可能不需要修改任何配置。默认配置通常足够启动一个单节点集群。但了解几个重要的配置项是有益的:
config/elasticsearch.yml
:cluster.name
: 集群名称。默认是elasticsearch
。同一个集群内的所有节点必须使用相同的名称。node.name
: 节点名称。每个节点应该有一个唯一的名称。如果未设置,启动时会自动生成。network.host
: 绑定的网络地址。默认只绑定到localhost
(127.0.0.1)。如果你想让其他机器访问,需要修改为0.0.0.0
或特定的 IP 地址。注意: 在生产环境中将network.host
设置为0.0.0.0
是不安全的,除非你配置了防火墙或安全组。对于本地开发测试,默认值即可。http.port
: HTTP 端口。默认是 9200。transport.port
: 节点之间通信的端口。默认是 9300。
config/jvm.options
: 用于配置 Elasticsearch 进程的 JVM 参数,最常用的是调整堆内存大小 (-Xms
和-Xmx
)。对于入门,使用默认值即可。
4. 启动 Elasticsearch
打开终端或命令提示符,切换到 Elasticsearch 的根目录,然后运行启动脚本:
- Linux/macOS:
bash
./bin/elasticsearch - Windows:
bash
.\bin\elasticsearch.bat
Elasticsearch 将在前台启动,并在控制台输出日志信息。启动过程可能需要一些时间。当看到类似 [INFO ... ] started
的日志信息时,表示 Elasticsearch 节点已经成功启动并组建了一个单节点集群。
在生产环境中,通常会以后台服务的方式运行 Elasticsearch,并使用更健壮的启动脚本或服务管理器(如 Systemd, Docker 等)。但对于入门,前台启动更方便查看日志。
5. 验证启动
Elasticsearch 默认在 9200 端口提供 HTTP API 服务。你可以使用 curl
或浏览器访问该地址来验证它是否正在运行。
打开另一个终端或命令提示符,运行以下命令:
bash
curl http://localhost:9200
如果 Elasticsearch 正常运行,你应该会看到一个 JSON 响应,其中包含集群、节点和版本信息,类似这样(具体内容会因版本而异):
json
{
"name" : "your_node_name",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "...",
"version" : {
"number" : "8.x.x",
"build_flavor" : "default",
"build_type" : "tar",
"build_hash" : "...",
"build_date" : "...",
"build_snapshot" : false,
"lucene_version" : "...",
"minimum_wire_compatibility_version" : "...",
"minimum_index_compatibility_version" : "..."
},
"tagline" : "You Know, for Search"
}
你也可以检查集群的健康状态:
bash
curl http://localhost:9200/_cat/health?v
输出类似:
epoch timestamp cluster status index docs.count docs.deleted active.shards relocating.shards active.primary_shards initing.shards unassigned.shards pending_tasks max_task_wait_time active.gilmer.order
1678886400 11:20:00 elasticsearch green 0 0 0 0 0 0 0 0 - 0
status
为 green
表示集群健康状况良好,所有主分片和副本分片都已分配并可用。如果是 yellow
,通常表示所有主分片可用,但部分副本分片尚未分配(在单节点集群中,如果没有配置副本,或者副本配置多于节点数,就可能是 yellow)。如果是 red
,表示部分主分片不可用,集群存在问题。
恭喜!你已经成功启动了一个 Elasticsearch 单节点集群。
第四部分:与 Elasticsearch 交互 – 使用 RESTful API
Elasticsearch 提供了一套全面的 RESTful API,你可以通过 HTTP 请求(GET, POST, PUT, DELETE 等)与它进行交互。你可以使用 curl
命令、Postman 工具、编程语言的 HTTP 库,或者 Kibana 的 Dev Tools 控制台来发送这些请求。
在本指南中,我们将主要使用 curl
命令来演示操作。
1. Kibana Dev Tools (推荐用于学习)
在实际开发和学习中,Kibana 的 Dev Tools 是一个非常方便的工具。如果你已经安装并启动了 Kibana (通常与 Elasticsearch 一起下载安装,并在默认端口 5601 运行),你可以访问 http://localhost:5601/app/dev_tools
。
Dev Tools 提供了一个交互式控制台,左侧输入 API 请求,右侧显示响应。它的优点是可以自动格式化 JSON,提供语法高亮和自动补全。对于初学者来说,使用 Dev Tools 比直接使用 curl
命令行更容易。
安装 Kibana: 访问 Elastic 官方网站下载页面 (https://www.elastic.co/cn/downloads/kibana),下载与你的 Elasticsearch 版本兼容的 Kibana 版本,解压并运行 bin/kibana
(或 bin\kibana.bat
) 即可。默认情况下,Kibana 会尝试连接运行在 localhost:9200
的 Elasticsearch。
2. 使用 curl
进行交互
即使使用 Kibana Dev Tools,理解 curl
命令也很有帮助,因为它直接反映了底层的 HTTP 请求。
基本格式:
bash
curl -X <HTTP方法> '<URL>' -H 'Content-Type: application/json' -d '<请求体,通常是JSON>'
-X <HTTP方法>
: 指定 HTTP 方法,如GET
,POST
,PUT
,DELETE
。'<URL>'
: Elasticsearch API 的端点。通常是http://localhost:9200/<索引名称>/<_doc>/<文档ID>
或http://localhost:9200/_<API端点>
。-H 'Content-Type: application/json'
: 告诉服务器请求体是 JSON 格式。在发送带有请求体的 POST 或 PUT 请求时很重要。-d '<请求体>'
: 指定请求体的内容。对于 JSON 请求,需要将 JSON 字符串放在单引号或双引号内。
注意: 在 Windows 的 cmd
或 PowerShell 中使用 curl
可能需要稍微调整语法,或者直接使用 Invoke-RestMethod
。建议在 Linux/macOS 或安装了 Bash 环境的 Windows (如 Git Bash, WSL) 中使用 curl
。或者直接使用 Kibana Dev Tools。
第五部分:基本操作 – 增删改查 (CRUD)
现在我们来学习如何在 Elasticsearch 中执行基本的文档操作。
1. 索引(添加/更新)文档 (Index Documents)
索引一个文档就是将一个 JSON 文档存储到指定的索引中。Elasticsearch 提供了两种主要方法来索引文档:
- 使用指定的 ID (PUT): 如果你知道文档的唯一 ID,可以使用
PUT
请求,URL 格式为/<index>/_doc/<_id>
。如果文档 ID 已经存在,PUT
请求会替换现有文档。 - 自动生成 ID (POST): 如果你想让 Elasticsearch 自动为文档生成一个唯一的 ID,可以使用
POST
请求,URL 格式为/<index>/_doc
。
示例:索引一个带有指定 ID 的产品文档
假设我们有一个名为 products
的索引(如果不存在,Elasticsearch 会自动创建)。
“`bash
使用 curl
curl -X PUT ‘localhost:9200/products/_doc/1?pretty’ -H ‘Content-Type: application/json’ -d ‘
{
“name”: “Laptop”,
“price”: 1200,
“stock”: 50,
“description”: “Powerful laptop for work and gaming.”,
“category”: “Electronics”,
“created_at”: “2023-03-15T10:00:00Z”
}
‘
在 Kibana Dev Tools 中
PUT /products/_doc/1
{
“name”: “Laptop”,
“price”: 1200,
“stock”: 50,
“description”: “Powerful laptop for work and gaming.”,
“category”: “Electronics”,
“created_at”: “2023-03-15T10:00:00Z”
}
“`
products
: 索引名称。_doc
: Elasticsearch 中表示文档类型的默认端点(在 Elasticsearch 6.x 及以后版本中推荐使用,代替了旧版本的自定义 Type)。1
: 文档 ID。?pretty
: 可选参数,使 JSON 响应格式化,更易读。- 请求体是 JSON 格式的文档内容。
响应会返回一些元信息,包括索引名称 (_index
), 文档 ID (_id
), 版本 (_version
), 操作结果 (result
,例如 created
或 updated
), 以及是否包含分片失败 (_shards
信息)。
示例:索引一个由 Elasticsearch 自动生成 ID 的产品文档
“`bash
使用 curl
curl -X POST ‘localhost:9200/products/_doc?pretty’ -H ‘Content-Type: application/json’ -d ‘
{
“name”: “Mouse”,
“price”: 25,
“stock”: 200,
“description”: “Wireless ergonomic mouse.”,
“category”: “Electronics”
}
‘
在 Kibana Dev Tools 中
POST /products/_doc
{
“name”: “Mouse”,
“price”: 25,
“stock”: 200,
“description”: “Wireless ergonomic mouse.”
}
“`
响应中的 _id
字段将是 Elasticsearch 生成的唯一 ID。
重要提示: 索引操作是近实时的。文档被索引后,需要经过刷新操作才能被搜索到。默认情况下,刷新每秒发生一次。
2. 获取文档 (Get Documents)
通过 GET 请求可以根据文档 ID 获取特定的文档。
“`bash
使用 curl
curl -X GET ‘localhost:9200/products/_doc/1?pretty’
在 Kibana Dev Tools 中
GET /products/_doc/1
“`
响应会返回文档的元信息以及 _source
字段,其中包含了原始的 JSON 文档内容:
json
{
"_index" : "products",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"name" : "Laptop",
"price" : 1200,
"stock" : 50,
"description" : "Powerful laptop for work and gaming.",
"category" : "Electronics",
"created_at" : "2023-03-15T10:00:00Z"
}
}
如果文档不存在,found
字段将是 false
,且没有 _source
字段。
3. 更新文档 (Update Documents)
更新文档有两种方式:
- 替换整个文档 (PUT): 使用与索引文档相同的
PUT /<index>/_doc/<_id>
请求。这会用新的文档 完全替换 现有文档。你需要提供完整的文档内容,即使只修改了其中一小部分字段。 - 部分更新文档 (POST _update): 使用
POST /<index>/_update/<_id>
请求。这种方式更加高效,因为它只更新文档中指定的字段,而不需要获取、修改、然后重新索引整个文档。
示例:使用 PUT 替换文档(不推荐用于部分更新)
假设要更新 ID 为 1 的 Laptop 文档的价格和库存。
“`bash
使用 curl
curl -X PUT ‘localhost:9200/products/_doc/1?pretty’ -H ‘Content-Type: application/json’ -d ‘
{
“name”: “Laptop”,
“price”: 1250, # 价格更新
“stock”: 45, # 库存更新
“description”: “Powerful laptop for work and gaming.”, # 其他字段必须保留
“category”: “Electronics”,
“created_at”: “2023-03-15T10:00:00Z”
}
‘
在 Kibana Dev Tools 中
PUT /products/_doc/1
{
“name”: “Laptop”,
“price”: 1250,
“stock”: 45,
“description”: “Powerful laptop for work and gaming.”,
“category”: “Electronics”,
“created_at”: “2023-03-15T10:00:00Z”
}
“`
注意,你必须提供完整的文档内容。
示例:使用 POST _update 部分更新文档 (推荐)
更新 ID 为 1 的 Laptop 文档的价格和库存,只发送需要修改的字段。
“`bash
使用 curl
curl -X POST ‘localhost:9200/products/_update/1?pretty’ -H ‘Content-Type: application/json’ -d ‘
{
“doc”: {
“price”: 1250,
“stock”: 45
}
}
‘
在 Kibana Dev Tools 中
POST /products/_update/1
{
“doc”: {
“price”: 1250,
“stock”: 45
}
}
“`
在请求体中,你需要一个 doc
字段,其值是一个 JSON 对象,包含你想要更新的字段及其新值。Elasticsearch 会在内部获取现有文档,合并 doc
对象中的字段,然后重新索引合并后的文档。
4. 删除文档 (Delete Documents)
通过 DELETE 请求并指定文档 ID 来删除文档。
“`bash
使用 curl
curl -X DELETE ‘localhost:9200/products/_doc/1?pretty’
在 Kibana Dev Tools 中
DELETE /products/_doc/1
“`
响应会返回操作结果 (result
为 deleted
)。删除操作也是近实时的。文档被标记为删除,但不会立即从底层存储中移除,而是在后台进行清理。
5. 删除索引 (Delete Index)
如果你想删除整个索引及其包含的所有文档,可以使用 DELETE 请求并指定索引名称。
“`bash
使用 curl
curl -X DELETE ‘localhost:9200/products?pretty’
在 Kibana Dev Tools 中
DELETE /products
“`
这是一个危险的操作,执行后索引及其数据将无法恢复。
第六部分:搜索 – Elasticsearch 的核心能力
索引和存储数据是为了能够快速有效地搜索它们。搜索是 Elasticsearch 最强大的功能。你可以通过 _search
端点执行搜索。
有两种主要的搜索方式:
- URI Search (不常用,仅用于简单测试): 将查询参数直接放在 URL 的查询字符串中 (
?q=...
)。简单方便,但功能有限。 - Request Body Search (推荐): 将复杂的查询条件放在请求体中,使用 Elasticsearch 的查询领域特定语言 (Query DSL)。功能强大,支持各种复杂的查询、过滤、排序、分页和聚合。
1. URI Search (快速体验)
通过 ?q=
参数指定查询字符串。例如,搜索所有文档:
“`bash
使用 curl
curl -X GET ‘localhost:9200/products/_search?q=*&pretty’
在 Kibana Dev Tools 中
GET /products/_search?q=*
“`
搜索包含 “laptop” 关键词的文档(搜索所有字段):
“`bash
使用 curl
curl -X GET ‘localhost:9200/products/_search?q=laptop&pretty’
在 Kibana Dev Tools 中
GET /products/_search?q=laptop
“`
搜索 name
字段中包含 “laptop” 关键词的文档:
“`bash
使用 curl
curl -X GET ‘localhost:9200/products/_search?q=name:laptop&pretty’
在 Kibana Dev Tools 中
GET /products/_search?q=name:laptop
“`
响应会包含一个 hits
字段,其中有 total
(匹配到的文档总数) 和 hits
数组 (返回的文档列表,默认返回前 10 个)。每个命中文档包含元信息 (_index
, _id
, _score
– 相关度得分) 和 _source
(原始文档)。
2. Request Body Search (Query DSL)
这是执行搜索的标准和推荐方式。通过 POST /<index>/_search
或 GET /<index>/_search
请求,并将一个 JSON 对象作为请求体,该 JSON 对象描述了你的查询条件。
Query DSL 结构概览:
一个基本的搜索请求体通常包含以下顶级字段:
query
: 定义查询条件,决定哪些文档匹配,并计算每个文档的相关度得分 (_score
)。sort
: 定义结果的排序方式。from
: 分页起始位置(跳过的文档数)。size
: 返回的文档数量。_source
: 控制返回的文档是否包含_source
字段,或只包含指定字段。aggs
: 定义聚合,用于进行数据分析。
示例:匹配所有文档 (等同于 q=*
)
“`bash
使用 curl
curl -X POST ‘localhost:9200/products/_search?pretty’ -H ‘Content-Type: application/json’ -d ‘
{
“query”: {
“match_all”: {}
}
}
‘
在 Kibana Dev Tools 中
POST /products/_search
{
“query”: {
“match_all”: {}
}
}
“`
match_all
是一个查询类型,它匹配索引中的所有文档。
示例:全文搜索 (Match Query)
搜索 description
字段中包含 “powerful laptop” 的文档。match
查询会根据字段的映射进行分词处理。
“`bash
使用 curl
curl -X POST ‘localhost:9200/products/_search?pretty’ -H ‘Content-Type: application/json’ -d ‘
{
“query”: {
“match”: {
“description”: “powerful laptop”
}
}
}
‘
在 Kibana Dev Tools 中
POST /products/_search
{
“query”: {
“match”: {
“description”: “powerful laptop”
}
}
}
“`
示例:精确匹配 (Term Query)
搜索 category
字段精确等于 “Electronics” 的文档。term
查询用于精确匹配,不会对查询词进行分词。通常用于 keyword
或精确值类型的字段。
“`bash
使用 curl
curl -X POST ‘localhost:9200/products/_search?pretty’ -H ‘Content-Type: application/json’ -d ‘
{
“query”: {
“term”: {
“category”: “Electronics”
}
}
}
‘
在 Kibana Dev Tools 中
POST /products/_search
{
“query”: {
“term”: {
“category”: “Electronics”
}
}
}
“`
示例:范围查询 (Range Query)
搜索 price
字段在 1000 到 1500 之间的文档(包含边界)。
“`bash
使用 curl
curl -X POST ‘localhost:9200/products/_search?pretty’ -H ‘Content-Type: application/json’ -d ‘
{
“query”: {
“range”: {
“price”: {
“gte”: 1000,
“lte”: 1500
}
}
}
}
‘
在 Kibana Dev Tools 中
POST /products/_search
{
“query”: {
“range”: {
“price”: {
“gte”: 1000,
“lte”: 1500
}
}
}
}
“`
gte
: 大于等于 (Greater Than or Equal to)gt
: 大于 (Greater Than)lte
: 小于等于 (Less Than or Equal to)lt
: 小于 (Less Than)
示例:组合查询 (Bool Query)
使用 bool
查询可以组合多个查询子句(must
, filter
, should
, must_not
)。
must
: 子句必须匹配,会影响相关度得分。filter
: 子句必须匹配,但不计算相关度得分(常用于过滤,性能更好)。should
: 子句应该匹配,会影响相关度得分。must_not
: 子句不能匹配,不计算相关度得分。
搜索 category 为 “Electronics” 且 description 包含 “gaming” 的文档:
“`bash
使用 curl
curl -X POST ‘localhost:9200/products/_search?pretty’ -H ‘Content-Type: application/json’ -d ‘
{
“query”: {
“bool”: {
“filter”: [ # 使用 filter 子句进行精确过滤,不影响得分
{ “term”: { “category”: “Electronics” } }
],
“must”: [ # 使用 must 子句进行全文搜索,影响得分
{ “match”: { “description”: “gaming” } }
]
}
}
}
‘
在 Kibana Dev Tools 中
POST /products/_search
{
“query”: {
“bool”: {
“filter”: [
{ “term”: { “category”: “Electronics” } }
],
“must”: [
{ “match”: { “description”: “gaming” } }
]
}
}
}
“`
3. 查询 (Query) vs 过滤 (Filter)
理解 Query context 和 Filter context 的区别非常重要:
- Query context: 用于决定文档是否匹配 以及 文档的相关度得分 (
_score
)。用于执行全文搜索或任何需要计算相关度的场景。 - Filter context: 只用于决定文档是否匹配(是或否),不 计算相关度得分。结果可以被缓存,因此通常比 Query context 更快,适合用于结构化数据的精确匹配、范围、存在性检查等过滤场景。
在 bool
查询中,must
和 should
子句在 Query context 中执行,而 filter
和 must_not
子句在 Filter context 中执行。
4. 排序 (Sorting)
默认情况下,搜索结果按相关度得分 (_score
) 降序排列。你可以通过 sort
字段改变排序方式。
按价格升序排列:
“`bash
使用 curl
curl -X POST ‘localhost:9200/products/_search?pretty’ -H ‘Content-Type: application/json’ -d ‘
{
“query”: {
“match_all”: {}
},
“sort”: [
{ “price”: { “order”: “asc” } }
]
}
‘
在 Kibana Dev Tools 中
POST /products/_search
{
“query”: {
“match_all”: {}
},
“sort”: [
{ “price”: { “order”: “asc” } }
]
}
“`
sort
是一个数组,可以指定多个排序规则。"asc"
表示升序,"desc"
表示降序。对文本字段排序时,通常需要将字段映射为 keyword
类型。
5. 分页 (Pagination)
通过 from
和 size
参数进行分页。默认是 from: 0
, size: 10
。
获取第二页结果(每页 10 个):
“`bash
使用 curl
curl -X POST ‘localhost:9200/products/_search?pretty’ -H ‘Content-Type: application/json’ -d ‘
{
“query”: {
“match_all”: {}
},
“from”: 10,
“size”: 10
}
‘
在 Kibana Dev Tools 中
POST /products/_search
{
“query”: {
“match_all”: {}
},
“from”: 10,
“size”: 10
}
“`
第七部分:映射 (Mapping) 简介
如前所述,映射定义了字段的数据类型以及如何索引它们。理解映射对于实现精确的搜索和分析至关重要。
1. 动态映射
当你索引第一个包含某个字段的文档时,如果该字段没有预定义的映射,Elasticsearch 会根据字段值的类型自动创建映射。
- JSON 字符串 -> 默认映射为
text
和keyword
(取决于版本和配置)或只为text
。 - JSON 数字 (整数/浮点数) -> 映射为相应的数字类型 (
long
,integer
,float
,double
等)。 - JSON 布尔值 -> 映射为
boolean
。 - JSON 日期格式字符串 -> 映射为
date
。 - JSON 对象 -> 映射为
object
。 - JSON 数组 -> 如果数组包含相同类型的值,映射为该类型的字段;如果包含不同类型,则映射为第一个值的类型,或者抛出错误(取决于版本)。
动态映射很方便,但可能不是最优的。例如,字符串字段默认映射为 text
会进行分词,如果你想对这个字段进行精确匹配或聚合,就需要额外的 keyword
子字段,这需要显式配置。
2. 显式映射
你可以手动创建或更新索引的映射,以精确控制字段的行为。通常在创建索引时就定义好映射。
示例:创建一个带有显式映射的索引
“`bash
使用 curl
curl -X PUT ‘localhost:9200/products_with_mapping?pretty’ -H ‘Content-Type: application/json’ -d ‘
{
“mappings”: {
“properties”: {
“name”: {
“type”: “text”,
“fields”: {
“keyword”: {
“type”: “keyword”,
“ignore_العطول”: 256 # ignore_above 设置,如果字段长度超过256,则不索引为keyword
}
}
},
“price”: { “type”: “float” },
“stock”: { “type”: “integer” },
“description”: { “type”: “text” },
“category”: { “type”: “keyword” }, # 将 category 直接映射为 keyword
“created_at”: { “type”: “date” }
}
},
“settings”: {
“index”: {
“number_of_shards”: 1, # 入门通常设置为 1 个主分片
“number_of_replicas”: 0 # 入门通常设置为 0 个副本,单节点无副本
}
}
}
‘
在 Kibana Dev Tools 中
PUT /products_with_mapping
{
“mappings”: {
“properties”: {
“name”: {
“type”: “text”,
“fields”: {
“keyword”: {
“type”: “keyword”,
“ignore_above”: 256
}
}
},
“price”: { “type”: “float” },
“stock”: { “type”: “integer” },
“description”: { “type”: “text” },
“category”: { “type”: “keyword” },
“created_at”: { “type”: “date” }
}
},
“settings”: {
“index”: {
“number_of_shards”: 1,
“number_of_replicas”: 0
}
}
}
“`
在这个例子中:
mappings
: 定义了索引的映射。properties
: 定义了文档中字段的映射。name
: 定义为text
类型,用于全文搜索。同时通过fields
定义了一个名为keyword
的子字段,类型为keyword
,用于对name
字段进行精确匹配或聚合。访问时使用name.keyword
。price
,stock
,created_at
: 定义为适当的数值和日期类型。category
: 直接定义为keyword
类型,因为我们可能需要按分类精确过滤或统计。settings
: 定义了索引的设置,包括分片和副本数量。
定义显式映射可以确保数据被正确索引,从而支持你所需的搜索和分析功能。例如,如果你需要对某个字符串字段进行精确过滤或分组统计,必须将其映射为 keyword
类型。
3. 获取索引映射
你可以使用 GET /<index>/_mapping
来查看索引的映射。
“`bash
使用 curl
curl -X GET ‘localhost:9200/products_with_mapping/_mapping?pretty’
在 Kibana Dev Tools 中
GET /products_with_mapping/_mapping
“`
第八部分:一个简单的综合用例
让我们将之前学到的知识整合起来,模拟一个简单的产品搜索和分析场景。
假设我们要创建一个存储电商产品信息的索引,并支持按关键词搜索产品名称和描述,按分类过滤,并统计不同分类的产品数量。
步骤 1: 创建带有显式映射的索引
我们使用上面创建 products_with_mapping
索引的映射,确保 name
有 keyword
子字段,category
是 keyword
。
“`bash
在 Kibana Dev Tools 中执行 (或者使用 curl)
PUT /my_products
{
“mappings”: {
“properties”: {
“name”: {
“type”: “text”,
“fields”: {
“keyword”: {
“type”: “keyword”,
“ignore_above”: 256
}
}
},
“price”: { “type”: “float” },
“stock”: { “type”: “integer” },
“description”: { “type”: “text” },
“category”: { “type”: “keyword” },
“created_at”: { “type”: “date” }
}
},
“settings”: {
“index”: {
“number_of_shards”: 1,
“number_of_replicas”: 0
}
}
}
“`
步骤 2: 索引一些产品文档
添加几个不同分类的产品。
“`bash
在 Kibana Dev Tools 中执行多次 POST 请求
POST /my_products/_doc
{
“name”: “Laptop Pro”,
“price”: 1500,
“stock”: 30,
“description”: “High-performance laptop for professionals.”,
“category”: “Electronics”,
“created_at”: “2023-03-15T10:00:00Z”
}
POST /my_products/_doc
{
“name”: “Desk Chair”,
“price”: 250,
“stock”: 150,
“description”: “Ergonomic office chair with adjustable height.”,
“category”: “Furniture”,
“created_at”: “2023-03-15T11:00:00Z”
}
POST /my_products/_doc
{
“name”: “Gaming Mouse”,
“price”: 75,
“stock”: 80,
“description”: “High-precision mouse for gaming.”,
“category”: “Electronics”,
“created_at”: “2023-03-15T12:00:00Z”
}
POST /my_products/_doc
{
“name”: “Monitor”,
“price”: 300,
“stock”: 100,
“description”: “27-inch 4K monitor.”,
“category”: “Electronics”,
“created_at”: “2023-03-15T13:00:00Z”
}
POST /my_products/_doc
{
“name”: “Office Desk”,
“price”: 400,
“stock”: 70,
“description”: “Modern office desk with storage.”,
“category”: “Furniture”,
“created_at”: “2023-03-15T14:00:00Z”
}
“`
步骤 3: 执行搜索
搜索包含 “laptop” 或 “gaming” 的产品:
bash
POST /my_products/_search
{
"query": {
"match": {
"description": "laptop gaming"
}
}
}
这将使用 description
字段的 text
映射进行全文搜索。
搜索 “Electronics” 分类下的产品:
bash
POST /my_products/_search
{
"query": {
"term": {
"category": "Electronics"
}
}
}
这将使用 category
字段的 keyword
映射进行精确过滤。
搜索名称或描述包含 “office” 且分类为 “Furniture” 的产品:
使用 bool
查询结合 should
和 filter
。
bash
POST /my_products/_search
{
"query": {
"bool": {
"filter": [
{ "term": { "category": "Furniture" } } # 精确过滤分类
],
"should": [ # 匹配名称或描述之一即可
{ "match": { "name": "office" } },
{ "match": { "description": "office" } }
],
"minimum_should_match": 1 # 至少匹配一个 should 子句
}
}
}
步骤 4: 执行聚合分析
统计不同分类的产品数量:
bash
POST /my_products/_search
{
"size": 0, # 不返回搜索结果,只返回聚合结果
"aggs": {
"products_by_category": { # 聚合名称
"terms": { # 聚合类型:按词条(值)分组
"field": "category" # 按 category 字段聚合 (需要 keyword 类型)
}
}
}
}
响应中的 aggregations
字段将包含按 category
字段分组的结果,显示每个分类及其文档计数。
json
...
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : null, # size=0 时 max_score 为 null
"hits" : [ ] # size=0 时 hits 数组为空
},
"aggregations" : {
"products_by_category" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "Electronics",
"doc_count" : 3
},
{
"key" : "Furniture",
"doc_count" : 2
}
]
}
}
...
这个简单的例子展示了 Elasticsearch 在搜索和基本分析方面的能力。聚合是 Elasticsearch 另一个非常强大的功能,可以执行更复杂的统计计算(如求平均值、最大值、最小值)、嵌套聚合、地理位置聚合等等。
第九部分:超越基础 – 接下来学习什么?
本指南仅仅触及了 Elasticsearch 的皮毛。要更深入地掌握 Elasticsearch,你可以继续学习以下主题:
- 更复杂的查询和过滤: Full-text queries (phrase, match_phrase), Compound queries (constant_score), Geo queries, etc.
- 分析器 (Analyzers): 深入了解文本如何被分词和索引,以及如何定制分析器来满足特定语言或业务需求。
- 聚合 (Aggregations): 学习更高级的聚合类型,如 metrics aggregations (sum, avg, min, max), bucket aggregations (date_histogram, range), parent/child aggregations, etc.
- 集群管理: 了解如何设置多节点集群、主节点选举、分片分配、故障转移等。
- 索引生命周期管理 (ILM): 如何自动管理索引的生命周期,如按时间轮转索引、将旧数据迁移到更便宜的存储、最终删除。
- 快照和恢复: 如何备份和恢复你的 Elasticsearch 数据。
- 性能调优: 如何优化索引和搜索性能,包括 JVM 设置、分片策略、缓存、查询优化等。
- Elastic Stack 的其他组件: 学习 Kibana (可视化和监控)、Logstash (数据收集和转换) 和 Beats (轻量级数据采集器),构建完整的数据处理管线。
- 安全性: 在生产环境中如何保护你的 Elasticsearch 集群,包括用户认证、权限控制、加密等。
总结
恭喜你阅读完这篇详细的 Elasticsearch 入门指南!
我们从认识 Elasticsearch 是什么、为什么选择它开始,深入理解了索引、文档、映射、分片等核心概念。接着,我们学习了如何在本地安装并启动 Elasticsearch,并通过 RESTful API 进行了基本的增删改查操作。最后,我们重点探讨了 Elasticsearch 的核心功能——搜索,学习了如何使用 Query DSL 进行各种复杂的搜索和过滤,并通过一个简单的用例展示了搜索和聚合的结合应用。
Elasticsearch 是一个功能强大且复杂的系统,入门只是第一步。最重要的是动手实践,通过实际操作来加深理解。尝试索引不同类型的数据,运行各种搜索和聚合查询,观察结果,理解不同 API 的作用。
希望这篇指南能帮助你顺利迈出学习 Elasticsearch 的第一步。祝你在数据搜索和分析的旅程中取得成功!