Mastering Elasticsearch Java REST Client: From Raw HTTP to a Ready‑to‑Use Wrapper

This tutorial walks through setting up an Elasticsearch server, creating a Java REST client, implementing a custom GET‑with‑body request, building a reusable ESClient wrapper with common CRUD and search methods, and providing Groovy performance test cases for indexing and querying.

FunTester
FunTester
FunTester
Mastering Elasticsearch Java REST Client: From Raw HTTP to a Ready‑to‑Use Wrapper

The author optimized Elasticsearch (ES) performance at work, achieving nearly double the speed, and decided to learn the ES Java client for more accurate testing by bypassing intermediate services.

Preparation

Start an ES server (ensure the client and server versions match), create a new project, and add the required dependencies for the Java REST high‑level client.

Learning Resources

Consult the official Elasticsearch Java REST High Level Client documentation (e.g., https://www.elastic.co/guide/en/elasticsearch/client/java-rest/6.7/java-rest-high-search.html) and, if possible, review internal source code to understand how the client wraps HTTP calls.

HTTP GET with Body Implementation

Many ES APIs use HTTP GET with a request body. The following custom class enables a GET request that carries a body payload.

package com.funtester.httpclient

import org.apache.http.client.methods.HttpEntityEnclosingRequestBase
import javax.annotation.concurrent.NotThreadSafe

/**
 * HttpGet request that carries a body
 */
@NotThreadSafe
class HttpGetByBody extends HttpEntityEnclosingRequestBase {
    static final String METHOD_NAME = "GET";

    @Override
    String getMethod() {
        return METHOD_NAME;
    }

    /**
     * Constructor with URI string
     */
    HttpGetByBody(final String uri) {
        this(new URI(uri))
    }

    HttpGetByBody(final URI uri) {
        super();
        setURI(uri);
    }

    HttpGetByBody() {
        super();
    }
}

Using the Elasticsearch Java REST Client

Composing raw HTTP requests for ES can be error‑prone. The high‑level client abstracts this complexity while still using Apache HttpAsyncClient under the hood. Below is the client‑creation snippet used by the ES client.

private CloseableHttpAsyncClient createHttpClient() {
    RequestConfig.Builder requestConfigBuilder = RequestConfig.custom()
        .setConnectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS)
        .setSocketTimeout(DEFAULT_SOCKET_TIMEOUT_MILLIS);
    if (requestConfigCallback != null) {
        requestConfigBuilder = requestConfigCallback.customizeRequestConfig(requestConfigBuilder);
    }
    try {
        HttpAsyncClientBuilder httpClientBuilder = HttpAsyncClientBuilder.create()
            .setDefaultRequestConfig(requestConfigBuilder.build())
            .setMaxConnPerRoute(DEFAULT_MAX_CONN_PER_ROUTE)
            .setMaxConnTotal(DEFAULT_MAX_CONN_TOTAL)
            .setSSLContext(SSLContext.getDefault())
            .setTargetAuthenticationStrategy(new PersistentCredentialsAuthenticationStrategy());
        if (httpClientConfigCallback != null) {
            httpClientBuilder = httpClientConfigCallback.customizeHttpClient(httpClientBuilder);
        }
        final HttpAsyncClientBuilder finalBuilder = httpClientBuilder;
        return AccessController.doPrivileged(() -> finalBuilder.build());
    } catch (NoSuchAlgorithmException e) {
        throw new IllegalStateException("could not create the default ssl context", e);
    }
}

Custom ESClient Wrapper

To simplify everyday ES operations, the author wrapped the high‑level client into an ESClient class that provides concise methods for indexing, retrieving, checking existence, deleting, searching, and scrolling.

Test Cases

Groovy scripts demonstrate how to benchmark indexing and searching using a concurrent executor ( FunQpsConcurrent). The first script adds documents; the second performs a match‑query search.

package com.funtest.groovytest

import com.alibaba.fastjson.JSONObject
import com.funtester.es.ESClient
import com.funtester.frame.SourceCode
import com.funtester.frame.execute.FunQpsConcurrent
import java.util.concurrent.atomic.AtomicInteger

class ESC extends SourceCode {
    static void main(String[] args) {
        def client = new ESClient("127.0.0.1", 9200, "http")
        def data = new JSONObject()
        data.name = "FunTester"
        data.age = getRandomInt(100)
        def index = new AtomicInteger(0)
        def test = {
            data.put("time", index.getAndIncrement())
            client.index("fun", "tt", data)
        }
        new FunQpsConcurrent(test, "ES添加数据").start()
    }
}
package com.funtest.groovytest

import com.alibaba.fastjson.JSONObject
import com.funtester.es.ESClient
import com.funtester.frame.SourceCode
import com.funtester.frame.execute.FunQpsConcurrent
import org.elasticsearch.index.query.QueryBuilders
import java.util.concurrent.atomic.AtomicInteger

class ESC extends SourceCode {
    static void main(String[] args) {
        def client = new ESClient("127.0.0.1", 9200, "http")
        def data = new JSONObject()
        data.name = "FunTester"
        data.age = getRandomInt(100)
        def index = new AtomicInteger(0)
        def test = {
            client.search("fun", QueryBuilders.matchQuery("time", getRandomInt(10)))
        }
        new FunQpsConcurrent(test, "ES搜索").start()
    }
}

ESClient API Implementation

The full wrapper class includes methods for common CRUD and search operations, as well as a scroll‑based search for large result sets.

package com.funtester.es

import com.funtester.frame.SourceCode
import groovy.util.logging.Log4j2
import org.apache.http.HttpHost
import org.elasticsearch.action.delete.DeleteRequest
import org.elasticsearch.action.get.GetRequest
import org.elasticsearch.action.get.GetResponse
import org.elasticsearch.action.index.IndexRequest
import org.elasticsearch.action.index.IndexResponse
import org.elasticsearch.action.search.SearchRequest
import org.elasticsearch.action.search.SearchResponse
import org.elasticsearch.action.search.SearchScrollRequest
import org.elasticsearch.client.RequestOptions
import org.elasticsearch.client.RestClient
import org.elasticsearch.client.RestHighLevelClient
import org.elasticsearch.common.unit.TimeValue
import org.elasticsearch.index.query.QueryBuilder
import org.elasticsearch.search.SearchHits
import org.elasticsearch.search.builder.SearchSourceBuilder
import org.elasticsearch.search.fetch.subphase.FetchSourceContext
import java.util.concurrent.TimeUnit

/**
 * ES client API practice class
 */
@Log4j2
class ESClient extends SourceCode {
    String host
    int port
    String scheme
    RestHighLevelClient client

    ESClient(String host, int port = 9200, String scheme = "http") {
        this.host = host
        this.port = port
        this.scheme = scheme
        def builder = RestClient.builder(new HttpHost(host, port, scheme))
        builder.setMaxRetryTimeoutMillis(1000)
        client = new RestHighLevelClient(builder)
    }

    /** Add document */
    def index(String index, type, Map data) {
        IndexRequest indexRequest = new IndexRequest(index, type).source(data)
        IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT)
        indexResponse.getId()
    }

    /** Get document */
    def get(String index, type, id) {
        GetRequest getRequest = new GetRequest(index, type, id)
        GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT)
        if (getResponse.isExists()) {
            getResponse.getSourceAsString()
        }
    }

    /** Check existence */
    def exists(String index, type, id) {
        GetRequest getRequest = new GetRequest(index, type, id)
        getRequest.fetchSourceContext(new FetchSourceContext(false))
        getRequest.storedFields("_none_")
        client.exists(getRequest, RequestOptions.DEFAULT)
    }

    /** Delete document */
    def delete(String index, type, id) {
        DeleteRequest deleteRequest = new DeleteRequest(index, type, id)
        client.delete(deleteRequest, RequestOptions.DEFAULT)
    }

    /** Search */
    def search(String index, QueryBuilder query, int size = 10) {
        SearchRequest searchRequest = new SearchRequest(index)
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
        sourceBuilder.query(query)
        sourceBuilder.from(0)
        sourceBuilder.size(size)
        sourceBuilder.timeout(new TimeValue(1, TimeUnit.SECONDS))
        searchRequest.source(sourceBuilder)
        client.search(searchRequest, RequestOptions.DEFAULT)
    }

    /** Scroll search */
    def searchScroll(String index, QueryBuilder query, int size = 10) {
        SearchRequest searchRequest = new SearchRequest(index)
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
        searchSourceBuilder.query(query)
        searchSourceBuilder.size(size)
        searchRequest.source(searchSourceBuilder)
        searchRequest.scroll(TimeValue.timeValueMinutes(1L))
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT)
        String scrollId = searchResponse.getScrollId()
        SearchHits hits = searchResponse.getHits()
        def searchHits = hits.getHits()
        while (searchHits != null && searchHits.length > 0) {
            SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId)
            scrollRequest.scroll(TimeValue.timeValueMinutes(1L))
            searchResponse = client.scroll(scrollRequest, RequestOptions.DEFAULT)
            scrollId = searchResponse.getScrollId()
            searchHits = searchResponse.getHits().getHits()
        }
    }

    /** Close client */
    def close() {
        client.close()
    }
}

This guide provides a practical, end‑to‑end example of setting up, using, and extending the Elasticsearch Java REST client for backend services.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBackend DevelopmentElasticsearchPerformance TestingGroovyREST Client
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.