一、文档
最顶层结构或者根对象(root object)序列化成的JSON数据(以唯一ID标识并存储于Elasticsearch中),可以认为对象(object)和文档(document)是等价相通的。
文档元数据
节点 | 说明 |
---|---|
_index | 文档存储的地方 |
_type | 文档代表的对象的类 |
_id | 文档的唯一标识 |
_index
索引(index)类似于关系型数据库里的“数据库”——它是我们存储和索引关联数据的地方。数据被存储和索引在分片(shards)中, 索引只是一个把一个或多个分片分组在一起的逻辑空间。索引名必须是全部小写, 不能以下划线开头, 不能包含逗号
_type
在关系型数据库中, 我们经常将相同类的对象存储在一个表里, 因为它们有着相同的结构。 同理, 在Elasticsearch中, 我们使用相同类型(type)的文档表示相同的“事物”, 因为他们的数据结构也是相同的
_id
id仅仅是一个字符串, 它与 _index 和 _type 组合时, 就可以在ELasticsearch中唯一标识一个文档。 当创建一个文档, 你可以自定义 _id , 也可以让Elasticsearch帮你自动生成
二、文档CRUD操作
1. 索引文档
指定id创建文档 ,使用put
put /website/blog/1
{
"title":"first blog",
"date":"2019/10/28"
}
响应
{
"_index": "website",
"_type": "blog",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": true
}
响应指出请求的索引已经被成功创建, 这个索引中包含 _index 、 _type 和 _id 元数据,版本号是1
注意使用put并不能保证每次都是新建文档,如果再次put相同id的文档,会对原有的文档进行覆盖。
PUT /website/blog/1
{
"title": "three blog",
"feel":"nice",
"date": "2019/10/28"
}
//响应
{
"_index": "website",
"_type": "blog",
"_id": "1",
"_version": 2, //版本号自增
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": false //表示不是新建,而是覆盖
}
在内部, Elasticsearch已经标记旧文档为删除并添加了一个完整的新文档。 旧版本文档不会立即消失, 但你也不能去访问它。 Elasticsearch会在你继续索引更多数据时清理被删除的文档
不指定id,使用post
post /website/blog
{
"title":"second blog",
"date":"2019/10/28"
}
//响应
{
"_index": "website",
"_type": "blog",
"_id": "AW4TC7FgHfHdzvmX4JMA",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": true
}
自动生成id:AW4TC7FgHfHdzvmX4JMA,每次都是新建文档,不会覆盖
自动生成的id,长度为20个字符,URL安全,base64编码,GUID,分布式系统并行生成时不可能会发生冲突
使用put创建新文档
PUT /website/blog/1?op_type=create
{
"title": "four blog",
"feel":"nice",
"date": "2019/10/28"
}
//响应
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[blog][1]: version conflict, document already exists (current version [2])",
"index_uuid": "oqUDP19MSL2Y9XmPxCf1cA",
"shard": "3",
"index": "website"
}
],
"type": "version_conflict_engine_exception",
"reason": "[blog][1]: version conflict, document already exists (current version [2])",
"index_uuid": "oqUDP19MSL2Y9XmPxCf1cA",
"shard": "3",
"index": "website"
},
"status": 409
}
存在不会进行覆盖,报409错误
也可以这样写,效果和上面相同
PUT /website/blog/3/_create
{
"title": "five blog",
"feel":"nice",
"date": "2019/10/28"
}
2. 检索文档
使用Get
GET /website/blog/1
{
"_index": "website",
"_type": "blog",
"_id": "1",
"_version": 1,
"found": true,
"_source": {
"title": "first blog",
"date": "2019/10/28"
}
}
响应包含了现在熟悉的元数据节点, 增加了 _source 字段, 它包含了在创建索引时我们发送给Elasticsearch的原始文档
检索文档部分内容
get /website/blog/1?_source=title
{
"_index": "website",
"_type": "blog",
"_id": "1",
"_version": 1,
"found": true,
"_source": {
"title": "first blog"
}
}
只需要返回source字段
get /website/blog/1/_source
{
"title": "first blog",
"date": "2019/10/28"
}
查询对应索引下所有文档
get /website/_search
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1,
"hits": [
{
"_index": "website",
"_type": "blog",
"_id": "AW4TC7FgHfHdzvmX4JMA",
"_score": 1,
"_source": {
"title": "second blog",
"date": "2019/10/28"
}
},
{
"_index": "website",
"_type": "blog",
"_id": "1",
"_score": 1,
"_source": {
"title": "first blog",
"date": "2019/10/28"
}
}
]
}
}
字段解释:
took
整个搜索请求花费的毫秒数。
timeout
查询超时与否,如果响应速度比完整的结果更重要,可以指定超时时间
get /website/_search?timeout=10ms
shards
- total 字段 参与查询的分片数
- successful 字段 有多少是成功的
- failed 字段 有多少的是失败的
hits
total 字段 表示匹配到的文档总数
hits 数组包含了匹配到的前10条数据,hits 数组中的每个结果都包含 _index 、 _type 和文档的 _id 字段,每个节点都有一个 _score 字段, 这是相关性得分(relevance score), 它衡量了文档与查询的匹配程度。 默认的, 返回的结果中关联性最大的文档排在首位; 这意味着, 它是按照 _score 降序排列的。max_score 指的是所有文档匹配查询中 _score 的最大值
3. 修改文档
-
put是对原有文档的覆盖,使用新的文档替换旧文档
-
update API对文档可以进行局部更新
post /website/blog/1/_update
{
"doc":{
"action":"go to the moives",
"date":"2019/10/29"
}
}
get /website/blog/1
{
"_index": "website",
"_type": "blog",
"_id": "1",
"_version": 5,
"found": true,
"_source": {
"title": "one blog",
"feel": "nice",
"date": "2019/10/29",
"action": "go to the moives"
}
}
最简单的 update 请求表单接受一个局部文档参数 doc , 它会合并到现有文档中,存在的标量字段被覆盖, 新字段被添加。例如上述例子新加了action字段,修改了date值
可以使用Groovy实现局部更新,我们可以在脚本中写各种逻辑,然后执行即可。
4. 删除文档
DELETE /website/blog/AW4TDmKTHfHdzvmX4JMC
{
"found": true,
"_index": "website",
"_type": "blog",
"_id": "AW4TDmKTHfHdzvmX4JMC",
"_version": 2,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}
删除一个文档也不会立即从磁盘上移除, 它只是被标记成已删除。 Elasticsearch
将会在你之后添加更多索引的时候才会在后台进行删除内容的清理。
未找到对应删除文档,删除失败
DELETE /website/blog/AW4
{
"found": false,
"_index": "website",
"_type": "blog",
"_id": "AW4",
"_version": 1,
"result": "not_found",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}
三、版本控制与更新冲突
对于多用户的局部更新,由于网络原因,可能会出现旧值覆盖新值的情况,或者多线程常见的问题。
ES使用乐观版本并发控制,为每个文档指定版本号,通过CAS比较,如果不是期望的版本号,也就是说中间被人修改过该文档,就让本次更新请求失败
使用内部版本号
PUT /website/blog/3?version=2
{
"title": "five blog",
"feel":"sad",
"date": "2019/10/29"
}
//响应
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[blog][3]: version conflict, current version [1] is different than the one provided [2]",
"index_uuid": "lhDIRTZ7S9mYFUIj9z6AcA",
"shard": "4",
"index": "website"
}
],
"type": "version_conflict_engine_exception",
"reason": "[blog][3]: version conflict, current version [1] is different than the one provided [2]",
"index_uuid": "lhDIRTZ7S9mYFUIj9z6AcA",
"shard": "4",
"index": "website"
},
"status": 409
}
文档3的版本号是1,这里传入的version参数值为2,所以发生冲突,更新失败
注意,这里传入参数version值必须和文档现有的version值完全相等,精确匹配,否则失败
使用外部版本号
PUT /website/blog/4?version=3&version_type=external
{
"title": "six blog",
"feel":"funny",
"date": "2019/10/29"
}
指定该文档的版本为4,外部版本号必须是整数, 大于0小于 9.2e+18
PUT /website/blog/4?version=3&version_type=external
{
"title": "six blog",
"feel":"funny",
"date": "2019/10/29"
}
//响应
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[blog][4]: version conflict, current version [3] is higher or equal to the one provided [2]",
"index_uuid": "lhDIRTZ7S9mYFUIj9z6AcA",
"shard": "2",
"index": "website"
}
],
"type": "version_conflict_engine_exception",
"reason": "[blog][4]: version conflict, current version [3] is higher or equal to the one provided [2]",
"index_uuid": "lhDIRTZ7S9mYFUIj9z6AcA",
"shard": "2",
"index": "website"
},
"status": 409
}
报错的原因是因为外部版本号要求更新传入的version > 当前文档版本号,而不是精确匹配,否则报错,这里讲version值传入4即可成功更新