Elasticsearch 学习笔记筑基-文档操作篇

Elasticsearch 学习笔记 - 文档操作完整篇

摘要 :本文全面系统地介绍了 Elasticsearch 文档操作的核心知识,涵盖文档的新增 (指定ID、自动生成ID、创建式新增)、更新 (全量更新、部分更新、脚本更新、批量更新)、查询 (ID查询、字段过滤、批量查询)和删除 (ID删除、批量删除、按查询删除)四大操作。文章不仅提供了详细的语法示例和返回结果解析,还通过实际应用场景 (用户管理、商品管理、日志记录、订单管理、计数器)展示了如何在实际项目中应用这些操作。最后,总结了最佳实践建议常用命令速查返回结果字段详解注意事项,帮助开发者高效、安全地使用 Elasticsearch 进行文档操作。

目录


一、文档新增操作

Elasticsearch提供了多种文档新增方式,根据不同的业务场景可以选择合适的方法。

1.1 指定ID新增文档

语法格式
json 复制代码
PUT /<index>/_doc/<_id>
POST /<index>/_doc/<_id>
示例
json 复制代码
PUT /user/_doc/1
{
  "name": "张三",
  "age": 20,
  "gender": "男"
}
返回结果
json 复制代码
{
  "_index" : "user",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}
特点
  • 使用PUT或POST方法均可
  • 需要手动指定文档ID
  • 如果ID已存在则会覆盖原文档(版本号会增加)
  • 如果ID不存在则创建新文档

1.2 自动生成ID新增文档

语法格式
json 复制代码
POST /<index>/_doc
示例
json 复制代码
POST /user/_doc
{
  "name": "李四",
  "age": 25,
  "gender": "女"
}
返回结果
json 复制代码
{
  "_index" : "user",
  "_type" : "_doc",
  "_id" : "Ax1C123456789abc",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 1,
  "_primary_term" : 1
}
特点
  • 只能使用POST方法
  • Elasticsearch自动生成唯一ID(格式:类似UUID)
  • 避免ID冲突,适合分布式环境
  • 返回结果中包含生成的文档ID

1.3 创建式新增(避免覆盖)

语法格式
json 复制代码
PUT /<index>/_create/<_id>
示例
json 复制代码
PUT /user/_create/1
{
  "name": "王五",
  "age": 30,
  "gender": "男"
}
特点
  • 使用_create端点
  • 只能用于新增,不能用于更新
  • 如果ID已存在会报错(不会覆盖)
ID已存在时的错误返回
json 复制代码
{
  "error": {
    "root_cause": [
      {
        "type": "version_conflict_engine_exception",
        "reason": "[1]: version conflict",
        "index_uuid": "test_index",
        "shard": "0",
        "index": "user"
      }
    ],
    "type": "version_conflict_engine_exception",
    "reason": "[1]: version conflict",
    "index_uuid": "test_index",
    "shard": "0",
    "index": "user"
  },
  "status": 409
}

1.4 新增文档方式对比

方式 端点 ID存在时的行为 result字段 适用场景
PUT + _doc _doc 覆盖更新 created/updated 通用场景,需要更新功能
POST + _doc(指定ID) _doc 覆盖更新 created/updated 通用场景
POST + _doc(无ID) _doc 创建新文档 created 批量导入,无主键数据
PUT + _create _create 报错(409) created 确保不覆盖,强制新增

二、文档更新操作

Elasticsearch提供了多种文档更新方式,从全量覆盖到部分字段更新,再到复杂的脚本更新。

2.1 全量更新文档

2.1.1 使用 _doc 端点全量更新
语法格式
json 复制代码
PUT /<index>/_doc/<_id>
POST /<index>/_doc/<_id>
示例
json 复制代码
PUT /user/_doc/1
{
  "name": "张三",
  "age": 21,
  "gender": "男",
  "city": "北京"
}
返回结果(文档存在时)
json 复制代码
{
  "_index" : "user",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 2,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 1,
  "_primary_term" : 1
}
返回结果(文档不存在时)
json 复制代码
{
  "_index" : "user",
  "_type" : "_doc",
  "_id" : "999",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 5,
  "_primary_term" : 1
}
特点
  • 全量覆盖:会替换整个文档的_source内容
  • 文档不存在时创建:ID不存在时会创建新文档
  • 所有字段必须提供:未提供的字段会丢失
注意事项

⚠️ 使用这种方式更新时,必须提供完整的文档数据。如果只提供部分字段,其他字段会丢失!

json 复制代码
// 原文档
{
  "name": "张三",
  "age": 20,
  "gender": "男"
}

// 只更新age
PUT /user/_doc/1
{
  "age": 21
}

// 结果:只保留了age字段,其他字段丢失!
{
  "age": 21
}

2.2 部分更新文档

2.2.1 使用 _update 端点进行部分更新
语法格式
json 复制代码
POST /<index>/_update/<_id>
示例(使用doc更新字段)
json 复制代码
POST /user/_update/1
{
  "doc": {
    "age": 22
  }
}
返回结果
json 复制代码
{
  "_index" : "user",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 3,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 2,
  "_primary_term" : 1,
  "get" : {
    "found" : true,
    "_source" : {
      "name" : "张三",
      "age" : 22,
      "gender" : "男"
    }
  }
}
特点
  • 部分更新:只更新指定的字段,其他字段保持不变
  • 文档必须存在:文档不存在时会返回错误
  • 返回更新后的文档get字段包含更新后的文档内容
文档不存在时的错误返回
json 复制代码
{
  "error": {
    "root_cause": [
      {
        "type": "document_missing_exception",
        "reason": "[user][1] missing",
        "index_uuid": "test_index",
        "shard": "0",
        "index": "user"
      }
    ],
    "type": "document_missing_exception",
    "reason": "[user][1] missing",
    "index_uuid": "test_index",
    "shard": "0",
    "index": "user"
  },
  "status": 404
}
无实际变更时的返回
json 复制代码
POST /user/_update/1
{
  "doc": {
    "age": 22  // age已经是22,无实际变更
  }
}
json 复制代码
{
  "_index" : "user",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 3,
  "result" : "noop",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 2,
  "_primary_term" : 1,
  "get" : {
    "found" : true,
    "_source" : {
      "name" : "张三",
      "age" : 22,
      "gender" : "男"
    }
  }
}
特点
  • result字段返回"noop"(无操作)
  • 版本号不会增加
  • 分片操作仍然执行
2.2.2 使用 doc_as_upsert 实现存在性检查更新
语法格式
json 复制代码
POST /<index>/_update/<_id>
{
  "doc": { ... },
  "doc_as_upsert": true
}
示例(文档存在时更新,不存在时创建)
json 复制代码
POST /user/_update/999
{
  "doc": {
    "name": "赵六",
    "age": 28,
    "gender": "男"
  },
  "doc_as_upsert": true
}
返回结果(文档不存在时创建)
json 复制代码
{
  "_index" : "user",
  "_type" : "_doc",
  "_id" : "999",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 3,
  "_primary_term" : 1,
  "get" : {
    "found" : true,
    "_source" : {
      "name" : "赵六",
      "age" : 28,
      "gender" : "男"
    }
  }
}
特点
  • 文档存在时:执行更新操作
  • 文档不存在时:使用doc内容创建新文档
  • 类似于"先查询,存在则更新,不存在则插入"的逻辑
2.2.3 使用 upsert 字段实现不同的创建逻辑
语法格式
json 复制代码
POST /<index>/_update/<_id>
{
  "doc": { ... },
  "upsert": { ... }
}
示例(文档存在时执行doc更新,不存在时执行upsert创建)
json 复制代码
POST /user/_update/1000
{
  "doc": {
    "last_login": "2024-08-20T10:30:00Z",
    "login_count": 5
  },
  "upsert": {
    "username": "new_user",
    "created_at": "2024-08-20T10:30:00Z",
    "last_login": "2024-08-20T10:30:00Z",
    "login_count": 1
  }
}
特点
  • 文档存在时 :执行doc部分的更新
  • 文档不存在时 :执行upsert部分的内容创建
  • 适用场景:更新和创建需要不同字段的情况
doc_as_upsert vs upsert 对比
特性 doc_as_upsert upsert
文档存在时 执行doc更新 执行doc更新
文档不存在时 使用doc内容创建 使用upsert内容创建
灵活性 较低 较高
适用场景 更新和创建内容相同 更新和创建需要不同字段

2.3 使用脚本进行复杂更新

2.3.1 基础脚本更新
语法格式
json 复制代码
POST /<index>/_update/<_id>
{
  "script": {
    "source": "...",
    "lang": "painless"
  }
}
示例1:增加年龄
json 复制代码
POST /user/_update/1
{
  "script": {
    "source": "ctx._source.age += params.amount",
    "lang": "painless",
    "params": {
      "amount": 1
    }
  }
}
示例2:添加字段
json 复制代码
POST /user/_update/1
{
  "script": {
    "source": "ctx._source.city = params.city",
    "lang": "painless",
    "params": {
      "city": "上海"
    }
  }
}
示例3:条件更新
json 复制代码
POST /user/_update/1
{
  "script": {
    "source": "if (ctx._source.age > 18) { ctx._source.is_adult = true }",
    "lang": "painless"
  }
}
特点
  • 支持复杂的更新逻辑
  • 可以进行数学运算
  • 可以添加、修改、删除字段
  • 可以使用参数传递值
2.3.2 脚本更新的上下文变量
变量 说明 示例
ctx._source 文档的_source内容 ctx._source.age
ctx.op 操作类型 "index" / "delete" / "none"
params 传递给脚本的参数 params.amount
示例:使用ctx.op控制操作
json 复制代码
POST /user/_update/1
{
  "script": {
    "source": "if (ctx._source.age < 0) { ctx.op = 'delete' } else { ctx._source.age += params.amount }",
    "lang": "painless",
    "params": {
      "amount": 1
    }
  }
}
2.3.3 脚本upsert
语法格式
json 复制代码
POST /<index>/_update/<_id>
{
  "script": {
    "source": "...",
    "lang": "painless"
  },
  "upsert": { ... }
}
示例
json 复制代码
POST /user/_update/1001
{
  "script": {
    "source": "ctx._source.login_count++",
    "lang": "painless"
  },
  "upsert": {
    "username": "user1001",
    "login_count": 1,
    "created_at": "2024-08-20T10:30:00Z"
  }
}
特点
  • 文档存在时:执行脚本更新
  • 文档不存在时:使用upsert内容创建
  • 适用于计数器、累加器等场景
2.3.4 脚本upsert with scripted_upsert
语法格式
json 复制代码
POST /<index>/_update/<_id>
{
  "script": {
    "source": "...",
    "lang": "painless",
    "scripted_upsert": true
  },
  "upsert": { ... }
}
示例
json 复制代码
POST /counter/_update/1
{
  "script": {
    "source": """
      if (ctx.op == 'none') {
        ctx._source.count = 1;
      } else {
        ctx._source.count++;
      }
    """,
    "lang": "painless",
    "scripted_upsert": true
  },
  "upsert": {}
}
特点
  • 文档不存在时也会执行脚本
  • ctx.op == 'none'表示文档不存在
  • 更灵活的控制逻辑

2.4 检测noop(无操作)

2.4.1 使用 detect_noop 参数
语法格式
json 复制代码
POST /<index>/_update/<_id>?detect_noop=true
默认行为
  • detect_noop默认为true
  • 当更新内容与现有内容相同时,返回noop
禁用noop检测
json 复制代码
POST /user/_update/1?detect_noop=false
{
  "doc": {
    "age": 22
  }
}
特点
  • 即使值相同,也会执行更新
  • 版本号会增加
  • result返回updated而非noop

2.5 批量更新操作

2.5.1 使用 _bulk API 批量更新
语法格式
json 复制代码
POST /_bulk
示例
json 复制代码
POST /_bulk
{ "update" : { "_index" : "user", "_id" : "1" } }
{ "doc" : { "age" : 23 } }
{ "update" : { "_index" : "user", "_id" : "2" } }
{ "doc" : { "city" : "深圳" } }
{ "update" : { "_index" : "user", "_id" : "3" } }
{ "doc" : { "status" : "active" } }
返回结果
json 复制代码
{
  "took": 15,
  "errors": false,
  "items": [
    {
      "update": {
        "_index": "user",
        "_type": "_doc",
        "_id": "1",
        "_version": 4,
        "result": "updated",
        "_shards": {
          "total": 2,
          "successful": 1,
          "failed": 0
        },
        "_seq_no": 3,
        "_primary_term": 1,
        "status": 200
      }
    },
    {
      "update": {
        "_index": "user",
        "_type": "_doc",
        "_id": "2",
        "_version": 2,
        "result": "updated",
        "_shards": {
          "total": 2,
          "successful": 1,
          "failed": 0
        },
        "_seq_no": 4,
        "_primary_term": 1,
        "status": 200
      }
    },
    {
      "update": {
        "_index": "user",
        "_type": "_doc",
        "_id": "3",
        "_version": 2,
        "result": "noop",
        "_shards": {
          "total": 2,
          "successful": 1,
          "failed": 0
        },
        "_seq_no": 5,
        "_primary_term": 1,
        "status": 200
      }
    }
  ]
}
特点
  • 一次请求更新多个文档
  • 返回每个更新的结果
  • 通过errors字段判断是否有操作失败
2.5.2 批量更新时的错误处理
示例(部分失败)
json 复制代码
POST /_bulk
{ "update" : { "_index" : "user", "_id" : "1" } }
{ "doc" : { "age" : 23 } }
{ "update" : { "_index" : "user", "_id" : "999" } }
{ "doc" : { "city" : "广州" } }
返回结果(文档不存在)
json 复制代码
{
  "took": 10,
  "errors": true,
  "items": [
    {
      "update": {
        "_index": "user",
        "_type": "_doc",
        "_id": "1",
        "_version": 4,
        "result": "updated",
        "status": 200
      }
    },
    {
      "update": {
        "_index": "user",
        "_type": "_doc",
        "_id": "999",
        "error": {
          "type": "document_missing_exception",
          "reason": "[user][999] missing",
          "index_uuid": "test_index",
          "shard": "0",
          "index": "user"
        },
        "status": 404
      }
    }
  ]
}

2.6 按查询更新(Update By Query)

语法格式
json 复制代码
POST /<index>/_update_by_query
示例
json 复制代码
POST /user/_update_by_query
{
  "query": {
    "match": {
      "city": "北京"
    }
  },
  "script": {
    "source": "ctx._source.region = '华北'",
    "lang": "painless"
  }
}
返回结果
json 复制代码
{
  "took": 45,
  "timed_out": false,
  "total": 100,
  "updated": 100,
  "deleted": 0,
  "batches": 1,
  "version_conflicts": 0,
  "noops": 0,
  "retries": {
    "bulk": 0,
    "search": 0
  },
  "throttled_millis": 0,
  "requests_per_second": -1,
  "throttled_until_millis": 0,
  "failures": []
}
特点
  • 根据查询条件批量更新文档
  • 适用于更新符合特定条件的所有文档
  • 可能会花费较长时间,取决于匹配的文档数量

2.7 更新操作对比总结

更新方式 端点 文档存在时 文档不存在时 适用场景
全量更新 _doc 覆盖更新 创建新文档 需要替换整个文档
部分更新 _update + doc 更新指定字段 报错(404) 只更新部分字段
doc_as_upsert _update + doc + doc_as_upsert 更新指定字段 创建文档(使用doc内容) 更新和创建内容相同
upsert _update + doc + upsert 执行doc更新 创建文档(使用upsert内容) 更新和创建需要不同字段
脚本更新 _update + script 执行脚本 报错(404) 复杂更新逻辑
脚本upsert _update + script + upsert 执行脚本 创建文档(使用upsert内容) 计数器等场景
按查询更新 _update_by_query 批量更新 - 批量更新符合条件的文档

三、文档查询操作

3.1 根据ID查询文档

3.1.1 查询完整文档(包含元数据)
语法格式
json 复制代码
GET /<index>/_doc/<_id>
示例
json 复制代码
GET /user/_doc/1
返回结果(文档存在时)
json 复制代码
{
  "_index" : "user",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "name" : "张三",
    "age" : 20,
    "gender" : "男"
  }
}
返回结果(文档不存在时)
json 复制代码
{
  "_index" : "user",
  "_type" : "_doc",
  "_id" : "999",
  "found" : false
}
3.1.2 仅查询文档数据(不含元数据)
语法格式
json 复制代码
GET /<index>/_source/<_id>
示例
json 复制代码
GET /user/_source/1
返回结果(文档存在时)
json 复制代码
{
  "name" : "张三",
  "age" : 20,
  "gender" : "男"
}
返回结果(文档不存在时)
json 复制代码
{
  "error": {
    "root_cause": [
      {
        "type": "index_not_found_exception",
        "reason": "no such index [user]",
        "index_uuid": "_na_",
        "index": "user"
      }
    ],
    "type": "index_not_found_exception",
    "reason": "no such index [user]",
    "index_uuid": "_na_",
    "index": "user"
  },
  "status": 404
}
3.1.3 HEAD请求检查文档是否存在
检查完整文档是否存在
bash 复制代码
HEAD /user/_doc/1
检查文档数据是否存在
bash 复制代码
HEAD /user/_source/1
返回结果
  • 文档存在时:HTTP 200 OK
  • 文档不存在时:HTTP 404 Not Found
特点
  • HEAD请求只返回文档是否存在
  • 不返回具体内容,节省网络流量
  • 适用于存在性检查场景

3.2 查询字段过滤

3.2.1 仅返回指定字段(_source_includes)
语法格式
json 复制代码
GET /<index>/_source/<_id>?_source_includes=<field1>,<field2>,...
GET /<index>/_doc/<_id>?_source_includes=<field1>,<field2>,...
示例
json 复制代码
GET /user/_source/1?_source_includes=name,age
返回结果
json 复制代码
{
  "name" : "张三",
  "age" : 20
}
3.2.2 排除指定字段(_source_excludes)
语法格式
json 复制代码
GET /<index>/_source/<_id>?_source_excludes=<field1>,<field2>,...
GET /<index>/_doc/<_id>?_source_excludes=<field1>,<field2>,...
示例
json 复制代码
GET /user/_source/1?_source_excludes=gender
返回结果
json 复制代码
{
  "name" : "张三",
  "age" : 20
}
3.2.3 同时使用includes和excludes
示例
json 复制代码
GET /user/_source/1?_source_includes=name,age,gender&_source_excludes=gender
注意事项
  • 当同时使用时,excludes优先级更高
  • 上述示例实际只返回name和age

3.3 批量查询文档

3.3.1 _msearch 端点(多搜索API)
语法格式
json 复制代码
GET /<index>/_msearch
POST /<index>/_msearch
示例
json 复制代码
GET /user/_msearch
{ "query": { "match_all": {} } }
{ "query": { "match": { "name": "张三" } } }
特点
  • 批量执行多个搜索查询
  • 返回结果数组,每个查询对应一个结果
  • 提高查询效率,减少网络往返

3.4 查询操作对比总结

查询方式 端点 返回内容 found字段 适用场景
GET _doc 完整文档+元数据 需要版本信息、检查存在性
GET _source 仅文档数据 仅需要数据内容时
HEAD _doc 仅HTTP状态码 快速检查文档是否存在
HEAD _source 仅HTTP状态码 快速检查文档数据是否存在
GET _msearch 批量查询结果 批量查询场景

3.5 查询操作性能对比

操作 数据传输量 响应速度 推荐场景
GET /_doc/id 大(含元数据) 较快 需要完整信息
GET /_source/id 小(仅数据) 仅需数据
HEAD /_doc/id 极小(仅状态) 最快 存在性检查

四、文档删除操作

4.1 根据ID删除文档

语法格式
json 复制代码
DELETE /<index>/_doc/<_id>
示例
json 复制代码
DELETE /user/_doc/1

4.2 删除操作返回结果

4.2.1 删除已存在的文档
返回结果
json 复制代码
{
  "_index" : "user",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 2,
  "result" : "deleted",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 1,
  "_primary_term" : 1
}
特点
  • result字段返回 "deleted"
  • _version 版本号会增加
  • HTTP状态码:200 OK
  • 文档被标记为删除,稍后会被物理清理
4.2.2 删除不存在的文档
返回结果
json 复制代码
{
  "_index" : "user",
  "_type" : "_doc",
  "_id" : "999",
  "_version" : 1,
  "result" : "not_found",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 2,
  "_primary_term" : 1
}
特点
  • result字段返回 "not_found"
  • _version 版本号仍然会增加
  • HTTP状态码:仍然是200 OK(而非404)
  • 不会报错,操作被视为成功

4.3 删除操作的详细说明

4.3.1 删除机制
  1. 逻辑删除:文档首先被标记为已删除
  2. 版本递增:无论文档是否存在,版本号都会递增
  3. 物理删除:在段合并时才真正从磁盘移除
4.3.2 删除操作的result字段
result值 含义 适用场景
deleted 文档存在且已被删除 成功删除已存在的文档
not_found 文档不存在 尝试删除不存在的文档
4.3.3 删除操作的HTTP状态码
状态码 含义 出现场景
200 OK 删除请求成功 无论文档是否存在,只要请求格式正确
404 Not Found 索引不存在 当索引本身不存在时

4.4 删除操作的注意事项

注意事项 说明
版本号递增 无论文档是否存在,删除操作都会增加版本号
延迟删除 删除的文档不会立即从磁盘移除,等待段合并
result字段 deleted表示成功删除,not_found表示文档不存在
HTTP状态码 无论文档是否存在,都返回200 OK
幂等性 多次删除同一个文档,最终结果一致

4.5 批量删除操作

语法格式
json 复制代码
POST /_bulk
示例
json 复制代码
POST /_bulk
{ "delete" : { "_index" : "user", "_id" : "1" } }
{ "delete" : { "_index" : "user", "_id" : "2" } }
{ "delete" : { "_index" : "user", "_id" : "3" } }
特点
  • 一次请求删除多个文档
  • 返回每个删除操作的结果
  • 即使部分文档不存在,整个操作仍返回200 OK
  • 通过errors字段判断是否有操作失败

4.6 按查询删除(Delete By Query)

语法格式
json 复制代码
POST /<index>/_delete_by_query
示例
json 复制代码
POST /user/_delete_by_query
{
  "query": {
    "match": {
      "status": "inactive"
    }
  }
}
返回结果
json 复制代码
{
  "took": 30,
  "timed_out": false,
  "total": 100,
  "deleted": 100,
  "batches": 1,
  "version_conflicts": 0,
  "noops": 0,
  "retries": {
    "bulk": 0,
    "search": 0
  },
  "throttled_millis": 0,
  "requests_per_second": -1,
  "throttled_until_millis": 0,
  "failures": []
}
特点
  • 根据查询条件批量删除文档
  • 适用于删除符合特定条件的所有文档
  • 可能会花费较长时间,取决于匹配的文档数量

五、实际应用场景示例

5.1 用户信息管理完整流程

场景说明

管理用户账户的完整生命周期,包括创建、查询、更新和删除。

操作示例
1. 新增用户
json 复制代码
PUT /user/_doc/1001
{
  "user_id": 1001,
  "username": "admin",
  "email": "admin@example.com",
  "role": "administrator",
  "created_at": "2024-01-01T00:00:00",
  "status": "active"
}
2. 查询完整用户信息(含元数据)
json 复制代码
GET /user/_doc/1001
3. 仅获取用户数据
json 复制代码
GET /user/_source/1001
4. 仅获取用户名和邮箱
json 复制代码
GET /user/_source/1001?_source_includes=username,email
5. 更新用户邮箱(部分更新)
json 复制代码
POST /user/_update/1001
{
  "doc": {
    "email": "newemail@example.com"
  }
}
6. 更新登录次数(脚本更新)
json 复制代码
POST /user/_update/1001
{
  "script": {
    "source": "ctx._source.login_count++",
    "lang": "painless"
  }
}
7. 检查用户是否存在
bash 复制代码
HEAD /user/_doc/1001
# 返回:200 OK(存在)或 404 Not Found(不存在)
8. 删除用户
json 复制代码
DELETE /user/_doc/1001

5.2 商品信息管理

场景说明

电商系统中的商品信息录入、更新和查询。

操作示例
1. 录入商品(自动生成ID)
json 复制代码
POST /product/_doc
{
  "product_name": "无线蓝牙耳机",
  "category": "电子产品",
  "price": 299.00,
  "stock": 100,
  "tags": ["蓝牙", "无线", "耳机"],
  "description": "高音质蓝牙5.0耳机",
  "created_at": "2024-08-20T10:30:00"
}
2. 查询商品(使用返回的ID)
json 复制代码
GET /product/_doc/Ax7C123456789abc
3. 仅获取价格和库存(用于显示)
json 复制代码
GET /product/_source/Ax7C123456789abc?_source_includes=price,stock,product_name
4. 更新库存(部分更新)
json 复制代码
POST /product/_update/Ax7C123456789abc
{
  "doc": {
    "stock": 95
  }
}
5. 减少库存(脚本更新)
json 复制代码
POST /product/_update/Ax7C123456789abc
{
  "script": {
    "source": "ctx._source.stock -= params.quantity",
    "lang": "painless",
    "params": {
      "quantity": 5
    }
  }
}
6. 检查商品是否售罄(查询库存)
json 复制代码
GET /product/_source/Ax7C123456789abc?_source_includes=stock
7. 商品下架(删除)
json 复制代码
DELETE /product/_doc/Ax7C123456789abc

5.3 日志记录生命周期管理

场景说明

系统日志的收集、查询、更新和定期清理。

操作示例
1. 存储错误日志
json 复制代码
POST /logs/_doc
{
  "timestamp": "2024-08-20T10:30:00Z",
  "level": "ERROR",
  "service": "payment-service",
  "message": "Payment gateway timeout",
  "details": {
    "gateway": "alipay",
    "retry_count": 3,
    "transaction_id": "TXN123456"
  },
  "hostname": "server-01",
  "ip_address": "192.168.1.100"
}
2. 查询特定日志
json 复制代码
GET /logs/_doc/log_id_12345
3. 仅获取日志级别和消息(用于日志列表展示)
json 复制代码
GET /logs/_source/log_id_12345?_source_includes=level,message,timestamp
4. 排除details字段(减少传输量)
json 复制代码
GET /logs/_source/log_id_12345?_source_excludes=details
5. 更新日志处理状态
json 复制代码
POST /logs/_update/log_id_12345
{
  "doc": {
    "status": "resolved",
    "resolved_at": "2024-08-20T11:00:00Z"
  }
}
6. 定期清理旧日志(按时间删除)
json 复制代码
POST /logs/_delete_by_query
{
  "query": {
    "range": {
      "timestamp": {
        "lt": "2024-01-01T00:00:00Z"
      }
    }
  }
}
7. 删除特定日志
json 复制代码
DELETE /logs/_doc/log_id_old

5.4 订单记录管理

场景说明

订单系统的创建、查询、更新和归档。

操作示例
1. 创建订单(指定订单号作为ID)
json 复制代码
PUT /orders/_doc/ORDER20240820001
{
  "order_id": "ORDER20240820001",
  "customer_id": 1001,
  "items": [
    {"product_id": "P001", "quantity": 2, "price": 299.00},
    {"product_id": "P002", "quantity": 1, "price": 99.00}
  ],
  "total_amount": 697.00,
  "status": "pending",
  "created_at": "2024-08-20T10:30:00Z",
  "updated_at": "2024-08-20T10:30:00Z"
}
2. 查询订单详情
json 复制代码
GET /orders/_doc/ORDER20240820001
3. 仅查询订单状态
json 复制代码
GET /orders/_source/ORDER20240820001?_source_includes=status,total_amount
4. 更新订单状态(部分更新)
json 复制代码
POST /orders/_update/ORDER20240820001
{
  "doc": {
    "status": "processing",
    "updated_at": "2024-08-20T10:35:00Z"
  }
}
5. 检查订单是否存在
bash 复制代码
HEAD /orders/_doc/ORDER20240820001
6. 订单完成后归档(删除)
json 复制代码
DELETE /orders/_doc/ORDER20240820001
7. 批量删除已完成的订单
json 复制代码
POST /orders/_delete_by_query
{
  "query": {
    "term": {
      "status": "completed"
    }
  }
}

5.5 计数器/统计应用

场景说明

使用脚本的upsert功能实现计数器。

操作示例
1. 初始化或增加访问计数
json 复制代码
POST /counter/_update/page_views_20240820
{
  "script": {
    "source": "ctx._source.count++",
    "lang": "painless"
  },
  "upsert": {
    "page": "/home",
    "count": 1,
    "date": "2024-08-20"
  }
}
2. 查询计数
json 复制代码
GET /counter/_doc/page_views_20240820

5.6 文档存在性检查应用

场景1:用户注册时检查用户名是否已存在
bash 复制代码
# 检查用户名对应的文档是否存在
HEAD /user/_doc/1001

# 根据返回状态码判断
# 200 OK - 用户已存在
# 404 Not Found - 用户不存在,可以注册
场景2:缓存预热前的数据检查
bash 复制代码
# 检查缓存数据是否存在于ES
HEAD /cache/_doc/cache_key_123

# 存在则从ES加载,不存在则从数据库加载
场景3:分布式锁的实现
json 复制代码
# 1. 尝试创建锁文档(使用_create)
PUT /locks/_create/resource_123
{
  "locked_by": "server-01",
  "locked_at": "2024-08-20T10:30:00Z",
  "ttl": 300
}

# 2. 检查锁是否存在
HEAD /locks/_doc/resource_123

# 3. 释放锁(删除)
DELETE /locks/_doc/resource_123

六、最佳实践建议

6.1 新增文档 - ID选择策略

场景 推荐方式 原因 示例
已有主键数据 指定ID 保持业务逻辑一致性 用户ID、订单号
日志、事件数据 自动生成ID 避免冲突,写入性能更好 系统日志、埋点数据
批量导入 根据数据源决定 有主键用主键,无主键自动生成 数据库迁移
分布式系统 自动生成ID 避免ID冲突,提高并发性 微服务架构
需要防止覆盖 使用_create ID冲突时报错,确保数据安全 重要业务数据

6.2 更新文档 - 选择策略

场景 推荐方式 原因 示例
替换整个文档 全量更新(_doc) 简单直接 用户资料完整更新
只更新部分字段 部分更新(_update + doc) 保留其他字段 更新邮箱、状态等
计数器、累加器 脚本更新 支持数学运算 访问计数、库存扣减
存在则更新,不存在则创建 upsert 一条命令搞定 用户首次登录统计
更新和创建字段不同 upsert(分离doc和upsert) 灵活性高 登录记录初始化
批量更新 _bulk 高效 批量更新状态

6.3 查询文档 - 性能优化

6.3.1 合理选择查询端点
场景 推荐端点 原因
需要版本信息 GET /_doc/id 返回完整元数据
仅需要数据 GET /_source/id 减少数据传输
检查存在性 HEAD /_doc/id 最小网络开销
批量查询 GET /_msearch 减少请求次数
6.3.2 字段过滤优化
json 复制代码
// 仅返回需要的字段
GET /user/_source/1?_source_includes=name,email,phone

// 排除大字段(如文章内容、附件)
GET /article/_source/1?_source_excludes=content,attachments,comments

// 组合使用(实际应用较少)
GET /user/_source/1?_source_includes=name,email,phone&_source_excludes=password
6.3.3 批量查询优化
json 复制代码
// 不推荐:多次单独查询
GET /user/_doc/1
GET /user/_doc/2
GET /user/_doc/3

// 推荐:使用msearch批量查询
GET /user/_msearch
{ "query": { "ids": { "values": ["1", "2", "3"] } } }

6.4 删除文档 - 最佳实践

6.4.1 批量删除优化
json 复制代码
// 不推荐:多次单独删除
DELETE /user/_doc/1
DELETE /user/_doc/2
DELETE /user/_doc/3

// 推荐:使用bulk批量删除
POST /_bulk
{ "delete" : { "_index" : "user", "_id" : "1" } }
{ "delete" : { "_index" : "user", "_id" : "2" } }
{ "delete" : { "_index" : "user", "_id" : "3" } }
6.4.2 删除前检查
bash 复制代码
# 先检查文档是否存在
HEAD /user/_doc/1

# 根据检查结果决定是否删除
# 200 OK → 执行删除
# 404 Not Found → 跳过或记录日志
6.4.3 软删除策略
json 复制代码
// 1. 标记为删除(软删除)
POST /user/_update/1
{
  "doc": {
    "is_deleted": true,
    "deleted_at": "2024-08-20T10:30:00Z"
  }
}

// 2. 查询时过滤已删除文档
GET /user/_search
{
  "query": {
    "bool": {
      "must_not": {
        "term": {
          "is_deleted": true
        }
      }
    }
  }
}

// 3. 定期物理删除标记为删除的文档
POST /user/_delete_by_query
{
  "query": {
    "bool": {
      "must": [
        { "term": { "is_deleted": true } },
        { "range": { "deleted_at": { "lt": "2024-07-01" } } }
      ]
    }
  }
}
6.4.4 大量删除策略
文档数量 推荐方式 原因
少量(<100) DELETE或bulk 简单直接
中量(100-10000) _delete_by_query 按条件批量删除
大量(>10000) 重建索引 删除成本高,重建更快

6.5 通用性能优化建议

6.5.1 批量操作
json 复制代码
// 批量导入
POST /_bulk
{ "index" : { "_index" : "user", "_id" : "1" } }
{ "name" : "张三", "age" : 20 }
{ "index" : { "_index" : "user", "_id" : "2" } }
{ "name" : "李四", "age" : 25 }
{ "index" : { "_index" : "user", "_id" : "3" } }
{ "name" : "王五", "age" : 30 }
6.5.2 刷新间隔设置
json 复制代码
// 大批量导入前设置
PUT /user/_settings
{
  "index": {
    "refresh_interval": "-1"  // 禁用自动刷新
  }
}

// 导入完成后恢复
PUT /user/_settings
{
  "index": {
    "refresh_interval": "1s"  // 恢复默认值
  }
}
6.5.3 副本数优化
json 复制代码
// 大批量导入前设置副本为0
PUT /user/_settings
{
  "index": {
    "number_of_replicas": 0
  }
}

// 导入完成后恢复副本
PUT /user/_settings
{
  "index": {
    "number_of_replicas": 1
  }
}

6.6 错误处理指南

错误类型 HTTP状态码 原因 解决方案
版本冲突 409 并发更新同一文档 使用_versionif_seq_no做乐观锁
ID冲突(_create) 409 使用_create时ID已存在 检查ID是否存在,或改用_doc端点
字段映射错误 400 数据类型与mapping不匹配 提前定义好索引mapping,检查数据格式
索引不存在 404 操作的索引不存在 先创建索引,使用PUT /index
分片失败 503/500 节点故障或网络问题 检查集群健康状态,等待恢复
文档不存在(查询) 404 查询的文档不存在 使用HEAD预检查,或处理not_found
文档不存在(_update) 404 更新的文档不存在 使用doc_as_upsert或upsert
解析错误 400 JSON格式错误 检查JSON语法

6.7 并发控制策略

6.7.1 使用_version进行乐观锁
json 复制代码
// 1. 先查询获取版本号
GET /user/_doc/1
// 返回 _version: 1

// 2. 更新时指定版本号
PUT /user/_doc/1?version=1
{
  "name": "张三更新",
  "age": 21
}

// 如果版本号不匹配,返回409错误
6.7.2 使用if_seq_no和if_primary_term
json 复制代码
// 1. 先查询获取序列号
GET /user/_doc/1
// 返回 _seq_no: 0, _primary_term: 1

// 2. 更新时指定序列号
PUT /user/_doc/1?if_seq_no=0&if_primary_term=1
{
  "name": "张三更新",
  "age": 21
}

// 如果序列号不匹配,返回409错误

七、常用命令速查

7.1 文档新增操作

bash 复制代码
# 指定ID新增文档
PUT /index/_doc/id
POST /index/_doc/id

# 自动生成ID新增
POST /index/_doc

# 创建式新增(ID冲突时报错)
PUT /index/_create/id

# 示例
PUT /user/_doc/1001
{
  "name": "张三",
  "age": 20
}

7.2 文档更新操作

bash 复制代码
# 全量更新文档
PUT /index/_doc/id
POST /index/_doc/id

# 部分更新文档
POST /index/_update/id
{
  "doc": { "field": "new_value" }
}

# 脚本更新
POST /index/_update/id
{
  "script": {
    "source": "ctx._source.field += params.amount",
    "lang": "painless",
    "params": { "amount": 1 }
  }
}

# upsert(存在则更新,不存在则创建)
POST /index/_update/id
{
  "doc": { "last_login": "2024-08-20" },
  "upsert": { "username": "new_user", "created_at": "2024-08-20" }
}

# 按查询条件批量更新
POST /index/_update_by_query
{
  "query": { "match": { "status": "pending" } },
  "script": {
    "source": "ctx._source.status = 'processed'",
    "lang": "painless"
  }
}

7.3 文档查询操作

bash 复制代码
# 查询完整文档(含元数据)
GET /index/_doc/id

# 检查文档是否存在
HEAD /index/_doc/id

# 仅查询文档数据
GET /index/_source/id

# 检查文档数据是否存在
HEAD /index/_source/id

# 查询指定字段(包含)
GET /index/_source/id?_source_includes=field1,field2

# 查询指定字段(排除)
GET /index/_source/id?_source_excludes=field1,field2

# 批量查询
GET /index/_msearch

# 示例
GET /user/_doc/1001
GET /user/_source/1001?_source_includes=name,email
HEAD /user/_doc/1001

7.4 文档删除操作

bash 复制代码
# 删除指定ID文档
DELETE /index/_doc/id

# 批量删除
POST /_bulk
{ "delete" : { "_index" : "index", "_id" : "id" } }

# 按查询条件删除
POST /index/_delete_by_query
{
  "query": {
    "match": {
      "field": "value"
    }
  }
}

# 示例
DELETE /user/_doc/1001
POST /user/_delete_by_query
{
  "query": {
    "range": {
      "created_at": {
        "lt": "2024-01-01"
      }
    }
  }
}

7.5 索引操作

bash 复制代码
# 创建索引
PUT /index

# 查看索引信息
GET /index

# 删除索引
DELETE /index

# 查看索引mapping
GET /index/_mapping

# 查看索引setting
GET /index/_settings

# 示例
PUT /user
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "name": { "type": "text" },
      "age": { "type": "integer" }
    }
  }
}

7.6 批量操作

bash 复制代码
# 批量导入/更新/删除
POST /_bulk

# 示例:批量操作
POST /_bulk
{ "index" : { "_index" : "user", "_id" : "1" } }
{ "name" : "张三", "age" : 20 }
{ "create" : { "_index" : "user", "_id" : "2" } }
{ "name" : "李四", "age" : 25 }
{ "update" : { "_index" : "user", "_id" : "3" } }
{ "doc" : { "age" : 30 } }
{ "delete" : { "_index" : "user", "_id" : "4" } }

八、返回结果字段详解

8.1 新增/更新操作返回字段

字段 类型 说明 示例值
_index String 文档所属的索引名称 "user"
_type String 文档类型,ES 7.x+统一为_doc "_doc"
_id String 文档唯一标识符 "1001"
_version Number 文档版本号,每次更新自动递增 1
result String 操作结果 "created" / "updated" / "noop"
_shards Object 分片复制相关信息 见下表
_seq_no Number 用于并发控制的序列号 0
_primary_term Number 主分片的选举任期 1
get Object 更新后的文档内容(仅_update操作) 见下表
_shards字段说明
字段 说明
total 总分片数(主分片+副本分片)
successful 成功执行的分片数
failed 失败的分片数
get字段示例(_update操作)
json 复制代码
"get": {
  "found": true,
  "_source": {
    "name": "张三",
    "age": 22
  }
}

8.2 查询操作返回字段

字段 类型 说明 示例值
_index String 文档所属的索引名称 "user"
_type String 文档类型 "_doc"
_id String 文档唯一标识符 "1001"
_version Number 文档版本号 1
_seq_no Number 序列号 0
_primary_term Number 主任期 1
found Boolean 文档是否找到 true / false
_source Object 文档的实际数据 见下表
_source字段示例
json 复制代码
{
  "name": "张三",
  "age": 20,
  "gender": "男"
}

8.3 删除操作返回字段

字段 类型 说明 示例值
_index String 索引名称 "user"
_type String 文档类型 "_doc"
_id String 文档ID "1001"
_version Number 版本号(删除后仍递增) 2
result String 操作结果 "deleted" / "not_found"
_shards Object 分片信息 见8.1节
_seq_no Number 序列号 1
_primary_term Number 主任期 1

8.4 result字段所有可能值

操作 result值 含义
新增 created 文档创建成功
全量更新(ID存在) updated 文档更新成功
全量更新(ID不存在) created 文档创建成功
部分更新(有变更) updated 文档部分更新成功
部分更新(无变更) noop 无操作(值未变化)
删除(文档存在) deleted 文档删除成功
删除(文档不存在) not_found 文档不存在
新增(_create,ID已存在) - 返回409错误
部分更新(文档不存在) - 返回404错误

九、注意事项

9.1 ES版本差异

版本 重要变化 影响
ES 7.x _type字段固定为_doc 不再支持自定义type
ES 8.x 默认关闭索引类型 只能使用_doc
ES 7.x+ _create端点仍然支持 可以继续使用
ES 7.x+ _update端点仍然支持 可以继续使用

9.2 ID设计原则

原则 说明 示例
长度控制 ID长度影响性能和存储 推荐不超过64字符
避免过长 过长ID降低查询性能 避免使用UUID作为ID
推荐类型 数值型或短字符串 1001 / ORDER20240820
避免特殊字符 可能导致URL问题 避免使用/?#
业务含义 有业务含义的ID便于调试 USER_1001 / ORDER_20240820_001

9.3 并发控制

并发控制机制对比
机制 字段 精确度 适用场景
版本号 _version 中等 一般并发控制
序列号 _seq_no + _primary_term 高并发场景
删除/更新操作的并发控制
  • 删除/更新操作都会增加版本号
  • 可以使用乐观锁防止误操作
  • 操作前检查版本号或序列号

9.4 数据一致性

方面 说明 调整方式
近实时搜索 默认约1秒延迟 refresh_interval参数
刷新间隔 默认1秒 可调整为-1(禁用)或30s
延迟删除 删除后稍后物理清理 等待段合并
副本同步 默认异步 可调整为wait_for_active_shards

9.5 性能考虑

操作 性能建议
大量写入 禁用refresh、设置副本为0
批量操作 使用bulk API,每批1000-5000个文档
查询优化 使用_source过滤字段,减少传输
大量删除 考虑_delete_by_query或重建索引
大量更新 考虑_update_by_query或重建索引
并发控制 使用if_seq_no而非_version

9.6 更新操作特别注意事项

注意事项 说明
全量更新覆盖 使用_doc更新会替换整个文档,未提供的字段会丢失
部分更新不存在 使用_update时文档不存在会返回404
noop检测 默认开启,相同值更新会返回noop
脚本性能 脚本更新比doc更新慢,复杂逻辑优先使用脚本
版本号递增 noop操作不会递增版本号

9.7 安全注意事项

方面 建议
权限控制 设置合适的索引级别权限
数据验证 写入前验证数据格式
敏感数据 敏感字段加密存储
审计日志 记录重要操作的审计日志
备份策略 定期备份重要索引数据

十、学习资源

10.1 官方文档

资源 地址 说明
官方文档 https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html 完整的ES官方文档
API文档 https://www.elastic.co/guide/en/elasticsearch/reference/current/docs.html 文档API详细说明
Update API https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html 更新API详细说明
中文社区 https://elasticsearch.cn/ Elasticsearch中文社区

10.2 推荐阅读

主题 相关章节
基础概念 CRUD操作、索引管理
性能优化 批量操作、刷新策略、分片管理
并发控制 版本控制、序列号机制
数据建模 Mapping设计、字段类型选择
集群管理 节点配置、健康检查
脚本编程 Painless脚本语法、最佳实践

10.3 实践建议

  1. 学习路径

    • 先掌握基础CRUD操作
    • 学习查询和聚合
    • 理解索引设计和性能优化
    • 掌握集群运维
  2. 实践环境

    • 使用Docker快速搭建本地环境
    • 在测试环境充分验证后再上生产
    • 监控集群健康状态
  3. 问题排查

    • 查看ES日志
    • 使用_cat API查看状态
    • 分析慢查询日志

文档整理时间:2024年8月

基于《3.4 新增文档》、《3.5 查询文档和删除文档》、《3.6 更新文档》笔记整理

Elasticsearch 7.x/8.x 版本适用

相关推荐
醉颜凉1 小时前
Elasticsearch 核心基石:倒排索引全解析(原理+结构+流程图+实战)
elasticsearch·jenkins·流程图
公爵爱学习1 小时前
安装Ubuntu20.04遇到的问题 简要简述笔记
笔记
醉颜凉1 小时前
Elasticsearch底层原理:文档更新与删除完整执行流程深度剖析
大数据·elasticsearch·jenkins
小白说大模型2 小时前
AI驱动的个性化学习路径:知识图谱与知识点关联的存储与推理
大数据·人工智能·学习·mysql·机器学习·prompt·知识图谱
吃好睡好便好2 小时前
三伏天结束了
学习·生活·毕淑敏·心理游戏
Wang's Blog3 小时前
PostgreSQL笔记28: PostgreSQL ACID 特性与实现机制深度解析
大数据·笔记·postgresql
2501_931819703 小时前
方言标注标准化模板迭代优化诉求 泸州话标注公司学习信实翻译模板迭代方法论
学习
u0103055274 小时前
正则表达式find与matches区别详解
笔记
浪兎兎4 小时前
Nginx 笔记
运维·笔记·nginx