【README】
0.本文部分内容(数据)总结自 es 开发文档, Document APIs | Elasticsearch Guide [7.2] | Elastic ;
1.本文的es版本是7.2.1;
2.elasticsearch 是一个数据存储,检索和分析引擎;本文介绍的是 es数据存储开发方式;
- es是以文档为单位存储数据的,数据被序列化为json文档进行存储;
3.文档存储包括文档保存,修改,删除;(文档查询的开发方式比较复杂,单独新开一篇阐述)
- 保存文档:使用 put 或 post请求;
- 修改文档:使用put和post请求;
- put请求更新文档是全量替换;
- post请求路径不带 _update 更新文档是 全量替换;
- post请求路径带 _update 更新文档是 部分更新;且 带了 _update 时,会比较新老数据是否相同,若相同则不执行更新,返回noop;
- 删除文档:用delete请求(包括删除文档,删除索引);
【1】保存文档
保存文档可以用put请求或post请求;
- Put 与 post请求保存文档的区别:put必须带文档id,post可以带 或者不带id;
【1.1】put请求保存文档
1) 在customer索引下的 external类型下保存2号文档,文档id设置为2;
Put localhost:9200/customer/external/2{"name":"zhangsan2"
}{"_index": "customer","_type": "external","_id": "2","_version": 1,"result": "created", // 这里表示创建"_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 2,"_primary_term": 1
}
2)再执行一次,result就是更新 updated 的了;
{"_index": "customer","_type": "external","_id": "2","_version": 2,"result": "updated", // 第2次执行就是更新 "_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 3,"_primary_term": 1
}
【1.2】post请求保存文档
1) 在customer索引下的 external类型下保存文档(不带文档id);
Post localhost:9200/customer/external {"name":"zhangsan_post_1"
}{"_index": "customer","_type": "external","_id": "Qb1Gq4MBJc7j47GNcnTa", // 自动生成id,请求路径不需要设置id "_version": 1,"result": "created", // 新增数据"_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 4,"_primary_term": 1
}
【Post请求小结】
- 不带id就是永远新增,带了id的规则是无则新增,有则更新;
2)post请求保存文档,带文档id
Post localhost:9200/customer/external/Qr1Jq4MBJc7j47GNp3RF
{"name":"zhangsan_post_3"
}{"_index": "customer","_type": "external","_id": "Qr1Jq4MBJc7j47GNp3RF","_version": 5, // 版本号递增 "result": "updated", // 更新 "_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 9,"_primary_term": 1
}
【2】es文档简单查询
为了方便验证es文档保存,修改,删除的效果,本文介绍了一个简单查询。
【2.1】get请求带文档id查询文档
Get localhost:9200/customer/external/1
{}{"_index": "customer", // 在哪个索引 "_type": "external", // 在哪个类型 "_id": "1", // 记录id "_version": 2, // 表明数据被更新过,因为新建数据的版本号是1 "_seq_no": 1, // 并发控制字段,每次更新就会+1;用来做乐观锁;"_primary_term": 1, // 同上,主分片重新分配,如重启,就会变化 "found": true, // "_source": { // 真正的内容 "name": "zhangsan"}
}
【2.2】seq_no 和 primary_term 做并发控制(乐观锁)
步骤1)查询id等于1的文档
{"_index": "customer","_type": "external","_id": "1","_version": 4,"_seq_no": 11,"_primary_term": 1,"found": true,"_source": {"name": "zhangsan2_update_seq_11"}
}
步骤2)把 seq_no 和 primary_term 作为更新条件
客户端1:(修改成功)
Post localhost:9200/customer/external/1?if_seq_no=11&if_primary_term=1
{"name":"zhangsan_post_update_c1"
}{"_index": "customer","_type": "external","_id": "1","_version": 5,"result": "updated","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 12,"_primary_term": 1
}
客户端2(修改失败),因为 seq_no 因客户端1修改文档的事件而递增了,即不等于11 :
Post localhost:9200/customer/external/1?if_seq_no=11&if_primary_term=1
{"name":"zhangsan_post_update_c2"
}{"error": {"root_cause": [{"type": "version_conflict_engine_exception","reason": "[1]: version conflict, required seqNo [11], primary term [1]. current document has seqNo [12] and primary term [1]","index_uuid": "IIq46lyCQMisw9_iPDheiw","shard": "0","index": "customer"}],......},"status": 409
}
【3】修改文档
1)post 或 put请求都可以修改文档;
- post请求修改文档又分为 请求路径带 _update 和 不带_update;(post不带_update更新文档是全量替换,带_update 是部分更新)
- put请求修改文档 时的请求路径不能带 _update ;(put更新文档是全量替换)
【3.1】post请求路径带_update更新文档
1)更新文档id为1的文档
Post localhost:9200/customer/external/1/_update
{"doc":{ // 注意 post请求路径带update的更新的请求体不同"name":"post_update_202210061135"}
}{"_index": "customer","_type": "external","_id": "1","_version": 6,"result": "updated","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 13,"_primary_term": 1
}
2)再发送一次相同请求,且报文体也相同,则得到 noop ;
Post localhost:9200/customer/external/1/_update
{"doc":{"name":"post_update_202210061135"}
}{"_index": "customer","_type": "external","_id": "1","_version": 6,"result": "noop", // 没有做任何操作 "_shards": {"total": 0,"successful": 0,"failed": 0}
}
【3.2】post请求路径不带_update更新文档
1)更新id等于1的文档
Post localhost:9200/customer/external/1
{"name":"zhangsan_post_update_noupdate"
}{"_index": "customer","_type": "external","_id": "1","_version": 7,"result": "updated","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 14,"_primary_term": 1
}
2)再发一次相同请求,相同报文体,如下:
{"_index": "customer","_type": "external","_id": "1","_version": 8,"result": "updated","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 15,"_primary_term": 1
}
【小结】
- Post请求路径带update,会检查新老数据是否相同,若相同,则无需更新;即version, seq_no 都不会增加;
- Post请求路径不带update,则不会检查新老数据是否相同,无论是否相同,都会更新;即 version 和 seq_no 都会增加;
【3.3】put请求路径带_update更新文档(API不存在)
put请求更新文档时,其请求路径不能带 _update ;
【3.4】put请求路径不带_update更新文档 (全量替换)
0)初始数据:id为2的文档;
post localhost:9200/customer/external/2
{}
{"_index": "customer","_type": "external","_id": "2","_version": 6,"_seq_no": 8,"_primary_term": 2,"found": true,"_source": {"name": "post_zhangsan_02","addr": "post_addr_02"}
}
1)更新id为2的文档
Put localhost:9200/customer/external/2
{"doc":{"name":"put_update_202210071519" }
}
// 更新后的结果
{"_index": "customer","_type": "external","_id": "2","_version": 7,"_seq_no": 9,"_primary_term": 2,"found": true,"_source": {"doc": {"name": "put_update_202210071519"}}
}
【小结】
- put请求路径不带_update可以更新成功; 不会检查新老数据是否相同,都会更新;
- put请求更新文档是全量覆盖(虽然put请求仅更新了name字段,但结果是addr字段被删除了);
【3.5】post请求为索引新增字段
1)为id为1的文档新增 addr 字段;
Post localhost:9200/customer/external/1
{"doc":{"name":"post_noupdate_202210061151", "addr":"成都高新区01_post_新增属性"}
}{"_index": "customer","_type": "external","_id": "1","_version": 11,"result": "updated","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 18,"_primary_term": 1
}
【3.6】put请求为索引新增字段
1)为id为1的文档新增 addr 字段;
Put localhost:9200/customer/external/1
{"doc":{"name":"post_noupdate_202210061154", "addr":"成都高新区02_put_新增属性"}
}{"_index": "customer","_type": "external","_id": "1","_version": 12,"result": "updated","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 19,"_primary_term": 1
}
【3.7】post请求更新文档时带_update与不带_update的区别
1)post请求路径带_update,仅部分更新(我想大部分场景应该选择带 _update)
- 如文档有字段1,字段2;
- 通过post带update的更新,可以仅更新字段1 或 字段2;
2)post请求路径不带update,是全量覆盖
- 如文档有字段1,字段2;
- 通过post不带update的更新,是全量覆盖;
3)例子(post请求带与不带_update 更新文档的区别):
3.0)初始数据
get localhost:9200/customer/external/2
{}
{"_index": "customer","_type": "external","_id": "2","_version": 6,"_seq_no": 24,"_primary_term": 1,"found": true,"_source": {"name": "zhangsan2_insert","addr": "post_带update_成都04"}
}
3.1)Post请求路径带update的部分更新 ;
Post localhost:9200/customer/external/2/_update
{"doc":{"addr":"post_带update_成都05"}
}// 更新后的结果(部分更新):
{"_index": "customer","_type": "external","_id": "2","_version": 7,"_seq_no": 25,"_primary_term": 1,"found": true,"_source": {"name": "zhangsan2_insert","addr": "post_带update_成都05"}
}
3.2)post请求路径不带_update 的全量替换 ;
Post localhost:9200/customer/external/2
{ "addr":"post_不带update_成都06"
}// 更新后的结果(全量替换):
{"_index": "customer","_type": "external","_id": "2","_version": 8,"_seq_no": 26,"_primary_term": 1,"found": true,"_source": {"addr": "post_不带update_成都06" // (显然,这里是全量替换)}
}
【4】删除数据(删除文档和索引)
【4.1】删除文档
1)删除文档id为2的文档
Delete localhost:9200/customer/external/2{"_index": "customer","_type": "external","_id": "2","_version": 9,"result": "deleted", // 删除成功 "_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 27,"_primary_term": 1
}
2)删除完成后,再次查询id为2的文档,如下:
Get localhost:9200/customer/external/2
{}{"_index": "customer","_type": "external","_id": "2","found": false // 显然没有找到
}
【4.2】删除索引
注意: es中没有提供删除类型的api ;
1)删除customer索引;
Delete localhost:9200/customer{"acknowledged": true
}
2)删除索引文档后,,再次查询(报索引不存在);
Get localhost:9200/customer/external/2
{}
{"error": {"root_cause": [{"type": "index_not_found_exception","reason": "no such index [customer]","resource.type": "index_expression","resource.id": "customer","index_uuid": "_na_","index": "customer"}],...... },"status": 404
}