--- title: "Easysearch 文档操作:数据的增删改查" date: 2026-01-26 lastmod: 2026-01-26 description: "深入讲解 Easysearch 文档的增删改查核心操作,详解 PUT/POST 创建、GET 读取、_update 局部更新、脚本更新、DELETE 删除等操作方式,重点介绍并发控制的乐观锁机制(_seq_no 与 _primary_term),确保数据一致性。" tags: ["文档操作", "CRUD", "并发控制"] summary: "在掌握了环境搭建之后,下一步就是学会如何与 INFINI Easysearch 进行“对话”。 作为一款基于 JSON 的分布式搜索引擎,Easysearch 的交互方式非常直观:使用 HTTP 动词(GET, PUT, POST, DELETE)来操作 JSON 文档。这与现代 Web 开发中的 RESTful API 风格完全一致。 本文将带你通过 Dev Tools 或 Curl 完成最核心的 CRUD(创建、读取、更新、删除)操作,并深入探讨版本控制机制。 1. 创建文档 (Create) # 在 Easysearch 中,写入数据被称为“索引(Indexing)”文档。我们有两种方式来指定文档的唯一标识(_id)。 1.1 自定义 ID 写入 (PUT) # 如果你正在从 MySQL 或其他数据库同步数据,通常希望保持 ID 一致。此时使用 PUT 方法。 # 语法:PUT /<索引名>/_doc/<ID> PUT /users/_doc/1001 { "username": "zhangsan", "age": 28, "role": "admin", "tags": [ "developer", "gamer" ] } 结果:如果 ID 不存在,则创建;如果已存在,则覆盖(版本号增加)。 场景:用户画像、商品信息等具有明确主键的数据。 1." --- 在掌握了环境搭建之后,下一步就是学会如何与 INFINI Easysearch 进行“对话”。 作为一款基于 JSON 的分布式搜索引擎,Easysearch 的交互方式非常直观:**使用 HTTP 动词(GET, PUT, POST, DELETE)来操作 JSON 文档**。这与现代 Web 开发中的 RESTful API 风格完全一致。 本文将带你通过 Dev Tools 或 Curl 完成最核心的 CRUD(创建、读取、更新、删除)操作,并深入探讨版本控制机制。 --- ## 1. 创建文档 (Create) 在 Easysearch 中,写入数据被称为“索引(Indexing)”文档。我们有两种方式来指定文档的唯一标识(`_id`)。 ### 1.1 自定义 ID 写入 (PUT) 如果你正在从 MySQL 或其他数据库同步数据,通常希望保持 ID 一致。此时使用 `PUT` 方法。 ```json # 语法:PUT /<索引名>/_doc/ PUT /users/_doc/1001 { "username": "zhangsan", "age": 28, "role": "admin", "tags": [ "developer", "gamer" ] } ``` - **结果**:如果 ID 不存在,则创建;如果已存在,则**覆盖**(版本号增加)。 - **场景**:用户画像、商品信息等具有明确主键的数据。 ### 1.2 自动生成 ID 写入 (POST) 如果你的数据是日志或流水,没有天然的主键,可以让 Easysearch 自动生成一个 Base64 编码的 UUID。注意这里必须用 `POST`。 ```json # 语法:POST /<索引名>/_doc/ POST /logs/_doc/ { "timestamp": "2023-10-27T10:00:00Z", "message": "User login success" } ``` - **结果**:返回结果中会包含自动生成的 `_id`,如 `W0tPsHcB...`。 --- ## 2. 读取文档 (Read) 读取操作非常简单,只需要知道 ID 即可。 ### 2.1 根据 ID 获取 ```json GET /users/_doc/1001 ``` ### 2.2 检查文档是否存在 (HEAD) 如果你只想知道文档还在不在,不需要看具体内容(节省带宽),可以用 `HEAD` 方法。 ```bash curl -I HEAD "http://localhost:9200/users/_doc/1001" ``` - **返回 200 OK**:存在。 - **返回 404 Not Found**:不存在。 ### 2.3 只获取部分字段 (\_source) 如果文档非常大(例如包含长文本),但你只需要查看用户名,可以使用 `_source` 参数过滤。 ```json GET /users/_doc/1001?_source=username,role ``` --- ## 3. 更新文档 (Update) 这是初学者最容易混淆的部分。在 Easysearch 内部,**文档是不可变的(Immutable)**。所谓的“更新”,本质上都是“标记删除旧文档 + 写入新文档”。 ### 3.1 全量替换 (PUT) 如果你再次对同一个 ID 执行 `PUT`,Easysearch 会用新的 JSON **完全替换** 旧的 JSON。 ```json # 警告:如果不包含 tags 字段,原有的 tags 数据将丢失! PUT /users/_doc/1001 { "username": "zhangsan", "age": 29 } ``` ### 3.2 局部更新 (\_update API) 在实际业务中,我们往往只想修改一个字段(例如把年龄改为 30),而不希望重传整个文档。这时必须使用 `_update` 端点。 ```json # 语法:POST /<索引名>/_update/ # 可以新增字段 POST /users/_update/1001 { "doc": { "age": 30, "email": "zhangsan@example.com" } } ``` - **原理**:Easysearch 内部会自动先检索旧文档,修改内存中的 JSON,然后重新索引。 - **优势**:减少了网络传输量,且更符合业务逻辑。 ### 3.3 脚本更新 (Scripted Update) Easysearch 支持使用 Painless 脚本进行复杂的逻辑更新。例如,给年龄 +1,而不是设置为固定值: ```json POST /users/_update/1001 { "script": { "source": "ctx._source.age += params.count", "lang": "painless", "params": { "count": 1 } } } ``` --- ## 4. 删除文档 (Delete) ```json DELETE /users/_doc/1001 ``` - **注意**:删除是“逻辑删除”。文档只是被标记为 `.del`,数据依然占用磁盘空间,直到段合并(Segment Merge)发生时才会被物理清理。 --- ## 5. 进阶:并发控制 (Optimistic Concurrency Control) 在 Web 系统中,可能会出现这种情况: 1. 管理员 A 读取了用户数据(Age=20)。 2. 管理员 B 也读取了用户数据(Age=20)。 3. A 把 Age 改为 21 并保存。 4. B 把 Age 改为 30 并保存。 如果不做控制,B 的操作会覆盖 A 的操作,导致数据不一致(丢失更新问题)。 ### 传统方案:\_version (已不推荐用于并发控制) 虽然每个文档都有 `_version`,但它是内部计数器,在分布式高并发环境下不可靠。 ### 最佳实践:\_seq_no 和 \_primary_term Easysearch 使用 **序列号(Sequence Number)** 和 **主分片任期(Primary Term)** 来实现乐观锁。 **第一步:读取时获取当前状态** ```json GET /users/_doc/1001 ``` 返回结果中包含: ```json { "_seq_no": 15, "_primary_term": 2, "_source": { ... } } ``` **第二步:更新时带上条件** 只有当数据库里的 `_seq_no` 和 `_primary_term` 与我传进去的一致时,才允许修改。 ```json PUT /users/_doc/1001?if_seq_no=15&if_primary_term=2 { "username": "zhangsan", "age": 31 } ``` **结果推演:** - 如果在此期间没有其他人修改,更新成功,`_seq_no` 变为 16。 - 如果已经被管理员 A 修改过了(`_seq_no` 变成了 16),你的请求会直接失败,返回 **409 Conflict**。 - **你的应用逻辑**:捕获 409 错误 -> 重新读取最新数据 -> 提示用户或重新计算 -> 再次尝试提交。 ## 总结 1. **PUT vs POST**:有 ID 用 PUT,没 ID 用 POST。 2. **PUT vs \_update**:PUT 是全量覆盖(小心丢字段),`_update` 是局部修改**(推荐)**。 3. **Delete**:只是打上删除标记,空间不会立即释放。 4. **并发安全**:在关键业务(如库存扣减、金额修改)中,务必使用 `if_seq_no` 和 `if_primary_term` 防止数据冲突。 熟练掌握这些 CRUD 操作,是开发 Easysearch 应用的坚实基础。