Elasticsearch Overview
Elasticsearch is a search server built on top of Lucene, providing REST API interfaces for full-text search capabilities. As a highly scalable open-source search and analytics engine, it enables fast storage, search, and analysis of large datasets.
Key characteristics include distributed architecture, high availability, asynchronous writing, multiple APIs, and document-oriented design.
Core concepts encompass near real-time processing, clusters, nodes for data storage, indices, shards for distributing indices, and replicas for redundancy.
Common use cases include Wikipedia, Stack Overflow, and GitHub.
Version Compatibility Matrix
When integrating Spring Boot with Elasticsearch, version alignment is critical.
| Spring Boot Version | Spring Data Elasticsearch | Elasticsearch Version |
|---|---|---|
| 1.3.5 or lower | 1.3.4 or lower | 1.7.2 or lower |
| 1.4.x or higher | 2.0.0 to 5.0.0 | 2.0.0 to 5.0.0 |
This demonstration uses Spring Boot 1.5.9 with Elasticsearch 2.3.5.
Spring Data Elasticsearch Integration
Maven Dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<version>1.5.9.RELEASE</version>
</dependency>
Configuration
spring.data.elasticsearch.repositories.enabled=true
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
Note: Port 9300 is for Java client communication, while 9200 exposes the RESTful HTTP interface.
Additional configuration options:
# Cluster name (default: elasticsearch)
spring.data.elasticsearch.cluster-name=elasticsearch
# Comma-separated list of cluster nodes
spring.data.elasticsearch.cluster-nodes=host1:9300,host2:9300
# Additional client properties
spring.data.elasticsearch.properties=
# Enable repositories (default: true)
spring.data.elasticsearch.repositories.enabled=true
Domain Entity
@Document(indexName = "productindex", type = "product")
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
private Long productId;
private String productName;
private Integer price;
private String productDesc;
private String createTime;
// getters and setters omitted
}
When using Spring Data, the entity requirse indexName (equivalent to a database) and type (equivalent to a table). Both must be lowercase.
Repository Layer
public interface ProductDao extends ElasticsearchRepository<Product, Long> {
}
The repository inehrits standard CRUD operations from ElasticsearchRepository. The save method handles both insert and update (upsert behavior), delete removes data and indices, and search supports various query types including pagination and scoring.
Service Layer
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductDao productDao;
@Override
public boolean saveProduct(Product product) {
boolean success = false;
try {
productDao.save(product);
success = true;
} catch (Exception e) {
e.printStackTrace();
}
return success;
}
@Override
public List<Product> fullTextSearch(String keyword) {
QueryStringQueryBuilder queryBuilder = new QueryStringQueryBuilder(keyword);
System.out.println("Query: " + queryBuilder);
Iterable<Product> results = productDao.search(queryBuilder);
Iterator<Product> iterator = results.iterator();
List<Product> productList = new ArrayList<>();
while (iterator.hasNext()) {
productList.add(iterator.next());
}
return productList;
}
@Override
public List<Product> paginatedSearch(int pageNum, int pageSize, String keyword) {
Pageable pageable = new PageRequest(pageNum, pageSize);
QueryStringQueryBuilder queryBuilder = new QueryStringQueryBuilder(keyword);
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withPageable(pageable)
.withQuery(queryBuilder)
.build();
System.out.println("Query: " + searchQuery.getQuery().toString());
Page<Product> results = productDao.search(searchQuery);
return results.getContent();
}
@Override
public List<Product> weightedSearch(String keyword) {
FunctionScoreQueryBuilder scoreQuery = QueryBuilders.functionScoreQuery()
.add(QueryBuilders.boolQuery()
.should(QueryBuilders.matchQuery("productName", keyword)),
ScoreFunctionBuilders.weightFactorFunction(10))
.add(QueryBuilders.boolQuery()
.should(QueryBuilders.matchQuery("productDesc", keyword)),
ScoreFunctionBuilders.weightFactorFunction(100))
.setMinScore(2);
System.out.println("Query: " + scoreQuery.toString());
Iterable<Product> results = productDao.search(scoreQuery);
Iterator<Product> iterator = results.iterator();
List<Product> productList = new ArrayList<>();
while (iterator.hasNext()) {
productList.add(iterator.next());
}
return productList;
}
}
Query types include full-text search, paginated search, and weighted scoring. Weighted queries prioritize results based on assigned scores—higher weights rank first, while items below setMinScore are excluded from results.
API Testing
Insert Data:
POST http://localhost:8080/api/product
{"productId":1,"productName":"ItemA","price":299,"productDesc":"ItemA is an electronics product","createTime":"2024-01-15 10:30:00"}
{"productId":2,"productName":"ItemB","price":599,"productDesc":"ItemB is a clothing product","createTime":"2023-11-20 14:45:00"}
{"productId":3,"productName":"ItemC","price":199,"productDesc":"ItemC is a food product","createTime":"2024-03-10 09:15:00"}
Full-Text Search:
GET http://localhost:8080/api/product?keyword=electronics
Paginated Search:
GET http://localhost:8080/api/product?pageNum=0&pageSize=2&keyword=product
Weighted Search:
GET http://localhost:8080/api/product/weighted?keyword=ItemB
After inserting data, access the head plugin at http://localhost:9200/_plugin/head/ to visualize and query data.
Alternative Approaches
Beyond Spring Data, Elasticsearch can be accessed via:
- TransportClient - Native Elasticsearch API (version-dependent)
- ElasticsearchTemplate - Spring wrapper for direct bean injection
- JestClient - HTTP-based client that works with Elasticsearch 2.x and above without version-specific code changes
Using JestClient for Version-Agnostic Integration
JestClient wraps Elasticsearch HTTP REST interfaces, eliminating the need to rewrite client code when server versions change.
Maven Dependency
<dependency>
<groupId>io.searchbox</groupId>
<artifactId>jest</artifactId>
<version>5.3.3</version>
</dependency>
Implementation Example
import io.searchbox.client.JestClient;
import io.searchbox.client.JestClientFactory;
import io.searchbox.client.JestResult;
import io.searchbox.client.config.HttpClientConfig;
import io.searchbox.core.*;
import io.searchbox.indices.*;
public class ElasticsearchClientDemo {
private static JestClient client;
private static final String INDEX = "productindex";
private static final String TYPE = "product";
private static final String ES_URL = "http://127.0.0.1:9200";
public static void main(String[] args) throws Exception {
client = createClient();
batchInsert();
executeFullTextSearch();
executeTermSearch();
executeRangeSearch();
client.close();
}
private static JestClient createClient() {
JestClientFactory factory = new JestClientFactory();
factory.setHttpClientConfig(
new HttpClientConfig.Builder(ES_URL)
.connTimeout(60000)
.readTimeout(60000)
.multiThreaded(true)
.build());
return factory.getObject();
}
private static void batchInsert() throws Exception {
List<Object> items = new ArrayList<>();
items.add(new Product(1L, "ItemA", 299, "Electronics product", "2024-01-15 10:30:00"));
items.add(new Product(2L, "ItemB", 599, "Clothing product", "2023-11-20 14:45:00"));
items.add(new Product(3L, "ItemC", 199, "Food product", "2024-03-10 09:15:00"));
Bulk.Builder bulkBuilder = new Bulk.Builder()
.defaultIndex(INDEX)
.defaultType(TYPE);
for (Object item : items) {
bulkBuilder.addAction(new Index.Builder(item).build());
}
BulkResult result = client.execute(bulkBuilder.build());
System.out.println("Batch insert: " + result.isSucceeded());
}
private static void executeFullTextSearch() throws Exception {
String query = "product";
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.queryStringQuery(query));
sourceBuilder.from(0).size(2);
System.out.println("Full-text query: " + sourceBuilder.toString());
System.out.println("Results: " + executeSearch(sourceBuilder.toString()));
}
private static void executeTermSearch() throws Exception {
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.termQuery("price", 299));
System.out.println("Term query: " + sourceBuilder.toString());
System.out.println("Results: " + executeSearch(sourceBuilder.toString()));
}
private static void executeRangeSearch() throws Exception {
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.rangeQuery("price").gte(200).lte(500));
System.out.println("Range query: " + sourceBuilder.toString());
System.out.println("Results: " + executeSearch(sourceBuilder.toString()));
}
private static String executeSearch(String query) throws Exception {
Search search = new Search.Builder(query)
.addIndex(INDEX)
.addType(TYPE)
.build();
JestResult result = client.execute(search);
return result.getSourceAsString();
}
}
Sample Results
Full-Text Search:
{
"from": 0,
"size": 2,
"query": {
"query_string": {
"query": "product"
}
}
}
// Returns matching documents with pagination
Term Search:
{
"query": {
"term": {
"price": 299
}
}
}
// Returns documents matching exact price value
Range Search:
{
"query": {
"range": {
"price": {
"from": 200,
"to": 500,
"include_lower": true,
"include_upper": true
}
}
}
}
// Returns documents within price range
Elasticsearch Installation on Windows
-
Download from https://www.elastic.co/downloads - select the ZIP distribution for your target version
-
Start Elasticsearch - Run
bin/elasticsearch.batand verify athttp://localhost:9200 -
Install Head Plugin - Execute
plugin install mobz/elasticsearch-headfrom the bin directory, then access athttp://localhost:9200/_plugin/head/ -
Register as Windows Service - Run
service.bat installfollowed byservice.bat startfrom the bin directory, then verify viaservices.msc
Updated Version Support
For Spring Boot 2.x environments with newer Elasticsearch versions, use Spring Data Elasticsearch 3.x:
| Spring Data Elasticsearch | Elasticsearch Version |
|---|---|
| 3.2.x | 6.7.2 |
| 3.1.x | 6.2.2 |
| 3.0.x | 5.5.0 |
Official documentation: https://github.com/spring-projects/spring-data-elasticsearch