Elasticsearch 快速入门教程:从零开始探索强大的搜索引擎
前言
在当今爆炸式增长的数据时代,如何高效地存储、搜索和分析海量数据成为了一个核心挑战。无论是网站搜索、日志分析、监控告警,还是实时数据可视化,传统的数据库系统往往难以满足需求。正是在这样的背景下,Elasticsearch 应运而生,并迅速成为了分布式搜索和分析领域的领导者。
Elasticsearch(简称ES)是一个高度可扩展的开源全文本搜索和分析引擎。它基于 Apache Lucene 构建,隐藏了 Lucene 的复杂性,提供了一个简单易用的 RESTful API 接口。凭借其出色的速度、可伸缩性和灵活性,Elasticsearch 被广泛应用于各种场景。
本篇教程旨在帮助完全没有Elasticsearch经验的初学者,快速掌握其核心概念、安装部署以及基本操作。通过阅读本文,你将能够理解Elasticsearch的工作原理,搭建自己的本地环境,并进行文档的索引、检索、更新和删除等基本操作。
由于篇幅和入门定位的限制,本文将聚焦于Elasticsearch的核心基础,对于集群管理、性能优化、安全配置等高级主题将仅作简要提及或留待后续深入学习。
预计学习目标:
- 理解 Elasticsearch 是什么以及它为什么如此流行。
- 掌握 Elasticsearch 的核心概念(文档、索引、分片、副本等)。
- 学会在本地安装和启动 Elasticsearch。
- 学会使用 Kibana 的 Dev Tools 或 cURL 与 Elasticsearch 进行交互。
- 掌握文档的索引(添加)、获取、更新和删除等基本操作。
- 学习如何执行简单的搜索查询。
- 了解 Elasticsearch 的基本架构和优势。
让我们开始Elasticsearch的探索之旅吧!
第一章:Elasticsearch 核心概念解析
在深入实践之前,理解Elasticsearch的一些核心概念至关重要。这些概念构成了Elasticsearch的基础,有助于你更好地理解后续的操作。
1. 文档 (Document)
文档是 Elasticsearch 中最小的数据单元。你可以把它想象成关系型数据库中的一行记录,或者一个JSON对象。在 Elasticsearch 中,数据以JSON格式存储和传输。
一个文档可以包含多种类型的数据,如字符串、数字、布尔值、日期、嵌套对象等。每个文档都有一个唯一的ID(可以在索引时指定,或由Elasticsearch自动生成)以及它所属的索引。
示例文档 (JSON):
json
{
"title": "Elasticsearch Quick Start Guide",
"author": "Your Name",
"publish_date": "2023-10-27",
"tags": ["elasticsearch", "tutorial", "search"],
"content": "This article helps you quickly get started with Elasticsearch...",
"views": 150
}
2. 索引 (Index)
索引是 Elasticsearch 中存储相似文档的逻辑集合。你可以把它类比为关系型数据库中的一个“数据库”或“表”(尽管这种类比不完全准确,但有助于理解)。
一个索引定义了其中文档的映射 (Mapping),即文档中各个字段的数据类型、如何被分析(用于全文搜索)以及如何存储等信息。Elasticsearch 是一个Schema-less 的系统,这意味着你在索引文档时,如果没有预先定义 Mapping,Elasticsearch 会尝试根据文档内容自动创建 Mapping (Dynamic Mapping)。但这在实际应用中通常需要手动精确控制 Mapping,以确保搜索和分析的准确性。
重要特性:
- 逻辑隔离: 不同索引之间的数据是相互独立的。
- 可扩展性: 一个索引可以分布在多个节点上。
- 优化: 索引级别的设置(如分片数、副本数)对性能和可用性至关重要。
3. 类型 (Type) – 已废弃
在 Elasticsearch 7.0.0 版本之前,一个索引可以包含多个类型 (Type)。类型类似于关系型数据库中“表”的概念,用于区分索引中不同结构的文档。例如,一个博客索引可能包含 “post” 类型和 “comment” 类型。
重要提示: 从 Elasticsearch 7.0.0 开始,类型已被移除。 一个索引只能包含一种类型的文档。现有的应用在升级到新版本时需要进行数据迁移和结构调整。在本教程中,我们将遵循最新版本的约定,不再使用 Type。
4. 映射 (Mapping)
映射定义了索引中文档的结构和字段的数据类型,以及这些字段如何被索引和搜索。它是实现精确搜索和分析的基础。
核心作用:
- 数据类型: 明确每个字段的数据类型(如
text
,keyword
,long
,double
,boolean
,date
,object
,nested
等)。 - 分析器 (Analyzer): 对于文本字段,指定如何进行分词、标准化(如转小写、去除停用词)等处理,以实现全文搜索。
text
字段会被分析,而keyword
字段不会被分析,适合用于精确匹配、排序和聚合。 - 索引选项: 控制字段是否可被搜索、是否存储原始值等。
你可以手动创建带有特定 Mapping 的索引,也可以让 Elasticsearch 根据第一个文档的内容自动生成 Mapping。手动定义 Mapping 通常是更好的做法,特别是对于文本字段的分析器选择。
5. 字段 (Field)
文档中的每个键值对就是一个字段。例如,在上面的文档示例中,title
, author
, publish_date
等都是字段。
6. 集群 (Cluster)
一个集群由一个或多个 Elasticsearch 节点组成,它们共同存储所有的数据,并提供跨所有节点的索引和搜索能力。集群提供了分布式、高可用和可伸缩性。
7. 节点 (Node)
节点是运行 Elasticsearch 实例的服务器。一个节点是集群的一部分,存储数据,参与集群的索引和搜索功能。节点可以配置为不同的角色(如 Master 节点负责集群管理,Data 节点负责数据存储和搜索,Ingest 节点负责数据预处理等)。在快速入门时,通常从单个节点开始。
8. 分片 (Shard)
索引可以被水平分割成多个分片 (Shard)。每个分片都是一个独立的 Lucene 索引实例,包含索引数据的一部分。
为什么需要分片?
- 解决单个节点存储限制: 单个节点的磁盘容量、内存或CPU能力可能不足以处理大规模数据。通过分片,可以将一个大索引分布到多个节点上。
- 提高吞吐量: 分片允许在多个节点上并行执行索引和搜索操作,从而提高性能。
创建索引时,你可以指定主分片的数量 (number_of_shards
)。一旦索引创建,主分片数量不能修改。
9. 副本 (Replica)
副本是主分片的复制。每个主分片可以有零个或多个副本。副本也存储在不同的节点上(通常是不同于主分片所在的节点)。
为什么需要副本?
- 高可用性: 如果主分片所在的节点宕机,副本分片可以提升为新的主分片,确保数据不丢失和服务的连续性。
- 提高搜索吞吐量: 搜索请求可以由主分片或其任何副本处理,从而分担负载,提高搜索性能。
创建索引时,你可以指定副本分片的数量 (number_of_replicas
)。副本数量可以在索引创建后动态调整。
一个健康的 Elasticsearch 集群通常至少需要两个节点(以放置主分片和副本分片),并为每个主分片配置至少一个副本(number_of_replicas: 1
),以实现高可用。
概念总结关系图:
Cluster (集群)
└── Nodes (节点)
└── Indices (索引)
└── Shards (分片 - Primary 主要用于索引,Replica 用于高可用和搜索)
└── Documents (文档 - JSON 格式)
└── Fields (字段)
理解了这些核心概念,我们就具备了进行下一步实践的基础。
第二章:安装与启动 Elasticsearch 和 Kibana
开始使用 Elasticsearch 的第一步是在你的本地环境搭建起一个可用的实例。最简单的方式是直接下载并运行官方提供的二进制文件。我们还会同时安装 Kibana,它是 Elasticsearch 的官方可视化工具,对于开发、管理和查询非常方便。
1. 环境要求
- Java Development Kit (JDK): Elasticsearch 需要 Java 运行环境。请确保你的系统安装了 OpenJDK 或 Oracle JDK 11 或更高版本。可以从 Adoptium (Temurin) 或 Oracle 官网下载。安装后,设置好
JAVA_HOME
环境变量。 - 足够的内存和磁盘空间: 即使是入门,也建议分配至少 2GB 内存给 Elasticsearch,并确保有足够的磁盘空间来存储数据。
2. 下载 Elasticsearch 和 Kibana
访问 Elastic 官方网站下载页面:
* Elasticsearch 下载:https://www.elastic.co/cn/downloads/elasticsearch
* Kibana 下载:https://www.elastic.co/cn/downloads/kibana
请确保下载相同版本的 Elasticsearch 和 Kibana。选择适合你操作系统的版本(例如 .tar.gz
for Linux/macOS, .zip
for Windows)。
3. 安装步骤 (以 Linux/macOS 为例)
步骤 1:解压文件
将下载的 .tar.gz
文件解压到你选择的目录。
bash
tar -zxvf elasticsearch-x.y.z-linux-x86_64.tar.gz
tar -zxvf kibana-x.y.z-linux-x86_64.tar.gz
将 x.y.z
替换为你下载的实际版本号。
步骤 2:配置 Elasticsearch (可选但推荐)
进入解压后的 Elasticsearch 目录,找到 config
文件夹,编辑 elasticsearch.yml
文件。
对于本地单节点开发,通常不需要进行复杂的配置。但为了避免可能的网络绑定问题,可以修改 network.host
参数:
“`yaml
修改 config/elasticsearch.yml
network.host: 127.0.0.1
“`
这会将 Elasticsearch 绑定到本地回环地址。
步骤 3:启动 Elasticsearch
进入 Elasticsearch 目录,运行启动脚本:
“`bash
在 Elasticsearch 目录下执行
bin/elasticsearch
“`
Windows 用户运行 bin\elasticsearch.bat
。
Elasticsearch 会启动并输出大量日志。看到类似 [INFO ... ] cluster.routing.allocation - Cluster health status changed from [RED] to [YELLOW]
或 [INFO ... ] cluster.routing.allocation - Cluster health status changed from [YELLOW] to [GREEN]
的日志,表示 Elasticsearch 已经成功启动并加入了一个单节点集群。
默认情况下,Elasticsearch 运行在 http://localhost:9200
。你可以在浏览器中访问这个地址,如果看到一个包含版本信息和集群名称的 JSON 响应,说明 Elasticsearch 正在运行。
json
{
"name" : "your_node_name",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "...",
"version" : {
"number" : "x.y.z",
"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"
}
步骤 4:配置 Kibana
进入解压后的 Kibana 目录,找到 config
文件夹,编辑 kibana.yml
文件。
你需要告诉 Kibana 去连接哪个 Elasticsearch 实例。默认配置通常是连接本地的 Elasticsearch,但最好确认一下:
“`yaml
修改 config/kibana.yml
elasticsearch.hosts: [“http://localhost:9200”]
“`
步骤 5:启动 Kibana
进入 Kibana 目录,运行启动脚本:
“`bash
在 Kibana 目录下执行
bin/kibana
“`
Windows 用户运行 bin\kibana.bat
。
Kibana 也会输出日志。看到类似 Server running at http://localhost:5601
的日志,表示 Kibana 已经成功启动。
默认情况下,Kibana 运行在 http://localhost:5601
。在浏览器中访问这个地址,你应该能看到 Kibana 的登录页面(如果开启了安全认证)或主界面。
4. 使用 Kibana Dev Tools
Kibana Dev Tools 是一个非常实用的工具,它提供了一个控制台界面,可以直接输入和执行 Elasticsearch REST API 请求。这比使用 cURL 命令要方便得多。
在 Kibana 界面中,通常可以在左侧导航栏找到 “Management” -> “Dev Tools”。点击进入后,你会看到一个左边用于输入请求,右边用于显示响应的界面。
接下来的基本操作都将通过 Kibana Dev Tools 来演示。左侧输入框中输入请求,然后点击请求旁边的绿色播放按钮 (▶
) 或按 Ctrl/Cmd + Enter
执行。
基本请求格式:
http
METHOD /path/to/api
{
"request_body": "..."
}
例如,获取集群健康状态:
http
GET /_cat/health?v
在左侧输入框输入这行,点击执行,右侧会显示集群的健康信息。
第三章:基本操作:索引、获取、更新、删除文档
现在 Elasticsearch 和 Kibana 都已经运行起来了,我们可以开始进行一些基本的数据操作了。这些操作主要通过 RESTful API 来完成。
1. 查看集群健康状态 (_cat/health
)
这是一个常用的 API,用于快速了解集群的健康状况。
请求:
http
GET /_cat/health?v
响应示例:
epoch timestamp cluster status index docs.count docs.deleted active_shards relocating_shards active_primary_shards initializing_shards unassigned_shards pending_tasks max_task_wait_time active_gil_lock duration time_millis
1678886400 10:00:00 elasticsearch green 0 0 0 0 0 0 0 0 0 -1 0b -1 0
status 字段的含义:
green
: 所有主分片和副本分片都可用,集群健康。yellow
: 所有主分片都可用,但至少有一个副本分片不可用。数据仍然完整,但高可用性受影响。red
: 至少有一个主分片不可用。部分数据可能无法访问。
在单节点入门环境下,如果看到 yellow
是正常的,因为副本分片无法分配到其他节点上。如果有多个节点且配置了副本,通常会看到 green
。
2. 查看所有索引 (_cat/indices
)
这个 API 可以列出当前集群中的所有索引及其基本信息。
请求:
http
GET /_cat/indices?v
响应示例:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
green open .kibana_task_manager_x... 1 1 2 0 23.3kb 11.6kb
green open .kibana_x... 1 1 3 0 42.1kb 21kb
.kibana
开头的索引是 Kibana 自己创建和使用的,可以忽略它们。
3. 索引文档 (Index Document)
索引文档就是向 Elasticsearch 中添加数据。你可以指定文档的 ID,也可以让 Elasticsearch 自动生成。
方法一:使用 PUT 指定文档 ID
如果你有数据的唯一标识符,可以使用 PUT
方法。如果 ID 已经存在,会替换掉整个旧文档;如果不存在,则会创建新文档。
请求格式:
http
PUT /<index_name>/_doc/<document_id>
{
"field1": "value1",
"field2": "value2",
...
}
示例:索引一个产品文档到名为 products
的索引,ID 为 1
http
PUT /products/_doc/1
{
"name": "Laptop",
"brand": "Dell",
"price": 1200.50,
"in_stock": true,
"tags": ["electronics", "computer"]
}
响应示例:
json
{
"_index": "products",
"_id": "1",
"_version": 1,
"result": "created", // 或 "updated" 如果是替换旧文档
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
方法二:使用 POST 让 Elasticsearch 自动生成 ID
如果你没有数据的唯一标识符,可以使用 POST
方法。Elasticsearch 会自动生成一个唯一的 ID。
请求格式:
http
POST /<index_name>/_doc
{
"field1": "value1",
"field2": "value2",
...
}
示例:索引一个订单文档到名为 orders
的索引,自动生成 ID
http
POST /orders/_doc
{
"order_id": "ORD12345",
"customer_name": "Alice",
"amount": 250.75,
"order_date": "2023-10-27T10:30:00Z"
}
响应示例:
json
{
"_index": "orders",
"_id": "AbCdefGhIjKlMnOqrSTu", // 自动生成的 ID
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
注意:在单节点默认配置下,_shards.total
是主分片数 + 副本分片数 (1主 + 1副 = 2)。successful
是主分片和副本分片都成功写入的数量。
4. 获取文档 (Get Document)
通过文档的 ID 可以获取完整的文档内容。
请求格式:
http
GET /<index_name>/_doc/<document_id>
示例:获取之前索引的产品文档 (ID: 1)
http
GET /products/_doc/1
响应示例:
json
{
"_index": "products",
"_id": "1",
"_version": 1,
"_seq_no": 0,
"_primary_term": 1,
"found": true, // 表示找到了文档
"_source": { // 原始文档内容
"name": "Laptop",
"brand": "Dell",
"price": 1200.50,
"in_stock": true,
"tags": ["electronics", "computer"]
}
}
如果文档不存在,found
字段会是 false
,并且没有 _source
字段。
5. 更新文档 (Update Document)
Elasticsearch 中的更新操作实际上是先删除旧文档,再索引一个新文档。但 Elasticsearch 提供了两种更便捷的更新方式:
方法一:全量替换 (使用 PUT)
使用 PUT
方法索引文档时,如果指定了已存在的 ID,会用新的文档完全替换旧文档。
示例:修改产品文档 (ID: 1) 的价格
http
PUT /products/_doc/1
{
"name": "Laptop",
"brand": "Dell",
"price": 1150.00, // 价格变了
"in_stock": true,
"tags": ["electronics", "computer"],
"description": "Powerful laptop for work and play." // 新增字段
}
响应示例:
json
{
"_index": "products",
"_id": "1",
"_version": 2, // 版本号增加
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 1, // 序列号增加
"_primary_term": 1
}
可以看到 _version
增加到了 2,result
是 updated
。
方法二:局部更新 (使用 POST + _update
endpoint)
通常我们只需要修改文档的某个字段,而不是替换整个文档。这时应该使用 _update
接口。
请求格式:
http
POST /<index_name>/_update/<document_id>
{
"doc": {
"field_to_update1": "new_value1",
"field_to_update2": "new_value2",
...
}
}
示例:再次修改产品文档 (ID: 1) 的价格,并将其设置为缺货
http
POST /products/_update/1
{
"doc": {
"price": 1100.00,
"in_stock": false
}
}
响应示例:
json
{
"_index": "products",
"_id": "1",
"_version": 3, // 版本号再次增加
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1
}
这种方式只会修改 doc
字段下的内容,而不会影响文档的其他字段。
6. 删除文档 (Delete Document)
通过文档的 ID 可以删除文档。
请求格式:
http
DELETE /<index_name>/_doc/<document_id>
示例:删除产品文档 (ID: 1)
http
DELETE /products/_doc/1
响应示例:
json
{
"_index": "products",
"_id": "1",
"_version": 4, // 删除操作也会增加版本号
"result": "deleted",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 3,
"_primary_term": 1
}
再次尝试获取该文档,会返回 found: false
。
7. 删除索引 (Delete Index)
删除整个索引会删除该索引下的所有文档和 Mapping 设置。
请求格式:
http
DELETE /<index_name>
示例:删除 products
索引
http
DELETE /products
响应示例:
json
{
"acknowledged": true // 表示请求已被集群确认
}
再次查看所有索引 (GET /_cat/indices?v
),products
索引将不再列表中。
第四章:基本搜索查询 (Search)
Elasticsearch 最强大的功能在于其灵活且高性能的搜索能力。搜索通过 _search
endpoint 进行,请求体中使用 Elasticsearch 特有的 Query DSL (Domain Specific Language) 来定义查询条件。Query DSL 是一个基于 JSON 的语言。
1. 查询所有文档
最简单的搜索请求是查询一个索引中的所有文档。
请求格式:
http
GET /<index_name>/_search
示例:查询 orders
索引中的所有文档
假设我们之前创建了一个 orders
索引并添加了一些文档。
http
GET /orders/_search
响应示例 (部分):
json
{
"took": 3, // 查询花费的毫秒数
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": { // 搜索结果的核心部分
"total": { // 总命中数
"value": 10000,
"relation": "gte" // 表示实际命中数可能大于等于 10000 (默认限制)
},
"max_score": 1.0, // 最高相关度分数
"hits": [ // 具体命中的文档列表
{
"_index": "orders",
"_id": "AbCdefGhIjKlMnOqrSTu",
"_version": 1,
"_score": 1.0, // 相关度分数
"_source": { // 原始文档内容
"order_id": "ORD12345",
"customer_name": "Alice",
"amount": 250.75,
"order_date": "2023-10-27T10:30:00Z"
}
},
// ... 更多文档
]
}
}
注意 hits
部分,total
表示匹配到的文档总数,hits
数组包含了返回的文档详情。默认情况下,返回前 10 条文档。
2. 基本查询参数
可以在 _search
请求体中添加参数来控制查询行为,最常见的是 query
参数。
请求格式 (带 query):
http
GET /<index_name>/_search
{
"query": {
// Query DSL 定义查询条件
}
}
3. 常用查询类型
Query DSL 提供了各种查询类型来满足不同的搜索需求。
a. 匹配查询 (Match Query)
用于执行全文搜索。它会分析查询文本,然后匹配文档中被分析的字段。
请求格式:
http
GET /<index_name>/_search
{
"query": {
"match": {
"<field_name>": "<query_text>"
}
}
}
示例:在 orders
索引中搜索 customer_name
包含 “Alice” 的订单
http
GET /orders/_search
{
"query": {
"match": {
"customer_name": "Alice"
}
}
}
如果 customer_name
字段是 text
类型,match
查询会对其进行分词处理。
示例:在 products
索引中搜索 description
包含 “Powerful laptop” 的产品
如果之前创建了带有 description
字段的 products
索引。
http
GET /products/_search
{
"query": {
"match": {
"description": "Powerful laptop"
}
}
}
b. 词条查询 (Term Query)
用于精确匹配字段中的一个未分析的词条 (term)。通常用于 keyword
字段或者不进行分词处理的字段(如数字、日期、布尔值)。它不会对查询文本进行分词处理。
重要提示: 对于 text
字段,Elasticsearch 会创建一个子字段(通常命名为 .keyword
),它是该字段的未分析版本。使用 term
查询 text
字段时,通常应该查询其 .keyword
子字段。
请求格式:
http
GET /<index_name>/_search
{
"query": {
"term": {
"<field_name>": "<exact_value>"
}
}
}
示例:在 products
索引中搜索 brand
字段精确等于 “Dell” 的产品
假设 brand
字段是 keyword
类型。
http
GET /products/_search
{
"query": {
"term": {
"brand": "Dell"
}
}
}
示例:在 products
索引中搜索 in_stock
字段等于 true
的产品
假设 in_stock
字段是 boolean
类型。
http
GET /products/_search
{
"query": {
"term": {
"in_stock": true
}
}
}
区别 match
vs term
: match
用于全文搜索(对查询和文档字段都分词后匹配),term
用于精确匹配(不对查询分词,直接匹配文档中未分析的词条)。这是初学者常遇到的一个难点,理解字段的 Mapping(特别是 text
和 keyword
)以及查询类型的适用场景非常重要。
c. 范围查询 (Range Query)
用于查找字段值在指定范围内的文档。常用于数字和日期字段。
请求格式:
http
GET /<index_name>/_search
{
"query": {
"range": {
"<field_name>": {
"gte": <min_value>, // 大于等于
"gt": <min_value>, // 大于
"lte": <max_value>, // 小于等于
"lt": <max_value> // 小于
}
}
}
}
示例:在 products
索引中搜索价格 (price
) 在 1000 到 1500 之间的产品 (包含边界)
http
GET /products/_search
{
"query": {
"range": {
"price": {
"gte": 1000,
"lte": 1500
}
}
}
}
示例:在 orders
索引中搜索 2023 年 10 月份的订单
假设 order_date
是 date
类型。
http
GET /orders/_search
{
"query": {
"range": {
"order_date": {
"gte": "2023-10-01T00:00:00Z",
"lt": "2023-11-01T00:00:00Z"
}
}
}
}
4. 组合查询 (Boolean Query)
布尔查询 (Boolean Query) 允许你使用布尔逻辑 (AND
, OR
, NOT
) 组合多个查询子句。这是最常用和强大的查询类型之一。
布尔查询包含以下子句:
must
: 文档必须匹配此子句,相当于 AND。filter
: 文档必须匹配此子句,但子句会以非计分模式 (non-scoring mode) 执行。用于过滤结果而不影响相关度分数。常用于结构化搜索(如范围、Term、ID 查询)。也相当于 AND,但性能通常优于must
用于过滤场景。should
: 文档应该匹配此子句。如果文档匹配must
或filter
子句,则should
子句的匹配会提高文档的相关度分数。如果文档不匹配任何must
或filter
子句,那么至少需要匹配一个should
子句(默认情况下)才能被返回,相当于 OR。must_not
: 文档不能匹配此子句,相当于 NOT。以非计分模式执行。
请求格式:
http
GET /<index_name>/_search
{
"query": {
"bool": {
"must": [
// 匹配子句列表 (AND)
],
"filter": [
// 过滤子句列表 (AND, 非计分)
],
"should": [
// 推荐子句列表 (OR, 影响分数)
],
"must_not": [
// 排除子句列表 (NOT, 非计分)
]
}
}
}
示例:在 products
索引中搜索品牌是 “Dell” 或 “HP”,价格在 1000 到 2000 之间,并且描述中包含 “laptop” 但不包含 “gaming” 的产品
http
GET /products/_search
{
"query": {
"bool": {
"should": [
{ "term": { "brand.keyword": "Dell" } },
{ "term": { "brand.keyword": "HP" } }
],
"minimum_should_match": 1, // 至少匹配一个 should 子句
"filter": [
{
"range": {
"price": {
"gte": 1000,
"lte": 2000
}
}
}
],
"must": [
{ "match": { "description": "laptop" } }
],
"must_not": [
{ "match": { "description": "gaming" } }
]
}
}
}
这个示例结合了多种查询类型和布尔逻辑,展示了 Query DSL 的灵活性。注意 brand.keyword
的使用,假设 brand
字段的 Mapping 中有一个 keyword
子字段。minimum_should_match
参数指定 should
子句中至少需要匹配多少个子句才返回文档。
5. 分页与排序
- 分页: 使用
from
和size
参数控制返回结果的起始位置和数量。
http
GET /<index_name>/_search
{
"query": { ... },
"from": 10, // 跳过前 10 条
"size": 20 // 返回 20 条
} - 排序: 使用
sort
参数指定排序字段和排序方式 (asc
升序,desc
降序)。
http
GET /<index_name>/_search
{
"query": { ... },
"sort": [
{ "price": "asc" }, // 按价格升序
{ "order_date": "desc" } // 再按日期降序
]
}
注意:对text
字段进行排序通常需要在其keyword
子字段上进行。
6. 指定返回字段 (_source)
默认情况下,搜索结果会返回整个原始文档 (_source
)。你可以通过 _source
参数来指定只返回文档中的部分字段,这在文档很大时可以节省带宽和处理时间。
http
GET /<index_name>/_search
{
"query": { ... },
"_source": ["field1", "field3"] // 只返回 field1 和 field3
}
或者排除某些字段:
http
GET /<index_name>/_search
{
"query": { ... },
"_source": {
"excludes": ["sensitive_field"] // 排除 sensitive_field
}
}
第五章:更进一步:Mapping 和 Aggregations (简介)
1. 理解并控制 Mapping
前面提到,Mapping 定义了字段的数据类型和索引方式。手动创建和管理 Mapping 非常重要,特别是为了精确控制文本分析和字段类型。
示例:手动创建索引并定义 Mapping
创建一个名为 blogs
的索引,并定义字段类型:
http
PUT /blogs
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"title": {
"type": "text" // 用于全文搜索
},
"author": {
"type": "keyword" // 用于精确匹配、排序、聚合
},
"publish_date": {
"type": "date" // 日期类型
},
"tags": {
"type": "keyword" // 标签列表,每个标签都可精确匹配
},
"views": {
"type": "long" // 数字类型
},
"content": {
"type": "text",
"analyzer": "standard" // 指定标准分词器 (默认)
}
}
}
}
获取现有索引的 Mapping:
http
GET /<index_name>/_mapping
了解并设置正确的 Mapping 是构建高效搜索和分析的基础。
2. 聚合 (Aggregations)
聚合是 Elasticsearch 的另一个强大功能,它允许你对搜索结果进行分组、计算统计信息(如总和、平均值、最大/最小值)、创建直方图等。这使得 Elasticsearch 不仅是搜索引擎,也是一个强大的分析引擎。
聚合与查询是分开的,可以在同一个请求中同时执行。
示例:按品牌统计产品数量
http
GET /products/_search
{
"size": 0, // 不返回搜索结果,只返回聚合结果
"aggs": { // aggregations 的缩写
"products_by_brand": { // 聚合名称
"terms": { // 按词条(这里是品牌)分组
"field": "brand.keyword" // 聚合字段 (通常使用 keyword 字段)
}
}
}
}
响应示例 (部分):
json
{
...
"hits": { ... }, // size: 0 时 hits 数组是空的
"aggregations": {
"products_by_brand": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [ // 分组结果
{
"key": "Dell", // 品牌名称
"doc_count": 500 // 该品牌的文档数量
},
{
"key": "HP",
"doc_count": 400
},
// ... 更多品牌
]
}
}
}
聚合是 Elasticsearch 实现数据分析和可视化的关键。Kibana 中的 Visualize 功能就是基于 Elasticsearch 的聚合 API 实现的。
第六章:超越入门
本教程覆盖了 Elasticsearch 的核心概念、基本安装和常用的 CRUD (创建、读取、更新、删除) 及搜索操作。但这仅仅是冰山一角。要更深入地使用 Elasticsearch,还需要学习:
- 更高级的查询: 短语查询 (
match_phrase
), 前缀查询 (prefix
), 通配符查询 (wildcard
), 正则表达式查询 (regexp
), fuzzy 查询 (fuzzy
), More Like This 查询等。 - 全文搜索的奥秘: 深入理解分析器 (Analyzer),包括字符过滤器 (Character Filters), 分词器 (Tokenizer), 词条过滤器 (Token Filters)。如何创建自定义分析器。
- 相关度评分: 了解 TF/IDF 或 BM25 等评分算法,以及如何通过提升 (boosting) 或其他方法影响文档的相关度。
- 高级聚合: 管道聚合 (Pipeline Aggregations), 矩阵聚合 (Matrix Aggregations) 等。
- 集群管理: 多节点集群的搭建和管理,主节点选举,分片分配策略。
- 性能优化: 索引设计,查询优化,内存和磁盘调优。
- 数据摄入: 使用 Logstash, Beats 或 Ingest Node 管道导入和处理数据。
- 安全性: 用户认证、授权、加密等。
- 监控与报警: 使用 Elastic Stack 的 X-Pack 功能或第三方工具进行监控。
- 跨集群搜索 (Cross-Cluster Search) 和跨集群复制 (Cross-Cluster Replication)。
学习资源:
- Elasticsearch 官方文档: 最权威、最全面的资料,推荐仔细阅读。https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html
- Elastic 官方博客和教程: 提供了许多实用的文章和用例。
- 各种在线课程和书籍。
总结
恭喜你完成了这篇 Elasticsearch 快速入门教程!我们一起了解了 Elasticsearch 的核心概念,成功搭建了本地环境并进行了基础的数据操作和搜索。
你现在应该对 Elasticsearch 有了初步的认识,并且具备了继续深入学习的基础。记住,实践是最好的学习方法,尝试使用 Kibana Dev Tools 或你喜欢的编程语言客户端(Java, Python, Go, Node.js 等都提供了官方客户端库)来操作你的 Elasticsearch 实例,索引自己的数据,尝试不同的查询和聚合。
Elasticsearch 是一个功能强大且生态系统完善的平台,掌握它将极大地提升你在处理海量数据时的能力。祝你在后续的 Elasticsearch 学习和使用中取得成功!