Spring Data Elasticsearch(4.x)-使用@Id会强制_id字段在_source中出现

8

摘要

最近我们升级到了Spring Data Elasticsearch 4.x。此次主要发布的一部分意味着我们不再使用Jackson将我们的领域对象转换为json(改用MappingElasticsearchConverter)[1]。这意味着我们现在必须为所有文档添加一个新的id字段。

以前,我们的领域对象如下:

import org.springframework.data.annotation.Id;

public ESDocument {
    @Id
    private String id;

    private String field1;

    @JsonIgnore
    public String getId() {
        return id;
    }

    public String getField1() {
        return field1;
    }

这导致了ES中像这样的文档:
{
  "_index" : "test_index",
  "_type" : "_doc",
  "_id" : "d5bf7b5c-7a44-42f9-94d6-d59fe3988482",
  "_score" : 1.0,
  "_source" : {
    "field1" : "blabla"
  }
}

注意:
1. `@JsonIgnore` 注解用于确保我们不必在 `_source` 中拥有一个 `id` 字段。 2. 我们自己设置文档的 id,并且它最终在 `_id` 中。
问题
使用 Spring Data Elastic 4.x,`@JsonIgnore` 注解不再受到尊重,这意味着我们现在被迫在 `_source` 中拥有一个 `id` 字段,如下所示:
{
  "_index" : "test_index",
  "_type" : "_doc",
  "_id" : "d5bf7b5c-7a44-42f9-94d6-d59fe3988482",
  "_score" : 1.0,
  "_source" : {
    "id": "d5bf7b5c-7a44-42f9-94d6-d59fe3988482",
    "field1" : "blabla"
  }
}

问题

  1. 现在是否不再可能省略文档标识符的重复(即在_idid字段中)?如果可以,如何操作?(请注意,我们已经尝试了@org.springframework.data.annotation.Transient,但它无法正常工作,因为spring-data-elastic认为我们的文档没有id)。
  2. 我们之前抑制_source中的id字段的方法是错误或有问题吗?

版本

java:1.8.0_252
elasticsearch:7.6.2
spring-boot:2.3.1.RELEASE
spring-data-elastic:4.0.1.RELEASE

参考资料

[1] - https://spring.io/blog/2020/05/27/what-s-new-in-spring-data-elasticsearch-4-0


id字段将始终存在于文档中。但是,如果您不想要它,则可以从映射中删除它,或者您可以使用查询来获取文档并排除id字段。参考:https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-source-field.html#include-exclude - Harshit
感谢@Harshit。像我在问题中说的那样,到目前为止,我们的文档中没有_source.id字段。谢谢链接。 - Oliver Henlich
1个回答

8

问题 1:

如果要从_source中省略id字段,通常会使用@Transient注释,但正如您所写的那样,这对于id属性不起作用。Spring Data模块(不仅限于Spring Data Elasticsearch)会忽略瞬态属性。

但是,您可以使用org.springframework.data.annotation.ReadOnlyProperty注释来实现此目的:

@Id
@ReadOnlyProperty
private String id;

坦白说,直到现在我也不知道这个存在,这是来自Spring Data Commons的,当属性由MappingElasticsearchConverter写入时,在属性的isWriteable()方法中进行检查。
问题2:虽然不是错误,但是有问题,就像你发现的那样。我们总是在存储整个实体时考虑它的全部,所以我们从未考虑过不写入id。严格地说,这并不是必要的,你是对的,因为我们总是在_id字段和_source一起获得id,所以我们可以轻松地将实体重新组合起来,但是我们从未认为这是一个必要的功能。
注:
当您查看ES索引中的数据时,您会发现使用MappingElasticsearchConverter将写入一个名为_class的附加_source字段,其中包含实体类的名称(或定义的别名)。这允许映射泛型;有关更多信息请查阅文档-以防您想知道这来自哪里。 编辑于2022年11月18日: 最近(使用版本4.4.3),我们进行了更改,修复了Spring Data Elasticsearch中错误的行为:Spring Data Elasticsearch不能将数据写入标记有@ReadOnlyProperty的属性。这导致所提出的解决方案不再起作用,因为在从Elasticsearch读取数据时,id属性不再填充。
要在这种情况下设置id属性,必须向应用程序添加一个AfterConvertCallback
#import org.springframework.data.elasticsearch.core.event.AfterConvertCallback;

@Component
public class EntityAfterConvertCallback implements AfterConvertCallback<EsDocument> {

    @Override
    public EsDocument onAfterConvert(EsDocument entity, Document document, IndexCoordinates indexCoordinates) {
        entity.setId(document.getId());
        return entity;
    }
}

非常感谢 @P.J.Meisch 详细且有用的回复!我们会尝试您的建议并告知结果。 - Oliver Henlich
嗨@P.J.Meisch,我可以确认你的建议有效。再次感谢你的帮助! - Oliver Henlich
很高兴我能帮到你。 - P.J.Meisch
嗨@P.J.Meisch,感谢您在2022年11月18日的更新。我们正在升级我们的依赖关系。您建议使用AfterConvertCallback仅适用于我们读取文档时(即它允许我们读取文档ID并填充我们的对象)。然而,当我们保存新文档时,我们无法使用由Elasticsearch生成的ID填充我们的对象。理想情况下,我们可以使用AfterSaveCallback来提取此ID,但这似乎不可能。 - Oliver Henlich
Spring Data Elasticsearch 5.1将在@Document注释中添加属性storeIdInSource,因此这些解决方法不再必要。 - P.J.Meisch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接