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

时间:2016-11-22 12:49:45

标签: java spring-data-elasticsearch

我有一个文档,其中包含在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似乎不支持部分udpdate。所以我使用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);
  } 
}

但有两个类似的引用(存储库和ElasticsearchTemplate)似乎有点多余。

有人能建议我更好的解决方案吗?

2 个答案:

答案 0 :(得分:6)

您可以实现自己的自定义存储库,而不是将 ElasticsearchTemplate UserElasticsearchRepository 注入 UserServiceClass 并让现有的 UserElasticsearchRepository 扩展它。

我认为您现有的 UserElasticsearchRepository 看起来像这样。

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

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

public interface UserElasticsearchRepositoryCustom {

    public void updateAddress(User user, String updatedAddress);

}

然后通过创建一个名为 UserElasticsearchRepositoryImpl 的类来实现 UserElasticsearchRepositoryCustom ,并使用注入的 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);
    }
}

之后,只需使用 UserElasticsearchRepositoryCustom 扩展您的 UserElasticsearchRepository ,就可以看起来像这样。

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

最后,您的服务代码应如下所示。

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和地址。希望这有用。

答案 1 :(得分:1)

您也可以使用ElasticSearchTemplate来获取User对象而不是存储库接口。您可以使用NativeSearchQueryBuilder和其他类来构建查询。有了这个,你可以避免从你的班级两个类似的参考。如果这可以解决您的问题,请告诉我。