javaspring-bootelasticsearchspring-dataspring-data-elasticsearch

Spring Data Elasticsearch + Repository Upsert Query Method


I am working on replacing quite a lot of RestHighLevelClient code with Spring Data Elasticsearch. Wherever possible, I'm relying on the low-code approach provide by the Repository query methods. This has been quite nice as it's saving a lot of time. If I need to write something myself, I can always do so via a fragment, but this is a last resort option.

With this in mind, there are a few very simple queries that I've had to implement via fragments because I cannot figure out if they're possible via Repository query methods. These queries all involve upserts. For example:

// old code
class AuthorRepository {

  public void save(Author author) {
    UpdateRequest updateRequest = new UpdateRequest("author_index", author.getId())
      .doc(asJson(author), XContentType.JSON)
      .docAsUpsert(Boolean.TRUE);
  
    client.update(updateReqeust, RequestOptions.DEFAULT);
  }
}

This functionality is almost completely replaceable via default ElasticsearchRepository implementation, like so:

public interface AuthorRepository extends ElasticsearchRepository<Author, String> {
  // new code
  void save(Author author);

}

The only problem is the missing upsert functionality. It appears the generated query simply replaces whatever is already on the index.

Is there a way to tell the query methods to upsert? Thanks!

Also, in general I'm working w/ Spring Boot 3+, and letting Spring determine dependency versions.


Solution

  • when doing an upsert, you pass on a partial document which basically is a JSON doc.

    Spring Data Elasticsearch works on the concept of entities with property to field name matching, conversions of properties like dates, geo objects and other custom converters.

    When you pass an 'Author` object into your save method, is this complete? What is with possibly null properties in that object? Are they to be deleted in Elasticsearch or should they keep an existing value? From the entity approach the passed in object is the one to save and therefore a save will replace the existing data.

    Of course you can replace the save logic in a fragment and do an upsert there but as you say, this is not the default behaviour and upserting is not supported in the default repository implementations.