I remember the exact moment when the limitations of traditional databases stopped being a minor inconvenience and became a real bottleneck. I was building a product catalog for an e-commerce platform, and the search feature was embarrassingly slow. Every query involving partial text matching or fuzzy spelling corrections required carefully crafted LIKE '%term%' statements. The database struggled, the response time climbed, and the product owner kept asking, “Why can’t users just type ‘bluetooth speaker’ and see the most relevant results first, even if the exact phrase isn’t there?”
That question stuck with me. I had heard about Elasticsearch before, but I always assumed it was something reserved for teams with dedicated data engineers and complex DevOps pipelines. I was wrong. When I finally integrated Spring Boot with Elasticsearch using Spring Data Elasticsearch, I discovered that the path was much simpler. In fact, if you already work with Spring Data JPA, you are more than halfway there. The hard part is not the code—it is unlearning the relational mindset and embracing the idea that search is its own specialized operation with its own rules.
Let’s start with the foundation. Elasticsearch stores documents, not rows, and those documents are indexed and made searchable almost instantly. The integration with Spring Boot begins with a simple annotation. You define a POJO class, annotate it with @Document, and map it to an Elasticsearch index. Consider this example. Suppose you want to index product information for a catalog. You would create a class like this:
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@Document(indexName = "products")
public class Product {
@Id
private String id;
private String name;
private String description;
@Field(type = FieldType.Double)
private double price;
@Field(type = FieldType.Keyword)
private String category;
// getters, setters, constructors
}
That single annotation tells Spring Data Elasticsearch that this class represents a document in the “products” index. The @Id field marks the document identifier, and all other fields are automatically mapped unless you specify a custom type. Now, create a repository interface that extends ElasticsearchRepository:
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;
public interface ProductRepository extends ElasticsearchRepository<Product, String> {
List<Product> findByName(String name);
List<Product> findByDescriptionContaining(String keyword);
}
At this point, you already have basic search functionality. Calling productRepository.findByName("wireless mouse") will perform a term-level query on the name field. But the real power emerges when you write custom queries using the Elasticsearch query DSL via the @Query annotation or by using the ElasticsearchRestTemplate. For instance, to perform a full-text search that considers relevance scoring and fuzzy matches, you could write:
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;
public interface ProductRepository extends ElasticsearchRepository<Product, String> {
@Query("{\"match\": {\"description\": {\"query\": \"?0\", \"fuzziness\": \"AUTO\"}}}")
List<Product> searchByDescriptionFuzzy(String keyword);
}
Here the @Query annotation accepts raw Elasticsearch JSON. The question mark placeholder fills in the method parameter. The fuzziness: AUTO instructs Elasticsearch to correct minor typos automatically. I once saved a user from frustration by letting them find “blutooth speaker” and still see the correct results. That moment alone justified the entire integration.
You might wonder: “But I already have a production database. How do I keep data in sync between my relational store and Elasticsearch?” This is the central challenge of any dual-database architecture, and there is no one-size-fits-all solution, but I have found that a pattern using Spring’s @EventListener works surprisingly well for most small to medium applications. When a product is created or updated in your primary database, you simply trigger an index operation on the Elasticsearch side. For example:
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class ProductIndexer {
private final ProductRepository productRepository;
public ProductIndexer(ProductRepository productRepository) {
this.productRepository = productRepository;
}
@EventListener
public void onProductUpdate(ProductUpdatedEvent event) {
Product product = event.getProduct();
productRepository.save(product);
}
}
This approach works, but it introduces complexity when dealing with failures or asynchronous events. For systems that require eventual consistency and high throughput, consider using a message broker like Kafka or RabbitMQ to decouple the database update from the indexing operation. The key insight is that Elasticsearch is an eventually consistent system by nature, so you must design your application to tolerate a small lag between the source of truth and the search index.
Another subtlety that tripped me up early on was managing index mappings. Elasticsearch dynamically infers field types from the first document indexed. This can lead to unpleasant surprises when a field that was a number suddenly contains a string, causing a mapping conflict. To avoid this, you should define explicit mappings before indexing any data. Spring Data Elasticsearch supports this through the @Field annotation, as shown in the Product example, where the price field is explicitly set to FieldType.Double and category to FieldType.Keyword. You can also define mapping files in JSON or use the IndexOperations API from ElasticsearchOperations to create or update indices programmatically.
Let me share a personal learning. I spent an entire afternoon debugging why a fuzzy search on product names kept failing. The issue was that I had not set the appropriate analyzer on the name field. Elasticsearch uses the standard analyzer by default, which tokenizes text into lowercase words and removes common stop words. For product names like “iPhone 13 Pro Max,” the standard analyzer would split “13” and “Pro” into separate tokens, causing the fuzzy search to return irrelevant matches. The fix was to define a custom analyzer in the mapping that combined the standard analyzer with a keyword tokenizer for exact matches. Here is how you can set that up using the @Setting and @Mapping annotations:
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Setting;
import org.springframework.data.elasticsearch.annotations.Mapping;
@Document(indexName = "products")
@Setting(settingPath = "/elasticsearch/product-settings.json")
@Mapping(mappingPath = "/elasticsearch/product-mapping.json")
public class Product { ... }
The JSON files contain the analyzer definitions and field-specific mapping rules. This approach keeps the configuration external and maintainable.
You may be asking yourself, “Is this integration worth it for my application?” The answer depends on your search requirements. If you are building a simple CRUD application with basic lookups, sticking with a relational database is perfectly fine. But when you need to handle full-text search, autocomplete suggestions, faceted navigation, or real-time analytics on large datasets, Elasticsearch becomes almost indispensable. The cost of learning the integration is low if you already understand Spring Boot, and the benefit in user experience is enormous.
I remember deploying a search feature that let users filter products by price range, category, and rating simultaneously. The query was a combination of a bool filter and a match query. The implementation in Spring Boot, using the NativeSearchQueryBuilder, looked like this:
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
public List<Product> searchWithFilters(String keyword, double minPrice, double maxPrice, String category) {
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("description", keyword))
.filter(QueryBuilders.rangeQuery("price").gte(minPrice).lte(maxPrice))
.filter(QueryBuilders.termQuery("category", category)))
.build();
SearchHits<Product> hits = elasticsearchOperations.search(searchQuery, Product.class);
return hits.stream().map(hit -> hit.getContent()).collect(Collectors.toList());
}
The code is clean, readable, and powerful. The rangeQuery and termQuery filters immediately narrow down the result set, while the matchQuery handles relevance scoring. I saw search response times drop from over two seconds to under fifty milliseconds. The users stopped complaining. The product owner stopped asking questions.
Before you run off to implement this, remember that Elasticsearch requires its own infrastructure. You need to allocate memory for the JVM heap, configure cluster settings, and plan for backup and recovery. However, managed services like Elastic Cloud or AWS OpenSearch remove most of that burden. For local development, you can run Elasticsearch as a Docker container using a single command:
docker run -p 9200:9200 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:8.12.0
That’s it. Spring Boot will automatically pick up the connection if you define the properties in application.yml:
spring:
elasticsearch:
uris: http://localhost:9200
Now you are ready to index and search.
The last piece I want to share is about analytics. Elasticsearch is not just a search engine; it is a full analytics platform. With aggregations, you can compute metrics and build dashboards directly from your search results. For instance, to find the average product price per category, you can use the TermsAggregation combined with AvgAggregation. Spring Data Elasticsearch supports aggregations through the query builder as well.
If you are still on the fence, ask yourself one question: Have you ever had to implement an autocomplete feature that feels instant and anticipates typos? If so, you already know the answer. Elasticsearch makes that work. The combination with Spring Boot turns a complex distributed system into an approachable tool that respects the patterns you already know.
I encourage you to try this integration in your next project. Start small. Index a single table. Run a fuzzy search. See how it feels. Then expand to include facets, aggregations, and maybe even a suggestion system. The returns are immediate, and the learning curve is gentle enough that you will wonder why you waited so long.
If this article helped you see the path forward, please like, share, and comment below. Let me know what specific use case you are planning to tackle with Elasticsearch and Spring Boot. I would love to hear about your own moments of discovery.
As a best-selling author, I invite you to explore my books on Amazon. Don’t forget to follow me on Medium and show your support. Thank you! Your support means the world!
101 Books
101 Books is an AI-driven publishing company co-founded by author Aarav Joshi. By leveraging advanced AI technology, we keep our publishing costs incredibly low—some books are priced as low as $4—making quality knowledge accessible to everyone.
Check out our book Golang Clean Code available on Amazon.
Stay tuned for updates and exciting news. When shopping for books, search for Aarav Joshi to find more of our titles. Use the provided link to enjoy special discounts!
📘 Checkout my latest ebook for free on my channel!
Be sure to like, share, comment, and subscribe to the channel!
Our Creations
Be sure to check out our creations:
Investor Central | Investor Central Spanish | Investor Central German | Smart Living | Epochs & Echoes | Puzzling Mysteries | Hindutva | Elite Dev | JS Schools
We are on Medium
Tech Koala Insights | Epochs & Echoes World | Investor Central Medium | Puzzling Mysteries Medium | Science & Epochs Medium | Modern Hindutva