Elasticsearch 入门实战教程 – wiki基地


Elasticsearch 入门实战:从概念到实践的深度探索

引言:为何选择 Elasticsearch?

在当今这个信息爆炸的时代,我们面临着海量数据的存储、检索和分析挑战。传统的关系型数据库在处理复杂文本搜索、实时分析以及海量数据的横向扩展方面往往显得力不力。这时,一种新型的分布式搜索和分析引擎应运而生,它就是 Elasticsearh。

Elasticsearch 不仅仅是一个强大的全文搜索引擎,它还是一个具备高可用、可伸缩、近乎实时的数据存储和分析平台。它的核心优势在于:

  1. 强大的全文搜索能力: 基于 Apache Lucene,提供高性能、高相关的全文搜索。
  2. 分布式架构: 天生支持分布式,易于横向扩展,处理 PB 级别数据不在话下。
  3. 高可用与容错: 通过分片(Shards)和副本(Replicas)机制,确保数据安全和系统稳定。
  4. 近乎实时: 数据索引后很快就能被搜索到(通常在秒级)。
  5. 丰富的 API: 提供基于 RESTful 的 API,方便与各种应用集成。
  6. 灵活的数据模型: 主要使用 JSON 文档,非强制 Schema(Schema-less),易于快速迭代。
  7. 强大的分析能力: 集成了 Aggregations 功能,可以进行复杂的数据聚合分析。
  8. 生态系统完善: 与 Kibana (数据可视化)、Logstash (数据采集) 共同组成 ELK Stack (或 Elastic Stack),提供一站式解决方案。

无论是构建电商网站的商品搜索、分析海量日志数据、监控服务器指标、还是为应用添加站内搜索功能,Elasticsearch 都是一个极具吸引力的选择。

本教程将带你从零开始,深入理解 Elasticsearch 的核心概念,并亲自动手实践数据的索引、搜索和基本操作。

第一章:核心概念解析

在开始实践之前,理解 Elasticsearch 的一些基本概念至关重要。这些概念构成了 Elasticsearch 分布式、可伸缩和高可用特性的基础。

  1. 集群 (Cluster):

    • 一个集群由一个或多个节点组成,共同协作来存储和处理数据。
    • 集群通过一个唯一的名称进行标识(默认为 elasticsearch)。
    • 集群提供了数据的分布式存储、索引和搜索能力。
    • 一个单节点的 Elasticsearch 也是一个集群。
  2. 节点 (Node):

    • 一个节点是一个单独运行的 Elasticsearch 实例。
    • 每个节点属于一个特定的集群。
    • 节点有不同的类型(角色),如 Master 节点(管理集群)、Data 节点(存储数据)、Ingest 节点(预处理文档)等。在入门阶段,我们可以简单认为一个节点既是 Master 也是 Data 节点。
    • 节点通过名称进行标识(启动时自动生成或手动指定)。
  3. 索引 (Index):

    • 索引是 Elasticsearch 中组织数据的方式,类似于关系型数据库中的“数据库”或“表”(但概念上更偏向数据库)。
    • 索引存储了一系列结构相似的文档。
    • 每个索引都有一个唯一的名称(必须小写)。
    • 在 Elasticsearch 7.x 及以后版本,一个索引通常只有一个类型(Type),默认为 _doc。在早前版本中,一个索引可以有多个类型,但这已被废弃。我们后续的操作都基于只有一个 _doc 类型。
  4. 文档 (Document):

    • 文档是 Elasticsearch 中最小的数据单元,是你可以索引、搜索和删除的基本信息体。
    • 文档以 JSON 格式表示。
    • 每个文档在一个索引中都有一个唯一的 ID。你可以自己指定 ID,也可以让 Elasticsearch 自动生成。
    • 文档是无 Schema 的(Schema-less),这意味着你可以在同一个索引中存储具有不同字段的文档。但要注意,字段的数据类型一旦确定(通过 Mapping),就不能轻易改变。
  5. 分片 (Shard):

    • 分片是索引被分割成的更小的、管理单元。
    • 一个索引可以被分成多个分片,每个分片都是一个功能完整的、独立的 Lucene 索引实例。
    • 分片的目的是解决数据量过大无法存储在单个节点上的问题(水平拆分)以及提高搜索等操作的并行处理能力。
    • 创建索引时需要指定主分片 (Primary Shards) 的数量,这个数量一旦确定就不能更改(通常情况)。
  6. 副本 (Replica):

    • 副本是主分片的拷贝。
    • 副本有两个主要作用:
      • 高可用: 如果主分片所在的节点发生故障,副本可以提升为主分片,确保数据不丢失和服务不中断。
      • 提高搜索性能: 搜索请求可以由主分片或其副本处理,增加了读请求的处理能力。
    • 创建索引时可以指定副本分片 (Replica Shards) 的数量(每个主分片对应的副本数量)。副本数量可以随时更改。
    • 一个副本分片不会与它的主分片放置在同一个节点上,以保证容错性。

理解了这些概念,我们就可以开始搭建环境并进行实际操作了。

第二章:环境搭建:快速启动 Elasticsearch 和 Kibana

最快捷方便的 Elasticsearch 和 Kibana 启动方式是使用 Docker。

前提条件:

  • 安装 Docker 和 Docker Compose。

步骤:

  1. 创建一个名为 docker-compose.yml 的文件。
  2. 将以下内容粘贴到文件中:

“`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

  1. 等待几分钟,直到容器完全启动。
  2. 验证 Elasticsearch 是否启动成功:

    • 打开浏览器或使用 curl 访问 http://localhost:9200
    • 如果看到返回的 JSON 响应,包含 Elasticsearch 版本、集群名称等信息,说明启动成功。
  3. 验证 Kibana 是否启动成功:

    • 打开浏览器访问 http://localhost:5601
    • 如果看到 Kibana 的欢迎界面,说明启动成功。Kibana 可能需要一点时间来连接到 Elasticsearch。

现在,你已经拥有了一个运行中的 Elasticsearch 单节点集群和一个 Kibana 实例。我们将主要使用 Kibana 的 Dev Tools (开发工具) 来与 Elasticsearch 进行交互。

第三章:使用 Kibana Dev Tools 进行基本操作 (CRUD)

Kibana Dev Tools 提供了一个交互式的控制台,可以直接发送 RESTful 请求到 Elasticsearch 集群,非常适合学习和测试。

  1. 打开 Kibana (http://localhost:5601)。
  2. 在左侧导航栏中找到 “Dev Tools” (通常在 “Management” 或 “Analytics” 分类下,具体位置取决于版本)。
  3. 点击 “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 分页和排序

你可以在搜索请求中添加 fromsize 参数来实现分页,添加 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)。不会对查询字符串进行分词。
    • 适用于 keywordnumericdate 等精确值的字段。
    • 通常用于过滤,而不是全文搜索。

    例如,查找分类正好是 “技术图书” 的产品:

    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
  • 动态 Mapping: 当你索引一个新文档时,如果索引不存在或文档中的字段之前未出现过,Elasticsearch 会根据字段的值猜测其数据类型并自动创建 Mapping。这被称为动态 Mapping。

    • 例如,一个字符串字段可能会被映射为 text 类型,并自动创建一个 keyword 子字段 (field.keyword)。
    • 一个数字字段会被映射为相应的数值类型。
    • 一个看起来像日期的字符串可能会被映射为 date 类型。
  • 显式 Mapping: 虽然动态 Mapping 很方便,但在生产环境中,通常建议通过显式 Mapping 精确控制字段的数据类型和索引方式。这可以避免猜测错误,并更好地优化搜索性能。

查看现有 Mapping:

json
GET /products/_mapping

运行上面的命令,你会看到 Elasticsearch 为你之前索引的文档自动生成的 Mapping。例如,namecategory 字段可能被映射为 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 字段)是非常重要的一步。

第六章:实战演练:构建一个简单的产品搜索应用

让我们结合前面学到的知识,模拟一个简单的产品搜索场景。

场景: 我们要索引一些产品数据,并实现按关键词搜索产品名称和描述,按分类精确过滤,按价格范围过滤。

步骤:

  1. 删除旧索引 (如果存在): 为了确保从头开始,先删除之前的 products 索引。

    json
    DELETE /products

  2. 创建带有显式 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" // 日期类型
    }
    }
    }
    }

    运行上面的请求来创建索引。

  3. 索引一些示例文档:

    “`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 批量索引(入门暂不展开)。

  4. 执行搜索查询:

    • 按关键词搜索 (名称或描述包含“手机”或“笔记本”):

      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 的功能远不止这些。

你可以进一步学习的方向包括:

  1. 更高级的 Query DSL: fuzzy (模糊查询), wildcard (通配符), regexp (正则表达式), geo (地理位置查询) 等。
  2. Aggregations (聚合分析): Elasticsearch 强大的分析能力,可以进行分组 (Terms Aggregation)、统计 (Stats Aggregation)、求和 (Sum Aggregation)、平均值 (Avg Aggregation) 等操作,常用于生成报表和图表。
  3. Relevance and Scoring (相关性与得分): 深入理解 Elasticsearch 如何计算文档的相关性得分 (_score),以及如何通过查询 Boost、Function Score Query 等调整得分,优化搜索结果排序。
  4. Mapping 进阶: 了解更多字段类型、参数 (index, doc_values, store 等)、动态模板 (Dynamic Templates)。
  5. Analysis 进阶: 自定义分析器,组合不同的字符过滤器、分词器和词条过滤器,以满足特定的语言或业务需求。
  6. Indexing Performance (索引性能优化): 批量索引 (_bulk API)、Refresh Interval、Translog、Index Buffer 等概念。
  7. Cluster Management (集群管理): 了解 Master 选举、集群健康状态、节点类型、Shrink/Split API。
  8. Monitoring and Alerting (监控与告警): 使用 Metricbeat, Filebeat 等采集集群指标和日志,结合 Kibana 监控。
  9. Security (安全): 配置 X-Pack 安全功能,用户认证、授权、SSL/TLS 加密。
  10. Client Libraries (客户端库): 如何在你的应用中使用官方或第三方的客户端库 (Java, Python, Go, Node.js 等) 与 Elasticsearch 交互。
  11. 生产环境部署: 硬件选型、操作系统优化、网络配置、高可用配置、备份恢复策略。

总结

Elasticsearch 是一个功能强大且复杂的系统,但其核心思想相对直观:将数据索引成易于搜索的结构 (Lucene),并通过分布式架构提供高可用和伸缩性。

本教程从零开始,带你走过了:

  • 理解 Elasticsearch 的核心分布式概念 (集群、节点、索引、文档、分片、副本)。
  • 使用 Docker 快速搭建开发环境 (Elasticsearch + Kibana)。
  • 利用 Kibana Dev Tools 进行文档的 CRUD 操作。
  • 学习使用 Query DSL 进行基本的搜索、过滤、排序和分页。
  • 初步理解了 Mapping 和 Analysis 对数据处理和搜索的影响。
  • 通过一个简单的产品搜索案例,将所学知识串联起来。

掌握这些基础知识,你已经迈出了 Elasticsearch 学习的关键一步。接下来,通过更多的实践和深入学习更高级的功能,你将能够利用 Elasticsearch 解决更广泛、更复杂的数据检索和分析问题。

实践是最好的老师。请继续使用 Kibana Dev Tools 尝试不同的查询,索引自己的数据,探索更多的 API。祝你在 Elasticsearch 的学习旅程中一切顺利!


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部