ElasticSearch DSL & Java RestHighLevelClient: Complete Operations Guide
This tutorial demonstrates how to perform ElasticSearch index, document, query, and aggregation operations using both DSL queries and Java RestHighLevelClient within a SpringBoot project, covering setup, CRUD, search types, sorting, pagination, highlighting, and nested aggregations with code examples.
Project Setup
A SpringBoot project is created with the following structure: ElasticSearchConfig configures the RestHighLevelClient bean, MyHotel entity maps the index structure, DSL files store common queries, and JSON files hold request payloads. Four test classes cover index, document, search, and aggregation operations.
Maven Dependencies
The pom.xml declares elasticsearch-rest-high-level-client and overrides the ElasticSearch version to 7.14.2 via elasticsearch.version property because RestHighLevelClient was deprecated starting from 7.15. Other dependencies include Lombok, FastJSON, Commons Lang3, and Spring Boot test starter.
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<elasticsearch.version>7.14.2</elasticsearch.version>
</properties>
<dependencies>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>ElasticSearch Configuration
Custom connection properties are defined in application.yml:
elasticsearch:
username: elastic
password: tdGiSi*fhwW0F60*i*Jc
urls: [http://192.168.136.128:9200] ElasticSearchConfigreads these properties, builds an HttpHost array, configures basic authentication, and adds a compatibility header application/vnd.elasticsearch+json;compatible-with=7 to avoid errors when operating against ElasticSearch 8.x with the 7.x client.
@Configuration
public class ElasticSearchConfig {
@Value("${elasticsearch.username}")
private String username;
@Value("${elasticsearch.password}")
private String password;
@Value("${elasticsearch.urls}")
private String[] urls;
@Bean
public RestHighLevelClient restHighLevelClient() {
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(username, password));
ArrayList<HttpHost> hostlist = new ArrayList<>();
for (String url : urls) {
hostlist.add(HttpHost.create(url));
}
HttpHost[] hosts = hostlist.toArray(new HttpHost[0]);
RestClientBuilder builder = RestClient.builder(hosts);
builder.setHttpClientConfigCallback(httpClientBuilder ->
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
builder.setDefaultHeaders(new Header[]{
new BasicHeader(HttpHeaders.ACCEPT,
"application/vnd.elasticsearch+json;compatible-with=7"),
new BasicHeader(HttpHeaders.CONTENT_TYPE,
"application/vnd.elasticsearch+json;compatible-with=7")
});
return new RestHighLevelClient(builder);
}
}MyHotel Entity
Simple POJO with Lombok @Data holding hotel fields: id, name, address, price, brand, city, location (geo_point), and distance (for sorting results).
@Data
public class MyHotel {
private Integer id;
private String name;
private String address;
private Integer price;
private String brand;
private String city;
private String location;
private Double distance;
}Index Operations
DSL statements are stored in IndexDSL.txt; Java tests in IndexTest.
DSL Examples
Create index with mappings: id (keyword), name (text, ik_max_word analyzer, copy_to "all"), address (keyword, index:false), price (integer), brand (keyword, copy_to "all"), city (keyword), location (geo_point), all (text, ik_max_word).
Get index : GET /myhotel Add field (only addition allowed after creation): PUT /myhotel/_mapping with addScore (integer, index:false).
Delete index :
DELETE /myhotelJava Implementation
Uses CreateIndexRequest, GetIndexRequest, PutMappingRequest, DeleteIndexRequest. JSON payloads are read from classpath resources ( JSON/CreateMyHotelJson.txt, JSON/UpdateMyHotelJson.txt) via ResourceUtils and BufferedReader. The exists method returns boolean; get prints mappings as pretty JSON using FastJSON.
@Test
void createIndexTest() throws IOException {
CreateIndexRequest request = new CreateIndexRequest("myhotel");
File file = ResourceUtils.getFile("classpath:JSON/CreateMyHotelJson.txt");
String createJson;
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
createJson = br.readLine();
}
request.source(createJson, XContentType.JSON);
client.indices().create(request, RequestOptions.DEFAULT);
}Document Operations
DSL in DocDSL.txt; Java in DocTest.
DSL Examples
Add/Full update : POST /myhotel/_doc/1 or PUT /myhotel/_doc/1 with full JSON document.
Get by ID : GET /myhotel/_doc/1 Partial update : POST /myhotel/_update/1 with doc containing changed fields.
Delete :
DELETE /myhotel/_doc/1Java Implementation
addDocTest: reads JSON, extracts id, builds IndexRequest. getDocByIdTest: uses GetRequest, prints source JSON and deserializes to MyHotel. updateDocPartTest: UpdateRequest with request.doc("name", "new name", "price", "666"). deleteDocTest: DeleteRequest. bulkRequestTest: reads lines from DemoJsonData.txt, adds each as IndexRequest to BulkRequest, executes once.
@Test
void bulkRequestTest() throws IOException {
File file = ResourceUtils.getFile("classpath:JSON/DemoJsonData.txt");
BulkRequest request = new BulkRequest();
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
String json = br.readLine();
while (StringUtils.isNotBlank(json)) {
JSONObject jsonObj = JSON.parseObject(json);
request.add(new IndexRequest("myhotel")
.id(jsonObj.getString("id"))
.source(json, XContentType.JSON));
json = br.readLine();
}
}
client.bulk(request, RequestOptions.DEFAULT);
}Document Queries
DSL in SearchDSL.txt; Java in SearchTest.
Query Types & DSL
Match All : { "query": { "match_all": {} } } Single Field (match) : queries the all field (copy_to target) for "朝阳如家".
Multi-field (multi_match) : searches "朝阳如家" across brand and name; note: multi_match degrades performance, prefer copy_to.
Term (exact) : term on city for "北京".
Range : price between 100 and 300 ( gte / lte).
Bool Combination : must (match name "如家"), must_not (price > 400), filter (geo_distance 5km from 31.21,121.5). must and should affect scoring; filter and must_not do not.
Geo Distance Query : standalone geo_distance for 5km radius.
Function Score : base query (match "朝阳" on all), filter function boosting brand "速8" by weight 10, boost_mode=sum.
Sorting : by price asc then id asc; or by _geo_distance from a point (lat 31.034661, lon 121.612282) with unit km.
Pagination : from=20, size=10 with sort.
Highlighting : match on all for "北广场", highlight name with require_field_match=false because query field differs from highlight field.
Java Implementation Highlights
All tests use SearchRequest with QueryBuilders (matchAllQuery, matchQuery, multiMatchQuery, termQuery, rangeQuery, boolQuery, geoDistanceQuery, functionScoreQuery). boolTest demonstrates combining must (term on city) and filter (range on price). sortAndPageTest calculates from = (page-1)*size. matchDistanceTest uses GeoPoint and SortBuilders.geoDistanceSort with DistanceUnit.KILOMETERS. funcTest builds FunctionScoreQueryBuilder with FilterFunctionBuilder and ScoreFunctionBuilders.weightFactorFunction(10), sets boostMode(CombineFunction.SUM). Comment warns: if explicit sort is added, scoring is disabled. highlightTest sets HighlightBuilder().field("name").requireFieldMatch(false).
Common processResponse method extracts total hits, iterates SearchHit, deserializes to MyHotel, handles highlight fragments (replaces name), and reads sort values for distance.
@Test
void funcTest() throws IOException {
SearchRequest request = new SearchRequest("myhotel");
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
boolQuery.must(QueryBuilders.termQuery("city", "上海"));
boolQuery.filter(QueryBuilders.rangeQuery("price").gte(200).lte(600));
FunctionScoreQueryBuilder functionScoreQuery =
QueryBuilders.functionScoreQuery(boolQuery,
new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
QueryBuilders.termQuery("brand", "速8"),
ScoreFunctionBuilders.weightFactorFunction(10))
})
.boostMode(CombineFunction.SUM);
request.source().query(functionScoreQuery);
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
processResponse(response);
}Aggregation Queries
DSL in AggDSL.txt; Java in AggTest.
DSL Examples
Terms Aggregation : group by brand, size 20, default descending count. Can set order: { "_count": "asc" } for ascending.
Filtered Aggregation : first query price lte 200, then aggregate on brand.
Nested Aggregation (stats) : under each brand bucket, compute stats on price (min, max, sum, avg). Order by priceAgg.min asc.
Alternative Nested Aggregation : separate min, max, sum, avg sub-aggregations for selective retrieval.
Java Implementation
aggTest: range query price 200-500, size=0, terms aggregation on brand size 10. Parses buckets, prints brand and docCount. aggStatsTest: same query, terms aggregation with order(BucketOrder.aggregation("priceAgg.min", true)), sub-aggregation stats("priceAgg") on price. Retrieves Stats object for min/max/sum/avg as strings. aggregationTest: identical logic but uses individual MinAggregationBuilder, MaxAggregationBuilder, SumAggregationBuilder, AvgAggregationBuilder added as sub-aggregations. Retrieves each via bucket's aggregations map.
@Test
void aggStatsTest() throws IOException {
SearchRequest request = new SearchRequest("myhotel");
request.source().query(QueryBuilders.rangeQuery("price").gte(200).lte(500));
request.source().size(0);
TermsAggregationBuilder termsAggregationBuilder =
AggregationBuilders.terms("brandAgg").field("brand")
.order(BucketOrder.aggregation("priceAgg.min", true))
.subAggregation(AggregationBuilders.stats("priceAgg").field("price"));
request.source().aggregation(termsAggregationBuilder);
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
Aggregations aggregations = response.getAggregations();
Terms brandAgg = aggregations.get("brandAgg");
for (Terms.Bucket bucket : brandAgg.getBuckets()) {
String brandName = bucket.getKeyAsString();
long docCount = bucket.getDocCount();
Stats priceAgg = bucket.getAggregations().get("priceAgg");
System.out.println("brand: " + brandName + ", count: " + docCount +
", min: " + priceAgg.getMinAsString() +
", max: " + priceAgg.getMaxAsString() +
", sum: " + priceAgg.getSumAsString() +
", avg: " + priceAgg.getAvgAsString());
}
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
