Changing the application’s code

edit

The RestHighLevelClient supports the same request and response objects as the TransportClient, but exposes slightly different methods to send the requests.

More importantly, the high-level client:

  • does not support request builders. The legacy methods like client.prepareIndex() must be changed to use request constructors like new IndexRequest() to create requests objects. The requests are then executed using synchronous or asynchronous dedicated methods like client.index() or client.indexAsync().
  • does not provide indices or cluster management APIs. Management operations can be executed by external scripts or using the low-level client.

How to migrate the way requests are built

edit

The Java API provides two ways to build a request: by using the request’s constructor or by using a request builder. Migrating from the TransportClient to the high-level client can be straightforward if application’s code uses the former, while changing usages of the latter can require more work.

With request constructors

edit

When request constructors are used, like in the following example:

IndexRequest request = new IndexRequest("index", "doc", "id"); 
request.source("{\"field\":\"value\"}", XContentType.JSON);

Create an IndexRequest using its constructor

The migration is very simple. The execution using the TransportClient:

IndexResponse response = transportClient.index(indexRequest).actionGet();

Can be easily replaced to use the RestHighLevelClient:

IndexResponse response = client.index(request);

With request builders

edit

The Java API provides a request builder for every type of request. They are exposed by the TransportClient through the many prepare() methods. Here are some examples:

IndexRequestBuilder indexRequestBuilder   = transportClient.prepareIndex();  
DeleteRequestBuilder deleteRequestBuilder = transportClient.prepareDelete(); 
SearchRequestBuilder searchRequestBuilder = transportClient.prepareSearch(); 

Create a IndexRequestBuilder using the prepareIndex() method from the TransportClient. The request builder encapsulates the IndexRequest to be executed.

Create a DeleteRequestBuilder using the prepareDelete() method from the TransportClient. The request builder encapsulates the DeleteRequest to be executed.

Create a SearchRequestBuilder using the prepareSearch() method from the TransportClient. The request builder encapsulates the SearchRequest to be executed.

Since the Java High Level REST Client does not support request builders, applications that use them must be changed to use requests constructors instead.

While you are incrementally migrating your application and you have both the transport client and the high level client available you can always get the Request object from the Builder object by calling Builder.request(). We do not advise continuing to depend on the builders in the long run but it should be possible to use them during the transition from the transport client to the high level rest client.

How to migrate the way requests are executed

edit

The TransportClient allows to execute requests in both synchronous and asynchronous ways. This is also possible using the high-level client.

Synchronous execution

edit

The following example shows how a DeleteRequest can be synchronously executed using the TransportClient:

DeleteRequest request = new DeleteRequest("index", "doc", "id"); 
DeleteResponse response = transportClient.delete(request).actionGet(); 

Create the DeleteRequest using its constructor

Execute the DeleteRequest. The actionGet() method blocks until a response is returned by the cluster.

The same request synchronously executed using the high-level client is:

DeleteRequest request = new DeleteRequest("index", "doc", "id");
DeleteResponse response = client.delete(request); 

Execute the DeleteRequest. The delete() method blocks until a response is returned by the cluster.

Asynchronous execution

edit

The following example shows how a DeleteRequest can be asynchronously executed using the TransportClient:

DeleteRequest request = new DeleteRequest("index", "doc", "id"); 
transportClient.delete(request, new ActionListener<DeleteResponse>() { 
    @Override
    public void onResponse(DeleteResponse deleteResponse) {
        
    }

    @Override
    public void onFailure(Exception e) {
        
    }
});

Create the DeleteRequest using its constructor

Execute the DeleteRequest by passing the request and a ActionListener that gets called on execution completion or failure. This method does not block and returns immediately.

The onResponse() method is called when the response is returned by the cluster.

The onFailure() method is called when an error occurs during the execution of the request.

The same request asynchronously executed using the high-level client is:

DeleteRequest request = new DeleteRequest("index", "doc", "id"); 
client.deleteAsync(request, new ActionListener<DeleteResponse>() { 
    @Override
    public void onResponse(DeleteResponse deleteResponse) {
        
    }

    @Override
    public void onFailure(Exception e) {
        
    }
});

Create the DeleteRequest using its constructor

Execute the DeleteRequest by passing the request and a ActionListener that gets called on execution completion or failure. This method does not block and returns immediately.

The onResponse() method is called when the response is returned by the cluster.

The onFailure() method is called when an error occurs during the execution of the request.

Manage Indices using the Low-Level REST Client

edit

The low-level client is able to execute any kind of HTTP requests, and can therefore be used to call the APIs that are not yet supported by the high level client.

For example, creating a new index with the TransportClient may look like this:

Settings settings = Settings.builder() 
                                .put(SETTING_NUMBER_OF_SHARDS, 1)
                                .put(SETTING_NUMBER_OF_REPLICAS, 0)
                                .build();

String mappings = XContentFactory.jsonBuilder()  
                                .startObject()
                                    .startObject("doc")
                                        .startObject("properties")
                                            .startObject("time")
                                                .field("type", "date")
                                            .endObject()
                                        .endObject()
                                    .endObject()
                                .endObject()
                                .string();

CreateIndexResponse response = transportClient.admin().indices()  
        .prepareCreate("my-index")
        .setSettings(indexSettings)
        .addMapping("doc", docMapping, XContentType.JSON)
        .get();

if (response.isAcknowledged() == false) {
    
}

Define the settings of the index

Define the mapping for document of type doc using a XContentBuilder

Create the index with the previous settings and mapping using the prepareCreate() method. The execution is synchronous and blocks on the get() method until the remote cluster returns a response.

Handle the situation where the index has not been created

The same operation executed with the low-level client could be:

Settings indexSettings = Settings.builder() 
        .put(SETTING_NUMBER_OF_SHARDS, 1)
        .put(SETTING_NUMBER_OF_REPLICAS, 0)
        .build();

String payload = XContentFactory.jsonBuilder() 
        .startObject()
            .startObject("settings") 
                .value(indexSettings)
            .endObject()
            .startObject("mappings")  
                .startObject("doc")
                    .startObject("properties")
                        .startObject("time")
                            .field("type", "date")
                        .endObject()
                    .endObject()
                .endObject()
            .endObject()
        .endObject().string();

HttpEntity entity = new NStringEntity(payload, ContentType.APPLICATION_JSON); 

Response response = client.getLowLevelClient().performRequest("PUT", "my-index", emptyMap(), entity); 
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
    
}

Define the settings of the index

Define the body of the HTTP request using a XContentBuilder with JSON format

Include the settings in the request body

Include the mappings in the request body

Convert the request body from String to a HttpEntity and set its content type (here, JSON)

Execute the request using the low-level client. The execution is synchronous and blocks on the performRequest() method until the remote cluster returns a response. The low-level client can be retrieved from an existing RestHighLevelClient instance through the getLowLevelClient getter method.

Handle the situation where the index has not been created

Checking Cluster Health using the Low-Level REST Client

edit

Another common need is to check the cluster’s health using the Cluster API. With the TransportClient it can be done this way:

ClusterHealthResponse response = client.admin().cluster().prepareHealth().get(); 

ClusterHealthStatus healthStatus = response.getStatus(); 
if (healthStatus != ClusterHealthStatus.GREEN) {
    
}

Execute a ClusterHealth with default parameters

Retrieve the cluster’s health status from the response

Handle the situation where the cluster’s health is not green

With the low-level client, the code can be changed to:

Map<String, String> parameters = singletonMap("wait_for_status", "green");
Response response = client.getLowLevelClient().performRequest("GET", "/_cluster/health", parameters); 

ClusterHealthStatus healthStatus;
try (InputStream is = response.getEntity().getContent()) { 
    Map<String, Object> map = XContentHelper.convertToMap(XContentType.JSON.xContent(), is, true); 
    healthStatus = ClusterHealthStatus.fromStringString) map.get("status"; 
}

if (healthStatus == ClusterHealthStatus.GREEN) {
    
}

Call the cluster’s health REST endpoint and wait for the cluster health to become green, then get back a Response object.

Retrieve an InputStream object in order to read the response’s content

Parse the response’s content using Elasticsearch’s helper class XContentHelper. This helper requires the content type of the response to be passed as an argument and returns a Map of objects. Values in the map can be of any type, including inner Map that are used to represent the JSON object hierarchy.

Retrieve the value of the status field in the response map, casts it as a a String object and use the ClusterHealthStatus.fromString() method to convert it as a ClusterHealthStatus object. This method throws an exception if the value does not corresponds to a valid cluster health status.

Handle the situation where the cluster’s health is not green

Note that for convenience this example uses Elasticsearch’s helpers to parse the JSON response body, but any other JSON parser could have been use instead.