ElasticsearchNotes

第 04 章:第一批请求

zjc 于 2026-01-04 发布

这是《Elasticsearch 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 本章通过一组可执行请求熟悉 Elasticsearch 的基础 API:集群检查、创建索引、写入文档、查询文档、更新文档、删除文档、批量操作和搜索分析。

4.1 请求格式

Elasticsearch 提供 REST API,常见形式:

GET /_cluster/health
PUT /products
POST /products/_search

使用 curl:

curl -X GET "http://localhost:9200/_cluster/health?pretty"
curl -X PUT "http://localhost:9200/products" \
  -H 'Content-Type: application/json' \
  -d '{"settings":{"number_of_shards":1}}'

使用 Kibana Dev Tools 时可以省略主机地址:

GET /_cluster/health

4.2 检查集群

GET /
GET /_cluster/health?pretty
GET /_cat/nodes?v
GET /_cat/indices?v
GET /_cat/shards?v

解释:

API 用途
GET / 查看版本与集群名
_cluster/health 查看健康状态
_cat/nodes 查看节点
_cat/indices 查看索引
_cat/shards 查看分片分布

4.3 创建索引

PUT /books
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "standard"
      },
      "author": { "type": "keyword" },
      "price": { "type": "double" },
      "tags": { "type": "keyword" },
      "published_at": { "type": "date" }
    }
  }
}

查看设置:

GET /books/_settings

查看 Mapping:

GET /books/_mapping

删除索引:

DELETE /books

4.4 写入第一条文档

自动生成 ID:

POST /books/_doc
{
  "title": "Elasticsearch 实战教程",
  "author": "张三",
  "price": 99.0,
  "tags": ["搜索", "Elasticsearch"],
  "published_at": "2026-08-25T10:00:00Z"
}

指定 ID:

PUT /books/_doc/1
{
  "title": "分布式搜索原理",
  "author": "李四",
  "price": 129.0,
  "tags": ["分布式", "搜索"],
  "published_at": "2026-08-20T10:00:00Z"
}

返回示例:

{
  "_index": "books",
  "_id": "1",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

PUT /_doc/{id} 是 Index 操作:不存在则创建,存在则整体替换并版本加一。

4.5 读取文档

按 ID 读取:

GET /books/_doc/1

只读 _source

GET /books/_source/1

选择部分字段:

GET /books/_doc/1?_source=title,price

判断存在:

HEAD /books/_doc/1

不存在文档时:

{
  "_index": "books",
  "_id": "999",
  "found": false
}

4.6 更新文档

局部更新:

POST /books/_update/1
{
  "doc": {
    "price": 119.0
  }
}

脚本更新:

POST /books/_update/1
{
  "script": {
    "source": "ctx._source.price += params.increase",
    "lang": "painless",
    "params": {
      "increase": 10
    }
  }
}

upsert:

POST /books/_update/2
{
  "doc": {
    "price": 89.0
  },
  "doc_as_upsert": true
}

ES 的更新并不是原地修改字段。流程近似为:

读取旧文档 -> 合并修改 -> 删除旧版本 -> 写入新版本

频繁更新会带来额外索引成本。

4.7 删除文档

DELETE /books/_doc/1

按查询删除:

POST /books/_delete_by_query
{
  "query": {
    "term": {
      "author": "李四"
    }
  }
}

_delete_by_query 会占用较多资源,生产环境应控制批次和并发,避免影响在线查询。

4.8 批量写入

_bulk API 可以一次提交多个操作。

POST /_bulk
{"index": {"_index": "books", "_id": "3"}}
{"title": "日志检索与可观测性", "author": "王五", "price": 89.0, "tags": ["日志", "运维"], "published_at": "2026-08-19T10:00:00Z"}
{"index": {"_index": "books", "_id": "4"}}
{"title": "聚合分析入门", "author": "赵六", "price": 79.0, "tags": ["分析"], "published_at": "2026-08-18T10:00:00Z"}
{"update": {"_index": "books", "_id": "3"}}
{"doc": {"price": 85.0}}
{"delete": {"_index": "books", "_id": "4"}}

注意:

  1. 每行必须是完整 JSON;
  2. 请求体不能格式化为多行 JSON;
  3. 批量请求不宜过大,常见 5-15MB;
  4. 返回中需要逐条检查失败项;
  5. bulk 是批量提交,不保证所有操作在同一事务中成功。

4.9 第一条搜索请求

GET /books/_search
{
  "query": {
    "match": {
      "title": "搜索"
    }
  }
}

返回结构:

{
  "took": 12,
  "timed_out": false,
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 0.87,
    "hits": [
      {
        "_index": "books",
        "_id": "3",
        "_score": 0.87,
        "_source": {
          "title": "日志检索与可观测性"
        }
      }
    ]
  }
}

关键字段:

字段 含义
took 查询耗时,单位毫秒
timed_out 是否超时收集完结果
hits.total 命中数量
max_score 最高得分
hits.hits 当前页文档
_score 相关性得分
_source 原始文档

4.10 组合过滤与排序

GET /books/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "搜索" } }
      ],
      "filter": [
        { "range": { "price": { "lte": 100 } } }
      ]
    }
  },
  "sort": [
    { "published_at": { "order": "desc" } }
  ],
  "size": 10
}

must 参与相关性计算,filter 不参与评分且更容易被缓存。

4.11 简单聚合

GET /books/_search
{
  "size": 0,
  "aggs": {
    "by_author": {
      "terms": {
        "field": "author",
        "size": 10
      }
    },
    "avg_price": {
      "avg": {
        "field": "price"
      }
    }
  }
}

说明:

  1. size: 0 表示不要返回文档,只要聚合结果;
  2. terms 聚合用于分组;
  3. avg 聚合用于计算平均值;
  4. terms 默认按桶内文档数倒序;
  5. 聚合字段需要 Doc Values,通常是 keyword 或数值类型。

4.12 分页

GET /books/_search
{
  "from": 0,
  "size": 20,
  "query": {
    "match_all": {}
  }
}
GET /books/_search
{
  "size": 20,
  "query": { "match_all": {} },
  "sort": [
    { "published_at": "desc" },
    { "_id": "asc" }
  ],
  "search_after": ["2026-08-25T10:00:00Z", "1"]
}

浅分页适合产品搜索前几页;深分页应使用 search_after,不要使用巨大的 from

4.13 请求超时与条件刷新

手动刷新:

POST /books/_refresh

带超时查询:

GET /books/_search?timeout=500ms
{
  "query": { "match_all": {} }
}

注意:查询超时并不等于取消所有底层执行,也不保证返回完整结果。生产上应结合慢日志、查询分析和资源隔离治理。

4.14 常见错误

错误 原因 处理
index_not_found_exception 索引不存在 检查索引名
mapper_parsing_exception 字段类型不匹配 检查 Mapping
illegal_argument_exception text 直接聚合 使用 keyword 子字段
search_phase_execution_exception 查询语法或字段错误 查看返回 failure
es_rejected_execution_exception 写入或查询队列满 限流、扩容、降低并发
cluster_block_exception 磁盘水位或只读 检查磁盘和 block

4.15 本章小结

4.16 思考题

  1. PUT _doc/1 连续执行三次,_version 会如何变化?
  2. 为什么 _bulk 不能把 JSON 格式化成多行?
  3. mustfilter 的核心区别是什么?
  4. 为什么按 ID GET 通常比搜索更快、更确定?
  5. 如果 bulk 返回 HTTP 200,是否代表所有操作都成功?为什么?