Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Partial update with Spring Data Elasticsearch repository

I have a document with many fields (some nested) indexed on elasticsearch. For example:

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

Only some filed is mapped in my entity (I don't want to map the entire document):

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

  // getter/setter methods

}

In the service class I would like to do a partial update with ElasticsearchRepository, without mapping all document's fields in the entity:

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);
  } 
}

but save method overwrites the entire document:

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

Partial udpdate seems not supported by ElasticsearchRepository. So I used ElasticsearchTemplate, to make a partial update, for example:

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);
  } 
}

but seems a bit redundant to have two similar references (repository and ElasticsearchTemplate).

Can anyone suggest me a better solution?

like image 528
Andrea Avatar asked Nov 22 '16 12:11

Andrea


1 Answers

Instead of having both ElasticsearchTemplate and UserElasticsearchRepository injected into your UserServiceClass, you can implement your own custom repository and let your existing UserElasticsearchRepository extend it.

I assume that your existing UserElasticsearchRepository look something like this.

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

You have to create new interface name UserElasticsearchRepositoryCustom. Inside this interface you can list your custom query method.

public interface UserElasticsearchRepositoryCustom {

    public void updateAddress(User user, String updatedAddress);

}

Then implement your UserElasticsearchRepositoryCustom by create a class called UserElasticsearchRepositoryImpl and implement your custom method inside with injected 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);
    }
}

After that, just extends your UserElasticsearchRepository with UserElasticsearchRepositoryCustom so it should look like this.

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

Finally, you service code should look like this.

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);
  } 
}

You can also move your user finding logic into the custom repository logic as well so that you can passing only user id and address in the method. Hope this is helpful.

like image 137
Perthpiti Avatar answered Sep 19 '22 11:09

Perthpiti