A Simple Way to Index Java Beans in Elasticsearch

When it comes to data stores Java programmers are used to working with Java beans that are magically persisted. Solutions like Hibernate and the JPA specification for relational data stores or Morphia and Spring Data MongoDB are popular examples.

Developers working with Elasticsearch sometimes have the same desire - pass a Java bean and have it indexed automatically. There is an implementation of Spring Data for Elasticsearch available but it might be overhead for you or not be supported by your version of Elasticsearch. And there's Jest which uses the HTTP API that supports storing Java Beans directly.

If you want to do the same using the standard Java client for Elasticsearch there is no direct support for that but it can be implemented by hand easily.

Suppose you want to persist the following simple object structure that represents a book.

Publisher publisher = new Publisher();
publisher.setCountry("UK");
publisher.setName("Packt");
Book book = new Book();
book.setTitle("Learning Spring Boot");
book.setAuthors(Arrays.asList("Greg L. Turnquist"));
book.setPublisher(publisher);

Often it happens that we are thinking so hard about one way to solve a problem that we can't see the easier way. We don't need a special framework for Elasticsearch. Elastcsearch will happily store most JSON structures for you. And fortunately creating JSON documents from Java objects is a solved problem using Libraries like Jackson or GSON.

We can simply add a dependency, in this case to jackson-databind, to the project if it's not already there and instanciate an ObjectMapper.

ObjectMapper mapper = new ObjectMapper();

If you're using Spring Boot you will normally even be able to just @Autowire the ObjectMapper. The ObjectMapper can then be used to create a JSON representation of the object.

String value = mapper.writeValueAsString(book);

This will result in a string similar to this one.

{"title":"Learning Spring Boot","authors":["Greg L. Turnquist"],"publisher":{"name":"Packt","country":"UK"}}

You can then index the result using the Elasticsearch client interface.

IndexResponse response = client
        .prepareIndex(indexName, "book")
        .setSource(value).execute().actionGet();

When retrieving the document you can create Java objects again using the readValue method.

GetResponse getResponse = client
        .prepareGet(indexName, "book", response.getId())
        .execute().actionGet();
String source = getResponse.getSourceAsString();
Book persistedBook = mapper
        .readValue(source, Book.class);
assertEquals("Packt", persistedBook.getPublisher().getName());

Or even better: Maybe you don't even need to create a Java object again? When you're only displaying the result in a template maybe it's enough to just pass in a Map of the resulting document?

Map<String, Object> sourceAsMap = 
    getResponse.getSourceAsMap();

Sometimes we are looking for complicated solutions when we don't even need them. As Elasticsearch uses JSON everywhere it is very easy to use common libraries for serialization, be it in Java or in other languages.

About Florian Hopf

I am working as a freelance software developer and consultant in Karlsruhe, Germany and have written a German book on Elasticsearch. If you liked this post you can follow me on Twitter or subscribe to my feed to get notified of new posts. If you think I can help you and your company and you'd like to work with me please contact me directly

.