本文基于elasticsearch7.3.0版本。
elasticsearch中create、index、update都可以实现插入功能,但是实现原理并不相同。
由上面思维导图可以清晰的看出create、index的大致区别,下面我们来验证下思维导图中的场景:
1、首先明确一点:如何指定是create操作还是index操作?可以通过在ES DSL指令后面拼接op_type=create
或_create
实现。
例:假设目前我有一个索引为my_index,现在要向ES中索引一条doc,并指定是create操作:
POST my_index/_doc/1?op_type=create
{
"tag":"指定id为1,并指定为create操作"
}
# 上面请求等价于
POST my_index/_doc/1/_create
{
"tag":"指定id为1,并指定为create操作"
}
声明:文章后面内容中,所有演示的指令均省略了后面具体doc的内容,请知悉,如下图所示。
2、思考并验证:当向ES中索引一条doc,执行对应DSL指令时,ES底层默认触发什么操作?
场景1)向ES中索引一条数据,没有指定id,执行指令POST my_index/_doc
时:
当您在指令中不提供文档ID时,Elasticsearch会自动生成一个唯一的文档ID,并使用该ID进行create操作。
场景2)向ES中索引一条数据,指定的id不存在,执行指令POST my_index/_doc/1
(doc id为1的文档不存在)时:
当您在指令中指定的文档ID不存在时,则会使用指定的文档ID来执行create操作。
场景3)向ES中索引一条数据,指定了id并且id存在,执行指令POST my_index/_doc/1
(doc id为1的文档存在)时:
当您在指令中指定的文档ID已经存在时,则会使用指定文档ID来执行index操作,更新该文档。这是因为index操作在存在相同文档ID时会执行更新操作(版本号在原有基础上+1),而不是创建新文档。
场景4.)向ES中索引一条数据,指定了id并且id存在,并指定了版本号,执行指令POST my_index/_doc/1?version=7&version_type=external
时:
上述指令在Elasticsearch中,假设ID为1的文档已经存在,则会执行Elasticsearch的index操作。
在这个请求中,通过指定version参数为7和version_type参数为external,您告诉Elasticsearch在执行index操作时,将指定的版本号与文档的当前版本号进行比较。如果指定的版本号与当前版本号符合匹配规则,则会执行更新操作,否则会返回版本冲突错误。
补充:DSL中如何指定一个版本号?POST my_index/_doc/1?version=7&version_type=external_gte
注意:如果DSL指令如果指定了版本号,那么必须指定doc id,否则会报错。
3、下面我们演示思维导图中几种执行报错的场景:
场景1)执行create操作时,指定doc id并且id存在时,会报错。执行POST my_index/_doc/1?op_type=create
指令,执行结果如下:[1] :版本冲突,文档已存在(当前版本[7])
场景2)执行create操作时,指定外部版本号时,会报错。那么执行POST my_index/_doc/1?op_type=create&version=8&version_type=external
指令,执行结果如下:验证失败:1:创建操作仅支持内部版本控制。改为使用索引;
场景3)通过index更新数据时,指定的外部版本号没有超过当前版本号时,会报错。
先执行GET my_index/_doc/1
指令,查看doc id为1的数据对应的version,可以看到id为1的doc,version为7。
当我们执行指令POST my_index/_doc/1?version=7&version_type=external
时(指定了版本号没有指定op_type时,默认就是index操作),执行结果如下:[1] :版本冲突,当前版本[7]高于或等于提供的版本[7]
在Elasticsearch的DSL指令中,可以使用以下方式来更新文档:
1、使用update指令:update指令用于更新指定文档的内容。更新可以是部分更新或完整替换,具体取决于您提供的更新内容。下面是一个示例:
其中,index_name是索引名称,doc_id是要更新的文档ID,field1是要更新的字段名称,new_value是要更新的字段值。
POST /index_name/_update/doc_id
{
"doc": {
"field1": "new_value"
}
}
使用doc_as_upsert参数:如果要更新的文档doc_id不存在,您可以通过设置doc_as_upsert参数为true来执行全量覆盖操作。此时Elasticsearch会将doc参数中的内容作为新文档插入索引。下面是一个示例:
POST /index_name/_update/doc_id
{
"doc": {
"field1": "new_value"
},
"doc_as_upsert": true
}
2、使用update_by_query指令:update_by_query指令用于根据查询条件批量更新文档。您可以在查询条件中指定要更新的文档范围,然后提供要进行更新的内容。下面是一个示例:
POST /index_name/_update_by_query
{
"query": {
"match": {
"field1": "value"
}
},
"script": {
"source": "ctx._source.field1 = 'new_value'"
}
}
除了上述示例中的方式,还可以使用其他的更新方式,如通过script脚本来指定更新逻辑,或者使用upsert选项来指定如果文档不存在时要执行的操作。
请注意,具体的更新语法和选项可能会因Elasticsearch的版本而有所不同。建议参考官方文档或特定版本的API文档以获取准确的语法和选项。
1、在Elasticsearch中,如何选择使用index还是update进行doc更新?
index操作:使用index操作时,无论文档是否已存在,都会将提供的文档数据进行索引。如果指定的文档ID已存在,将会更新该文档的内容。这意味着index操作既可以用于创建新文档,也可以用于更新现有文档。
update操作:使用update操作时,可以对现有文档进行部分更新,而不是替换整个文档。通过update操作,您可以指定要应用的更新脚本或部分文档,以及如何更新现有文档的字段。这种方式更适合于需要对文档进行增量更新的情况。
因此,您可以根据具体的需求来选择使用index操作还是update操作。如果您希望完全替换文档或创建新文档,可以使用index操作。如果您只需要对文档的部分内容进行更新,可以选择update操作。
2、Elasticsearch的update和Lucene的update有哪些区别?
粒度不同:Lucene的update操作是底层索引库的操作,它以文档为单位进行更新。而Elasticsearch的update操作是在更高级别的抽象上进行的,可以对文档的部分内容进行更新。
更新方式不同:Lucene的update操作是通过先删除原始文档,再插入新文档来实现更新。而Elasticsearch的update操作可以通过使用更新脚本、部分文档或者提供的更新内容,对现有文档进行增量更新。
并发处理不同:Lucene的update操作是在单个节点上执行的,不支持分布式并发更新。而Elasticsearch的update操作是分布式的,可以在多个节点上并发执行更新操作。
功能扩展性不同:Elasticsearch的update操作提供了更丰富的功能和灵活性,如支持脚本更新、条件更新、局部更新等。而Lucene的update操作相对较为基础,功能较为有限。
总的来说,Lucene的update操作是底层索引库的原子操作,而Elasticsearch的update操作是在Lucene之上进行的更高级别的操作,提供了更多的功能和灵活性,适用于分布式环境下的文档更新需求。
3、ES 的DSL指令什么时候会使用Lucene的更新操作?
在Elasticsearch的DSL指令中,并不会直接使用Lucene的更新操作。Elasticsearch的DSL指令是在更高级别的抽象上操作的,当您使用Elasticsearch的DSL指令(如update、update_by_query等)来更新文档时,Elasticsearch解析DSL指令,并根据指令中提供的更新内容,在内部生成相应的Lucene更新操作。
虽然在DSL指令中没有直接使用Lucene的更新操作,但是Elasticsearch底层的引擎是基于Lucene的,它会利用Lucene的功能来实现文档的更新。