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





