使用Spring Data Elasticsearch存储库进行部分更新

10

我有一个文档,其中包含许多字段(一些嵌套),已在 Elasticsearch 上进行了索引。例如:

{
  "id" : 1,
  "username" : "...",
  "name" : "...",
  "surname" : "...",
  "address" : "...",
  "age": 42,
  ...
  "bookmarks" : [{...}, {...}],
  "tags" : [{...}, {...}]
}

我只想映射实体中的某些字段(不想映射整个文档):

@Document(indexName = "...", type = "...")
public class User {
  @Id
  private int id;
  private String username;
  private String address;

  // getter/setter methods

}

在服务类中,我想使用ElasticsearchRepository进行部分更新,而不需要映射实体中的所有文档字段:
public class UserServiceClass {

  @Autowired
  private UserElasticsearchRepository userElasticsearchRepository;

  public void updateAddress(int id, String updatedAddress) {
    User user = userElasticsearchRepository.findOne(id);
    user.setAddress(updatedAddress);
    userElasticsearchRepository.save(user);
  } 
}

保存方法会覆盖整个文档:

{
  "id" : 1,
  "username" : "...",
  "address" : "..."
}

ElasticsearchRepository似乎不支持部分更新。因此,我使用ElasticsearchTemplate进行部分更新,例如:

public class UserServiceClass {

  @Autowired
  private UserElasticsearchRepository userElasticsearchRepository;

  @Autowired
  private ElasticsearchTemplate elasticsearchTemplate;

  public void updateAddress(int id, String updatedAddress) {
    User user = userElasticsearchRepository.findOne(id);
    if (user.getUsername().equals("system")) {
      return;
    }

    IndexRequest indexRequest = new IndexRequest();
    indexRequest.source("address", updatedAddress);
    UpdateQuery updateQuery = new UpdateQueryBuilder().withId(user.getId()).withClass(User.class).withIndexRequest(indexRequest).build();
    elasticsearchTemplate.update(updateQuery);
  } 
}

但是拥有两个类似的引用(repository和ElasticsearchTemplate)似乎有些冗余。

有人能给我提供更好的解决方案吗?

2个回答

17

不要将ElasticsearchTemplateUserElasticsearchRepository都注入到UserServiceClass中,而是可以实现自己的自定义存储库,让现有的UserElasticsearchRepository扩展它。

我假设你现有的UserElasticsearchRepository看起来像这样。

public interface UserElasticsearchRepository extends ElasticsearchRepository<User, String> {
   ....
}

您需要创建新的接口名称UserElasticsearchRepositoryCustom。在此接口中,您可以列出自定义查询方法。

public interface UserElasticsearchRepositoryCustom {

    public void updateAddress(User user, String updatedAddress);

}

然后实现你的UserElasticsearchRepositoryCustom,创建一个名为UserElasticsearchRepositoryImpl的类,并在其中实现你的自定义方法,注入ElasticsearchTemplate

public class UserElasticsearchRepositoryImpl implements UserElasticsearchRepositoryCustom {

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Override
    public void updateAddress(User user, String updatedAddress){
        IndexRequest indexRequest = new IndexRequest();
        indexRequest.source("address", updatedAddress);
        UpdateQuery updateQuery = new UpdateQueryBuilder().withId(user.getId()).withClass(User.class).withIndexRequest(indexRequest).build();
        elasticsearchTemplate.update(updateQuery);
    }
}

然后,只需将您的UserElasticsearchRepository扩展为UserElasticsearchRepositoryCustom,使其看起来像这样。

public interface UserElasticsearchRepository extends ElasticsearchRepository<User, String>, UserElasticsearchRepositoryCustom {
   ....
}

最后,您的服务代码应该像这样。

public class UserServiceClass {

  @Autowired
  private UserElasticsearchRepository userElasticsearchRepository;

  public void updateAddress(int id, String updatedAddress) {
    User user = userElasticsearchRepository.findOne(id);
    if (user.getUsername().equals("system")) {
      return;
    }
    userElasticsearchRepository.updateAddress(user,updatedAddress);
  } 
}

你也可以把用户查找逻辑移动到自定义仓储逻辑中,这样你就可以在方法中只传递用户ID和地址。希望这对你有所帮助。

请问您能在这里帮助我吗?https://stackoverflow.com/questions/64771780/updating-elasticsearch-entities-with-bulk - Catalina
重要提示:存储库基础设施尝试通过扫描在其下找到存储库的包中的类来自动检测自定义实现片段。这些类需要遵循附加后缀的命名约定,默认为Impl。 - Rubén Aguado

1
您可以使用ElasticSearchTemplate获取用户对象,而不是使用存储库接口。您可以使用NativeSearchQueryBuilder和其他类来构建查询。通过这种方式,您可以避免从您的类中引用两个相似的参考。如果这解决了您的问题,请告诉我。

谢谢Shahnaz,我的最终解决方案中只使用了ElasticSearchTemplate来读取和更新用户对象。但我希望能够通过Spring Data Elasticsearch存储库进行部分更新。 - Andrea
目前据库中似乎没有可用的部分更新,如果您找到了什么,请告诉我。谢谢。 - Shahnaz Khan

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