我正在索引一组文档(将它们想象为论坛帖子),其中包含一个嵌套对象,该对象是与该帖子相关的用户。我的问题是用户字段可能会更新,但由于帖子没有更改,它们不会重新索引并且用户嵌套对象变得过时。有没有办法在不重新索引整个文档的情况下更新嵌套对象?或者唯一的解决方案是每次用户更改时重新索引用户的所有相关帖子?
3 回答
您可以使用更新 API。
curl -XPOST localhost:9200/docs/posts/post/_update -d '{
"script" : "ctx._source.nested_user = updated_nested_user",
"params" : {
"updated_nested_user" : {"field": "updated"}
}
}'
有关详细信息,请参阅此SO 答案。
请注意,更新脚本支持条件逻辑,如此处所示。因此,您可以在用户更改时标记论坛帖子,然后遍历帖子以仅更新用户更改的帖子。
curl -XPOST 'localhost:9200/docs/posts/post/_update' -d '{
"script" : "ctx._source.tags.contains(tag) ? "ctx._source.nested_user = updated_nested_John" : ctx.op = "none"",
"params" : {
"tag": "updated_John_tag",
"updated_nested_John" : {"field": "updated"}
}
}'
更新
也许我的三元运算符示例不完整。问题中没有提到这一点,但假设用户在应用程序的单独部分更改他们的信息,最好将这些更改应用到一个脚本中的论坛帖子。尝试直接检查用户字段以进行更改,而不是使用标签:
curl -XPOST 'localhost:9200/docs/posts/post/_update' -d '{
"script" : "ctx._source.nested_user.contains(user) ? "ctx._source.nested_user = updated_nested_John" : ctx.op = "none"",
"params" : {
"user": "John",
"updated_nested_John" : {"field": "updated"}
}
}'
但是,如前所述,这可能比重新索引完整帖子要慢。
遗憾的是,elasticsearch 无法在不重新索引整个文档的情况下仅更新文档的一部分。所以,是的,您需要重新索引整个文档以更改嵌套部分。
如果您没有整个文档可以重新发送,您可以使用更新 API发送需要更改的部分,但请注意存在性能问题。
@Scott Rice 关于如何在这种情况下使用部分更新的回答非常有用,而@ramseykhalaf 的回答在某种意义上更正确,因为如果不重新索引这是不可能的。如果我们进行部分更新,无论如何我们都会重新索引整个文档。
然而,取决于对什么是“重新索引”的理解。
如果我们将重新索引定义为“将整个文档重新提交给 ES”——那么我们可以在这个意义上调用部分更新解决方案而不需要重新索引。如果我们将重新索引定义为“重新计算数据结构,以便有效地搜索索引中的更新文档”(这是我理解的更正确的定义),那么它总是会发生。
请注意,部分更新后,文档的整个旧副本将保留在索引中,标记为已删除(直到下一次从头开始重新索引或“优化”)。
为了避免这种情况,可以使用子父关系代替嵌套对象。可以在不接触父文档的情况下添加/删除/更新子级(但这当然有其成本 - 在内存中维护子-父关系林等)。