Elasticsearch 入门实战:从概念到实践的深度探索
引言:为何选择 Elasticsearch?
在当今这个信息爆炸的时代,我们面临着海量数据的存储、检索和分析挑战。传统的关系型数据库在处理复杂文本搜索、实时分析以及海量数据的横向扩展方面往往显得力不力。这时,一种新型的分布式搜索和分析引擎应运而生,它就是 Elasticsearh。
Elasticsearch 不仅仅是一个强大的全文搜索引擎,它还是一个具备高可用、可伸缩、近乎实时的数据存储和分析平台。它的核心优势在于:
- 强大的全文搜索能力: 基于 Apache Lucene,提供高性能、高相关的全文搜索。
- 分布式架构: 天生支持分布式,易于横向扩展,处理 PB 级别数据不在话下。
- 高可用与容错: 通过分片(Shards)和副本(Replicas)机制,确保数据安全和系统稳定。
- 近乎实时: 数据索引后很快就能被搜索到(通常在秒级)。
- 丰富的 API: 提供基于 RESTful 的 API,方便与各种应用集成。
- 灵活的数据模型: 主要使用 JSON 文档,非强制 Schema(Schema-less),易于快速迭代。
- 强大的分析能力: 集成了 Aggregations 功能,可以进行复杂的数据聚合分析。
- 生态系统完善: 与 Kibana (数据可视化)、Logstash (数据采集) 共同组成 ELK Stack (或 Elastic Stack),提供一站式解决方案。
无论是构建电商网站的商品搜索、分析海量日志数据、监控服务器指标、还是为应用添加站内搜索功能,Elasticsearch 都是一个极具吸引力的选择。
本教程将带你从零开始,深入理解 Elasticsearch 的核心概念,并亲自动手实践数据的索引、搜索和基本操作。
第一章:核心概念解析
在开始实践之前,理解 Elasticsearch 的一些基本概念至关重要。这些概念构成了 Elasticsearch 分布式、可伸缩和高可用特性的基础。
-
集群 (Cluster):
- 一个集群由一个或多个节点组成,共同协作来存储和处理数据。
- 集群通过一个唯一的名称进行标识(默认为
elasticsearch
)。 - 集群提供了数据的分布式存储、索引和搜索能力。
- 一个单节点的 Elasticsearch 也是一个集群。
-
节点 (Node):
- 一个节点是一个单独运行的 Elasticsearch 实例。
- 每个节点属于一个特定的集群。
- 节点有不同的类型(角色),如 Master 节点(管理集群)、Data 节点(存储数据)、Ingest 节点(预处理文档)等。在入门阶段,我们可以简单认为一个节点既是 Master 也是 Data 节点。
- 节点通过名称进行标识(启动时自动生成或手动指定)。
-
索引 (Index):
- 索引是 Elasticsearch 中组织数据的方式,类似于关系型数据库中的“数据库”或“表”(但概念上更偏向数据库)。
- 索引存储了一系列结构相似的文档。
- 每个索引都有一个唯一的名称(必须小写)。
- 在 Elasticsearch 7.x 及以后版本,一个索引通常只有一个类型(Type),默认为
_doc
。在早前版本中,一个索引可以有多个类型,但这已被废弃。我们后续的操作都基于只有一个_doc
类型。
-
文档 (Document):
- 文档是 Elasticsearch 中最小的数据单元,是你可以索引、搜索和删除的基本信息体。
- 文档以 JSON 格式表示。
- 每个文档在一个索引中都有一个唯一的 ID。你可以自己指定 ID,也可以让 Elasticsearch 自动生成。
- 文档是无 Schema 的(Schema-less),这意味着你可以在同一个索引中存储具有不同字段的文档。但要注意,字段的数据类型一旦确定(通过 Mapping),就不能轻易改变。
-
分片 (Shard):
- 分片是索引被分割成的更小的、管理单元。
- 一个索引可以被分成多个分片,每个分片都是一个功能完整的、独立的 Lucene 索引实例。
- 分片的目的是解决数据量过大无法存储在单个节点上的问题(水平拆分)以及提高搜索等操作的并行处理能力。
- 创建索引时需要指定主分片 (Primary Shards) 的数量,这个数量一旦确定就不能更改(通常情况)。
-
副本 (Replica):
- 副本是主分片的拷贝。
- 副本有两个主要作用:
- 高可用: 如果主分片所在的节点发生故障,副本可以提升为主分片,确保数据不丢失和服务不中断。
- 提高搜索性能: 搜索请求可以由主分片或其副本处理,增加了读请求的处理能力。
- 创建索引时可以指定副本分片 (Replica Shards) 的数量(每个主分片对应的副本数量)。副本数量可以随时更改。
- 一个副本分片不会与它的主分片放置在同一个节点上,以保证容错性。
理解了这些概念,我们就可以开始搭建环境并进行实际操作了。
第二章:环境搭建:快速启动 Elasticsearch 和 Kibana
最快捷方便的 Elasticsearch 和 Kibana 启动方式是使用 Docker。
前提条件:
- 安装 Docker 和 Docker Compose。
步骤:
- 创建一个名为
docker-compose.yml
的文件。 - 将以下内容粘贴到文件中:
“`yaml
version: ‘3.8’
services:
elasticsearch:
image: elasticsearch:8.12.0 # 使用一个具体的版本,建议使用最新稳定版
container_name: elasticsearch
environment:
– discovery.type=single-node # 单节点模式
– xpack.security.enabled=false # 关闭安全认证(开发环境方便,生产环境强烈建议开启)
– ES_JAVA_OPTS=-Xms512m -Xmx512m # 设置JVM内存,根据机器情况调整
ports:
– “9200:9200” # REST API 端口
– “9300:9300” # 节点间通信端口
volumes:
– esdata:/usr/share/elasticsearch/data # 数据卷持久化数据
ulimits: # 生产环境需要调整更大的限制
memlock:
soft: -1
hard: -1
networks:
– elastic-network
kibana:
image: kibana:8.12.0 # 确保Kibana版本与Elasticsearch版本一致
container_name: kibana
environment:
ELASTICSEARCH_HOSTS: ‘[“http://elasticsearch:9200”]’ # 连接到同网络的Elasticsearch容器
ports:
– “5601:5601” # Kibana UI 端口
depends_on:
– elasticsearch
networks:
– elastic-network
volumes:
esdata: # 定义数据卷
networks:
elastic-network: # 定义一个bridge网络
driver: bridge
“`
- 注意: 上述配置为了入门方便,禁用了 X-Pack 安全认证 (
xpack.security.enabled=false
)。在生产环境中,强烈建议开启并配置安全认证! 生产环境还需要调整 JVM 内存、文件描述符限制、内存锁定等参数。 -
请根据你的 Docker Hub 访问速度选择合适的镜像版本。Elasticsearch 和 Kibana 版本必须保持一致。
-
打开终端,进入到
docker-compose.yml
文件所在的目录。 - 运行以下命令启动容器:
bash
docker-compose up -d
- 等待几分钟,直到容器完全启动。
-
验证 Elasticsearch 是否启动成功:
- 打开浏览器或使用
curl
访问http://localhost:9200
。 - 如果看到返回的 JSON 响应,包含 Elasticsearch 版本、集群名称等信息,说明启动成功。
- 打开浏览器或使用
-
验证 Kibana 是否启动成功:
- 打开浏览器访问
http://localhost:5601
。 - 如果看到 Kibana 的欢迎界面,说明启动成功。Kibana 可能需要一点时间来连接到 Elasticsearch。
- 打开浏览器访问
现在,你已经拥有了一个运行中的 Elasticsearch 单节点集群和一个 Kibana 实例。我们将主要使用 Kibana 的 Dev Tools (开发工具) 来与 Elasticsearch 进行交互。
第三章:使用 Kibana Dev Tools 进行基本操作 (CRUD)
Kibana Dev Tools 提供了一个交互式的控制台,可以直接发送 RESTful 请求到 Elasticsearch 集群,非常适合学习和测试。
- 打开 Kibana (
http://localhost:5601
)。 - 在左侧导航栏中找到 “Dev Tools” (通常在 “Management” 或 “Analytics” 分类下,具体位置取决于版本)。
- 点击 “Console”。你将看到一个左右分栏的界面,左边输入请求,右边显示响应。
Elasticsearch 的 RESTful API 主要通过 HTTP 方法(GET, POST, PUT, DELETE)和路径来指定操作。
3.1 创建(索引)文档 – CREATE / INDEX
索引文档就是将数据添加到 Elasticsearch 中。你可以指定文档 ID,也可以让 Elasticsearch 自动生成。
指定 ID 索引文档:
使用 PUT
请求,路径格式为 /<index_name>/_doc/<document_id>
。
例如,创建一个名为 products
的索引,并添加一个 ID 为 1
的产品文档:
json
PUT /products/_doc/1
{
"name": "Elasticsearch 入门实战教程",
"category": "技术图书",
"price": 99.5,
"tags": ["Elasticsearch", "教程", "实战"],
"publish_date": "2023-10-26"
}
点击控制台左侧的绿色播放按钮运行。如果成功,右侧会返回类似以下的响应:
json
{
"_index": "products",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
_index
: 文档所属的索引名称。_id
: 文档的 ID。_version
: 文档的版本号,每次修改或删除文档都会增加。result
: 操作结果,created
表示文档是新建的。_shards
: 分片信息,表示操作涉及的分片数量。_seq_no
和_primary_term
: 用于并发控制,后面会提及。
自动生成 ID 索引文档:
使用 POST
请求,路径格式为 /<index_name>/_doc
。
json
POST /products/_doc
{
"name": "Kibana 数据可视化指南",
"category": "技术图书",
"price": 75.0,
"tags": ["Kibana", "教程", "可视化"]
}
响应:
json
{
"_index": "products",
"_id": "..." , // Elasticsearch自动生成的唯一ID
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1
}
可以看到 _id
是一个由 Elasticsearch 自动生成的字符串。
3.2 读取文档 – READ
使用 GET
请求,路径格式为 /<index_name>/_doc/<document_id>
。
获取 ID 为 1
的文档:
json
GET /products/_doc/1
响应:
json
{
"_index": "products",
"_id": "1",
"_version": 1,
"_seq_no": 0,
"_primary_term": 1,
"found": true, // 是否找到文档
"_source": { // 文档的原始数据
"name": "Elasticsearch 入门实战教程",
"category": "技术图书",
"price": 99.5,
"tags": ["Elasticsearch", "教程", "实战"],
"publish_date": "2023-10-26"
}
}
_source
字段包含了原始的 JSON 文档数据。
3.3 更新文档 – UPDATE
更新文档有两种方式:全量更新和局部更新。
全量更新 (Reindex):
实际上,Elasticsearch 不支持真正意义上的原地更新。当你使用 PUT /<index>/_doc/<id>
索引一个已存在的文档时,Elasticsearch 会先删除旧文档,然后索引新文档,_version
会增加。这就像重新索引了一遍。
json
PUT /products/_doc/1
{
"name": "Elasticsearch 入门实战教程 (第二版)",
"category": "技术图书",
"price": 105.0,
"tags": ["Elasticsearch", "教程", "实战", "第二版"],
"publish_date": "2024-03-15",
"publisher": "博文出版"
}
这次响应中的 result
会是 updated
,_version
会变成 2
。新文档完全替换了旧文档。
局部更新 (Partial Update):
Elasticsearch 提供了 _update
API 来执行局部更新,这通常更高效,因为它不需要发送整个文档。
使用 POST
请求,路径格式为 /<index_name>/_update/<document_id>
,请求体中包含一个 doc
对象,里面是需要修改或添加的字段。
例如,给 ID 为 1
的文档添加一个 language
字段,并修改 price
:
json
POST /products/_update/1
{
"doc": {
"price": 110.0,
"language": "中文"
}
}
响应中的 result
会是 updated
,_version
会变成 3
。通过 GET /products/_doc/1
可以看到文档现在包含了 language
字段,并且 price
已更新。
你还可以使用 script
来更新,但这更复杂,适合批量或基于逻辑的更新。
3.4 删除文档 – DELETE
使用 DELETE
请求,路径格式为 /<index_name>/_doc/<document_id>
。
删除 ID 为 1
的文档:
json
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
}
再次尝试获取 ID 为 1
的文档:
json
GET /products/_doc/1
响应中的 found
将为 false
,表示文档未找到。
重要说明: Elasticsearch 的删除操作是标记删除,文档并不会立即从磁盘移除,而是在后台的段合并 (segment merge) 过程中被清除。
3.5 管理索引
除了文档操作,你还可以管理索引本身。
-
创建索引: 可以通过索引第一个文档时自动创建,或者显式创建。显式创建可以指定 Mapping 和 Settings。
json
PUT /my_new_index
{
"settings": {
"index": {
"number_of_shards": 3, // 指定主分片数量
"number_of_replicas": 1 // 指定副本分片数量
}
},
"mappings": { // 指定Mapping
"properties": {
"title": { "type": "text" },
"timestamp": { "type": "date" }
}
}
} -
获取索引信息:
json
GET /my_new_index -
获取索引 Mapping:
json
GET /my_new_index/_mapping -
获取索引 Settings:
json
GET /my_new_index/_settings -
删除索引:
json
DELETE /my_new_index注意:删除索引是不可逆的,会删除所有数据!
第四章:深入搜索:Query DSL 基础
搜索是 Elasticsearch 的核心功能。虽然可以使用简单的 URL 参数进行搜索 (GET /_search?q=...
),但更强大、更灵活的方式是使用请求体中的 Query DSL (Domain Specific Language)。Query DSL 是一种基于 JSON 的查询语言。
基本的搜索请求格式为 GET /<index_name>/_search
,请求体中包含一个 query
对象。
4.1 匹配所有文档 – match_all
最简单的查询是匹配索引中的所有文档。
json
GET /products/_search
{
"query": {
"match_all": {}
}
}
响应会包含所有文档的 _score
(相关性得分,match_all
情况下所有文档得分相同)、_index
、_id
以及 _source
数据。响应结构通常是:
json
{
"took": 23, // 查询耗时(毫秒)
"timed_out": false, // 是否超时
"_shards": { ... }, // 分片信息
"hits": { // 搜索结果
"total": { // 总匹配文档数
"value": 2,
"relation": "eq" // eq 表示 exact, gte 表示 great than or equal to (当结果超过10000时)
},
"max_score": 1.0, // 最高相关性得分
"hits": [ // 具体匹配到的文档列表
{
"_index": "products",
"_id": "...",
"_score": 1.0,
"_source": { ... }
},
{
"_index": "products",
"_id": "...",
"_score": 1.0,
"_source": { ... }
}
// ... 默认返回前10条
]
}
}
4.2 分页和排序
你可以在搜索请求中添加 from
和 size
参数来实现分页,添加 sort
参数来实现排序。
json
GET /products/_search
{
"query": {
"match_all": {}
},
"from": 0, // 从第0条开始 (第一页)
"size": 10, // 每页10条
"sort": [ // 排序
{ "price": { "order": "asc" } } // 按价格升序排序
// { "publish_date": { "order": "desc" } } // 也可以按日期降序
// "_score" // 也可以按相关性得分排序 (默认)
]
}
from
: 偏移量,跳过前多少条结果。size
: 返回结果的最大数量。sort
: 排序字段和顺序。注意,按非_score
排序时,相关性得分 (_score
) 将不起作用。
4.3 常用查询类型
Query DSL 提供了丰富的查询类型,以下是一些最常用的:
-
match
Query:- 用于执行全文搜索或模糊匹配。会对查询字符串进行分词处理,然后匹配文档中对应字段的分词。
- 适用于
text
类型的字段。
例如,搜索名称或描述中包含 “教程” 的产品:
json
GET /products/_search
{
"query": {
"match": {
"name": "教程"
}
}
}或者匹配多个字段:
json
GET /products/_search
{
"query": {
"multi_match": {
"query": "教程",
"fields": ["name", "category", "tags"] // 在这些字段中搜索
}
}
} -
term
Query:- 用于精确匹配单个词条 (term)。不会对查询字符串进行分词。
- 适用于
keyword
、numeric
、date
等精确值的字段。 - 通常用于过滤,而不是全文搜索。
例如,查找分类正好是 “技术图书” 的产品:
json
GET /products/_search
{
"query": {
"term": {
"category.keyword": "技术图书" // 注意这里使用了 .keyword 后缀
}
}
}
重要: 默认情况下,text
字段也会自动创建一个keyword
子字段(除非你在 Mapping 中禁用)。.keyword
后缀引用了这个子字段,它的内容是整个原始字符串,不会被分词。因此,term
查询通常作用在.keyword
字段上,以确保精确匹配。 -
range
Query:- 用于查找某个数值或日期字段在指定范围内 (大于、小于、大于等于、小于等于) 的文档。
例如,查找价格在 80 到 120 之间的产品:
json
GET /products/_search
{
"query": {
"range": {
"price": {
"gte": 80, // 大于等于
"lte": 120 // 小于等于
}
}
}
}
日期范围:
json
GET /products/_search
{
"query": {
"range": {
"publish_date": {
"gte": "2024-01-01",
"lt": "now/d" // 小于今天,now 是当前时间,/d 表示日期截断
}
}
}
} -
bool
Query:- 用于组合多个查询条件,通过逻辑关系 (
must
,filter
,should
,must_not
) 进行过滤。 must
: 子查询必须匹配,会影响相关性得分 (_score
)。相当于 AND。filter
: 子查询必须匹配,但不影响相关性得分。常用于结构化数据过滤,性能更好。相当于 AND。should
: 子查询应该匹配。如果至少一个should
匹配,文档就匹配。影响相关性得分。相当于 OR。must_not
: 子查询必须不匹配。不影响相关性得分。相当于 NOT。
例如,查找分类是 “技术图书” 且名称中包含 “教程” 且价格低于 100 的产品:
json
GET /products/_search
{
"query": {
"bool": {
"filter": [ // filter 不影响 _score,且性能好
{ "term": { "category.keyword": "技术图书" } },
{ "range": { "price": { "lt": 100 } } }
],
"must": [ // must 影响 _score
{ "match": { "name": "教程" } }
]
}
}
} - 用于组合多个查询条件,通过逻辑关系 (
4.4 结果字段过滤 (_source)
默认情况下,搜索结果会返回完整的 _source
文档。你可以使用 _source
参数来指定只返回部分字段,减少网络传输和处理开销。
json
GET /products/_search
{
"query": {
"match_all": {}
},
"_source": ["name", "price"] // 只返回 name 和 price 字段
}
你也可以排除某些字段:
json
GET /products/_search
{
"query": {
"match_all": {}
},
"_source": {
"excludes": ["tags", "publish_date"] // 排除 tags 和 publish_date
}
}
第五章:理解 Mapping 和 Analysis
Mapping 和 Analysis 是 Elasticsearch 如何处理和存储数据、特别是文本数据,以及如何进行搜索的基础。理解它们对于构建有效的搜索至关重要。
5.1 Mapping
- 定义: Mapping 定义了索引中文档及其字段的结构和数据类型。它告诉 Elasticsearch 如何索引数据以及数据是否可以被搜索。
-
数据类型: Elasticsearch 支持多种数据类型,包括:
- Text Types:
text
(用于全文搜索,会分词),keyword
(用于精确匹配,如标签、名称,不会分词) - Numeric Types:
long
,integer
,short
,byte
,double
,float
,half_float
,scaled_float
- Date Type:
date
- Boolean Type:
boolean
- Binary Type:
binary
- Geo Types:
geo_point
,geo_shape
- Structured Types:
object
,nested
(处理数组对象) - Specialized Types:
ip
,completion
,search_as_you_type
等
- Text Types:
-
动态 Mapping: 当你索引一个新文档时,如果索引不存在或文档中的字段之前未出现过,Elasticsearch 会根据字段的值猜测其数据类型并自动创建 Mapping。这被称为动态 Mapping。
- 例如,一个字符串字段可能会被映射为
text
类型,并自动创建一个keyword
子字段 (field.keyword
)。 - 一个数字字段会被映射为相应的数值类型。
- 一个看起来像日期的字符串可能会被映射为
date
类型。
- 例如,一个字符串字段可能会被映射为
- 显式 Mapping: 虽然动态 Mapping 很方便,但在生产环境中,通常建议通过显式 Mapping 精确控制字段的数据类型和索引方式。这可以避免猜测错误,并更好地优化搜索性能。
查看现有 Mapping:
json
GET /products/_mapping
运行上面的命令,你会看到 Elasticsearch 为你之前索引的文档自动生成的 Mapping。例如,name
和 category
字段可能被映射为 text
,并带有 keyword
子字段。
显式创建带有 Mapping 的索引:
你可以像之前那样使用 PUT /<index_name>
并指定 mappings
来创建索引时定义 Mapping。
json
PUT /my_products_with_mapping
{
"mappings": {
"properties": {
"product_name": {
"type": "text",
"analyzer": "ik_smart" // 指定中文分词器,如果已安装并配置
},
"product_id": {
"type": "keyword" // 产品ID用keyword,精确匹配
},
"price": {
"type": "float"
},
"category": {
"type": "keyword"
},
"description": {
"type": "text",
"analyzer": "ik_max_word"
},
"created_at": {
"type": "date"
}
}
}
}
* 注意: 一旦一个字段被索引,它的 Mapping(尤其是数据类型)通常不能修改。如果需要修改 Mapping,通常需要 Reindex (重建索引),将数据从旧索引导入到新索引。
5.2 Analysis
- 定义: Analysis 是一个将文本转换为一系列词条 (tokens) 的过程,这些词条随后被索引以供搜索。它是全文搜索的关键。
-
过程: Analysis 管道由零个或多个字符过滤器 (Character Filters)、一个分词器 (Tokenizer) 和零个或多个词条过滤器 (Token Filters) 组成。
- Character Filters (字符过滤器): 在文本被分词之前处理文本,例如移除 HTML 标签、替换特殊字符。
- Tokenizer (分词器): 将字符串分割成独立的词条。例如,标准分词器 (Standard Tokenizer) 按空格和标点符号分词。中文分词器 (如 IK Analyzer) 会根据词典或算法将中文句子分词。
- Token Filters (词条过滤器): 对分词器产生的词条进行处理,例如转换为小写、移除停用词 (如“的”、“是”)、添加同义词、词干提取 (如 “running” -> “run”)。
-
Analyzer (分析器): Analyzer 是 Analysis 过程的封装,它包含了字符过滤器、分词器和词条过滤器的组合。
- 默认分析器: Elasticsearch 默认使用
standard
分析器,它适用于大多数西方语言。对于中文、日文、韩文等,需要安装并配置相应的分词插件(如 IK Analyzer)。
如何测试分析器:
你可以使用 _analyze
API 来测试某个文本经过特定分析器处理后的结果。
json
GET /_analyze
{
"analyzer": "standard", // 或 "ik_smart", "ik_max_word" (如果已安装)
"text": "Quick Brown Foxes jump over the lazy dogs."
}
响应会显示分词后的词条列表。
json
GET /_analyze
{
"analyzer": "standard",
"text": "Elasticsearch入门实战教程"
}
使用 standard
分析器处理中文时,可能会按单字或简单的符号进行分词,效果不佳。
json
GET /_analyze
{
"analyzer": "ik_smart", // 需要安装IK分词器插件
"text": "Elasticsearch入门实战教程"
}
如果安装了 IK 分词器并使用 ik_smart
,结果可能是 ["elasticsearch", "入门", "实战", "教程"]
,更符合中文语境。
Mapping 与 Analysis 的关系:
在 Mapping 中,你可以为 text
类型的字段指定使用哪个分析器进行索引 (index_analyzer
,已废弃,现在直接用 analyzer
) 和搜索 (search_analyzer
)。如果没有指定,会使用默认的 standard
分析器,或在设置中配置的默认分析器。
keyword
类型的字段不进行 Analysis,它们的值被当作一个完整的词条进行索引。
总结:
- Mapping 决定了数据的类型和如何存储。
- Analysis 决定了
text
字段如何被分词和处理,从而影响全文搜索的结果和相关性。 - 对于中文搜索,安装和配置合适的中文字分词器(并在 Mapping 中应用到
text
字段)是非常重要的一步。
第六章:实战演练:构建一个简单的产品搜索应用
让我们结合前面学到的知识,模拟一个简单的产品搜索场景。
场景: 我们要索引一些产品数据,并实现按关键词搜索产品名称和描述,按分类精确过滤,按价格范围过滤。
步骤:
-
删除旧索引 (如果存在): 为了确保从头开始,先删除之前的
products
索引。json
DELETE /products -
创建带有显式 Mapping 的新索引: 定义好每个字段的类型。这里假设你已经安装了 IK 分词器。
json
PUT /products
{
"settings": {
"index": {
"number_of_shards": 1, // 单节点测试,一个主分片足够
"number_of_replicas": 0 // 单节点测试,不需要副本
}
},
"mappings": {
"properties": {
"product_id": {
"type": "keyword" // 精确匹配,不会分词
},
"name": {
"type": "text",
"analyzer": "ik_smart" // 使用ik分词器进行索引和搜索
},
"description": {
"type": "text",
"analyzer": "ik_max_word" // 描述字段用更细粒度的分词
},
"category": {
"type": "keyword" // 精确匹配分类
},
"price": {
"type": "float" // 浮点数类型
},
"stock": {
"type": "integer" // 整数类型
},
"on_sale": {
"type": "boolean" // 布尔类型
},
"added_date": {
"type": "date" // 日期类型
}
}
}
}
运行上面的请求来创建索引。 -
索引一些示例文档:
“`json
PUT /products/_doc/101
{
“product_id”: “101”,
“name”: “小米手机 13 Pro”,
“description”: “高性能旗舰手机,拍照效果极佳,超长续航。”,
“category”: “电子产品”,
“price”: 4999.0,
“stock”: 50,
“on_sale”: true,
“added_date”: “2023-03-10”
}PUT /products/_doc/102
{
“product_id”: “102”,
“name”: “华为 MateBook X Pro 2023”,
“description”: “轻薄笔记本电脑,办公学习利器,全面屏设计。”,
“category”: “电子产品”,
“price”: 9888.0,
“stock”: 30,
“on_sale”: true,
“added_date”: “2023-05-20”
}PUT /products/_doc/103
{
“product_id”: “103”,
“name”: “深入理解 Java 虚拟机”,
“description”: “Java 程序员必读的经典技术书籍。”,
“category”: “技术图书”,
“price”: 120.5,
“stock”: 100,
“on_sale”: false,
“added_date”: “2022-11-15”
}PUT /products/_doc/104
{
“product_id”: “104”,
“name”: “Elasticsearch 实战指南”,
“description”: “学习 Elasticsearch 从入门到精通,包含大量实战案例。”,
“category”: “技术图书”,
“price”: 85.0,
“stock”: 80,
“on_sale”: true,
“added_date”: “2023-08-01”
}PUT /products/_doc/105
{
“product_id”: “105”,
“name”: “办公室人体工学椅”,
“description”: “保护脊椎,舒适办公,适合长时间使用电脑。”,
“category”: “家居用品”,
“price”: 1500.0,
“stock”: 20,
“on_sale”: true,
“added_date”: “2023-09-01”
}
``
_bulk` API 批量索引(入门暂不展开)。
逐个运行这些请求,或者使用 -
执行搜索查询:
-
按关键词搜索 (名称或描述包含“手机”或“笔记本”):
json
GET /products/_search
{
"query": {
"multi_match": {
"query": "手机 笔记本",
"fields": ["name", "description"],
"type": "best_fields" // 匹配度最高的字段决定得分
}
}
}
运行你会发现返回了小米手机和华为笔记本。 -
按分类过滤 (精确匹配“技术图书”):
json
GET /products/_search
{
"query": {
"term": {
"category": "技术图书" // category 是 keyword 类型,直接用 term
}
}
}
运行你会发现返回了两本技术图书。 -
组合条件搜索 (电子产品分类,价格低于 6000,且名称包含“手机”):
json
GET /products/_search
{
"query": {
"bool": {
"filter": [
{ "term": { "category": "电子产品" } },
{ "range": { "price": { "lt": 6000 } } }
],
"must": [
{ "match": { "name": "手机" } }
]
}
}
}
运行你会发现只返回了小米手机。 -
查找所有在售 (on_sale 为 true) 的产品:
json
GET /products/_search
{
"query": {
"term": {
"on_sale": true
}
}
} -
按价格降序排序并分页 (第二页,每页 2 条):
json
GET /products/_search
{
"query": {
"match_all": {}
},
"sort": [
{ "price": { "order": "desc" } }
],
"from": 2,
"size": 2
}
运行会返回价格第三高和第四高的产品。
-
通过这些例子,你应该能初步掌握如何构建更复杂的查询来满足实际需求。
第七章:进阶学习方向与总结
本教程带你初步认识了 Elasticsearch 的核心概念、环境搭建、基本 CRUD 操作以及 Query DSL 的基础用法。然而,Elasticsearch 的功能远不止这些。
你可以进一步学习的方向包括:
- 更高级的 Query DSL: fuzzy (模糊查询), wildcard (通配符), regexp (正则表达式), geo (地理位置查询) 等。
- Aggregations (聚合分析): Elasticsearch 强大的分析能力,可以进行分组 (Terms Aggregation)、统计 (Stats Aggregation)、求和 (Sum Aggregation)、平均值 (Avg Aggregation) 等操作,常用于生成报表和图表。
- Relevance and Scoring (相关性与得分): 深入理解 Elasticsearch 如何计算文档的相关性得分 (
_score
),以及如何通过查询 Boost、Function Score Query 等调整得分,优化搜索结果排序。 - Mapping 进阶: 了解更多字段类型、参数 (
index
,doc_values
,store
等)、动态模板 (Dynamic Templates)。 - Analysis 进阶: 自定义分析器,组合不同的字符过滤器、分词器和词条过滤器,以满足特定的语言或业务需求。
- Indexing Performance (索引性能优化): 批量索引 (
_bulk
API)、Refresh Interval、Translog、Index Buffer 等概念。 - Cluster Management (集群管理): 了解 Master 选举、集群健康状态、节点类型、Shrink/Split API。
- Monitoring and Alerting (监控与告警): 使用 Metricbeat, Filebeat 等采集集群指标和日志,结合 Kibana 监控。
- Security (安全): 配置 X-Pack 安全功能,用户认证、授权、SSL/TLS 加密。
- Client Libraries (客户端库): 如何在你的应用中使用官方或第三方的客户端库 (Java, Python, Go, Node.js 等) 与 Elasticsearch 交互。
- 生产环境部署: 硬件选型、操作系统优化、网络配置、高可用配置、备份恢复策略。
总结
Elasticsearch 是一个功能强大且复杂的系统,但其核心思想相对直观:将数据索引成易于搜索的结构 (Lucene),并通过分布式架构提供高可用和伸缩性。
本教程从零开始,带你走过了:
- 理解 Elasticsearch 的核心分布式概念 (集群、节点、索引、文档、分片、副本)。
- 使用 Docker 快速搭建开发环境 (Elasticsearch + Kibana)。
- 利用 Kibana Dev Tools 进行文档的 CRUD 操作。
- 学习使用 Query DSL 进行基本的搜索、过滤、排序和分页。
- 初步理解了 Mapping 和 Analysis 对数据处理和搜索的影响。
- 通过一个简单的产品搜索案例,将所学知识串联起来。
掌握这些基础知识,你已经迈出了 Elasticsearch 学习的关键一步。接下来,通过更多的实践和深入学习更高级的功能,你将能够利用 Elasticsearch 解决更广泛、更复杂的数据检索和分析问题。
实践是最好的老师。请继续使用 Kibana Dev Tools 尝试不同的查询,索引自己的数据,探索更多的 API。祝你在 Elasticsearch 的学习旅程中一切顺利!