Elasticsearch的索引和文档操作是所有上层功能的基础,不管做日志检索、商品搜索还是指标分析,都绕不开这两个对象。索引负责组织数据,文档则是实际存储的一条条记录。本文用一组可以直接执行的REST请求展示完整流程,从创建索引开始,到写入文档、更新字段、删除数据,最后观察批量导入和版本控制。

创建索引与显式映射
先创建一个名为products的索引。如果不指定mapping,Elasticsearch会根据第一条写入的文档自动推断字段类型,这叫作动态映射。动态映射虽然省事,但很容易把商品ID识别成long,或者把时间字符串识别成text而不是date,后续聚合和排序就可能报错。生产环境更推荐在建索引时显式声明字段类型。
创建索引并指定mapping的请求如下。这里先定义三个字段:name为text类型并配置ik分词器,price为scaled_float类型方便精确计算,create_time为date类型。
curl -X PUT "localhost:9200/products" -H 'Content-Type: application/json' -d'
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "ik_max_word"
},
"price": {
"type": "scaled_float",
"scaling_factor": 100
},
"create_time": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
}
'
如果只是临时测试,也可以直接发送PUT /products不携带请求体,Elasticsearch会创建一个空索引,所有映射交给动态推断。字段类型一旦被推断错误,后续修改通常需要新建索引并迁移数据,因为mapping中的核心类型不可直接变更。text类型默认会生成一个额外的keyword子字段,适合做精确匹配,例如name.keyword。
创建完成后可以用GET /products查看索引的settings和mappings,用GET /_cat/indices?v查看集群中所有索引的健康状态、文档数和存储大小。删除索引使用DELETE /products,这个操作会物理清除所有分片数据,执行前要确认没有误删。
文档的增删改查与版本控制
写入单个文档可以使用PUT或POST。PUT需要指定文档ID,如果ID已存在会覆盖旧文档;POST不指定ID时由Elasticsearch自动生成一个随机字符串ID。下面用POST向products索引写入一条商品文档。
curl -X POST "localhost:9200/products/_doc" -H 'Content-Type: application/json' -d'
{
"name": "无线蓝牙耳机",
"price": 299.99,
"create_time": "2025-06-15 10:30:00"
}
'
响应中会返回_index、_id、_version和result字段。_version从1开始,每次修改文档都会递增。如果采用PUT指定ID写入,例如PUT /products/_doc/1001,再次写入相同ID时Elasticsearch会先删除旧文档再写入新文档,version继续加1。这种覆盖操作虽然简单,但不会保留历史版本,删除和写入之间还有短暂窗口。
更新部分字段建议使用_update API,它只合并指定字段,不需要重新发送整篇文档。下面的请求把文档ID为1001的价格改为259.99,同时给name字段追加一个颜色标签。doc参数内只写需要变更的字段即可。
curl -X POST "localhost:9200/products/_update/1001" -H 'Content-Type: application/json' -d'
{
"doc": {
"price": 259.99,
"name": "无线蓝牙耳机 黑色"
}
}
'
查询文档使用GET /products/_doc/1001,可以看到_source中保存的原始JSON。如果只想获取部分字段,可以加?_source=name,price参数。删除文档使用DELETE /products/_doc/1001,删除后再次查询会返回404。Elasticsearch删除文档并不会立刻释放磁盘空间,而是在段合并时真正清除,所以频繁删除后建议观察磁盘使用率。
批量操作与常见错误排查
单条写入适合调试,导入大量数据时用_bulk批量接口可以显著减少网络往返。_bulk请求体由多行JSON组成,每两行为一组,第一行描述操作和元数据,第二行是文档内容。下面示例批量写入两条商品数据。
curl -X POST "localhost:9200/_bulk" -H 'Content-Type: application/x-ndjson' --data-binary @bulk_data.json
其中bulk_data.json的内容如下,注意每一行末尾都必须是换行符,包括最后一行。
{"index":{"_index":"products","_id":"2001"}}
{"name":"机械键盘","price":399.00,"create_time":"2025-06-16 14:20:00"}
{"index":{"_index":"products","_id":"2002"}}
{"name":"4K显示器","price":1899.00,"create_time":"2025-06-16 15:05:00"}
批量请求中如果某一条因为字段类型不匹配而失败,其他文档仍然会继续写入,响应中的errors字段会变为true。排查时重点看items数组里每个子项的status和error字段。例如向price字段写入字符串abc,会返回mapper_parsing_exception异常,提示failed to parse field [price] of type [scaled_float]。这种错误不会影响整个批次,但需要逐条核对。
另一个常见问题是版本冲突。Elasticsearch默认采用乐观锁并发控制,每个写操作可以带上if_seq_no和if_primary_term参数,只有版本匹配时才会写入。如果两个客户端同时更新同一文档,后提交的一方会收到409 version_conflict_engine_exception。处理并发更新时,可以先获取最新版本号,再重新提交更新,或者使用脚本更新绕过版本检查。批量操作如果遇到版本冲突,同样不会回滚全部请求,只会跳过冲突项。
最后建议在测试环境开启慢日志,观察查询和写入耗时。索引的refresh_interval默认1秒,写入后立刻查询可能看不到最新数据,可以调用POST /products/_refresh强制刷新,但生产环境频繁刷新会增加CPU负载。对于不再变动的旧索引,可以关闭自动刷新,提高批量导入速度。
Elasticsearch索引操作文档操作修改时间:2026-09-24 13:06:28