- Spring Data Tutorial - Home
- Spring Data Apache Solr
- Overview
- Prerequisites
- Introduction
- What is Apache Solr?
- Getting Started
- Querying
- Features
- Conclusion
- Spring Data Cassandra
- Overview
- Prerequisites
- Introduction
- What is Cassandra?
- Getting Started
- Annotation AllowFiltering with Query Methods
- Partition and Clustering
- Coding hands-on on Partitioning and Clustering
- Features
- Conclusion
- Spring Data Couchbase
- Overview
- Prerequisites
- Introduction
- What is Couchbase?
- Getting Started
- Views
- CouchbaseTemplate
- Hands-on using CouchbaseTemplate
- Features
- Conclusion
- Spring Data Elasticsearch
- Overview
- Prerequisites
- Introduction
- What is ElasticSearch?
- Getting Started
- Querying
- Configuring ElasticsearchOperations bean
- Features
- Conclusion
- Spring Data JDBC
- Introduction
- Need of Spring Data JDBC
- Features
- Domain-Driven Design
- Prerequisites
- Getting Started
- Conclusion
- Spring Data JPA
- Background
- Introduction
- Prerequisites
- Getting Started
- Features
- Conclusion
- Spring Data MongoDB
- Overview
- Prerequisites
- Introduction
- What is MongoDB?
- Getting Started
- Query Methods
- Annotations
- Exposing REST end points
- Relationship
- Conclusion
- Spring Data Redis
- Overview
- Prerequisites
- Introduction
- What is Redis?
- Redis Java Clients
- Getting Started
- Features
- Conclusion
- Spring Data REST
- Background
- Introduction to Spring Data REST
- Prerequisites
- Getting Started
- Features
- Conclusion
- Spring Data Tutorial Useful Resources
- Spring Data Tutorial - Quick Guide
- Spring Data Tutorial - Useful Resources
- Spring Data Tutorial - Discussion
Configuring ElasticsearchOperations bean
So far, we were performing operations on CustomerRepository to fetch, update or delete the data. Spring Data Elasticsearch provides an interface called ElasticsearchOperations through which we can perform some more complex operation in a simple way. ElasticsearchTemplate is an implementation of ElasticsearchOperations, Lets configure this ElasticsearchTemplate bean, so that we can effectively use it. Create a class with name ElasticsearchConfig (probably we can give any name) and annotate it with @Configuartion. Use below code sample −
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
@Configuration
@EnableElasticsearchRepositories(basePackages = "com.tutorialspoint.es.repository")
@ComponentScan(basePackages = { "com.tutorialspoint.es.service.impl" })
public class ElasticsearchConfig {
@Value("${elasticsearch.home:D:\\PersonalData\\elasticsearch-7.6.2-windows-x86_64\\elasticsearch-7.6.2}")
private String elasticsearchHome;
@Value("${elasticsearch.cluster.name:tutorials_Point}")
private String clusterName;
@Bean
public Client client() throws UnknownHostException {
TransportClient client = null;
try {
final Settings elasticsearchSettings = Settings.builder().put("client.transport.sniff", true)
.put("path.home", elasticsearchHome).put("cluster.name", clusterName).build();
client = new PreBuiltTransportClient(elasticsearchSettings);
client.addTransportAddress(new TransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
} catch (UnknownHostException e) {
e.printStackTrace();
}
return client;
}
@Bean
public ElasticsearchOperations elasticsearchTemplate() throws UnknownHostException {
return new ElasticsearchTemplate(client());
}
}
All the annotations used above are Spring enable standard annotation. @EnableElasticsearchRepositories, is used to scan the repository package. In the above configuration we are using Transport Client. This transport client requires below settings −
client.transport.sniff, to be marked as true, to enable sniffing feature.
path.home is a path to the installation directory of Elastic search.
cluster.name is used to provide cluster name, in case we are not using default cluster name i.e., elasticsearch. Finally, to get TransportClient, we are passing the bind address and port. In the second bean, we are passing this transport client and getting ElasticsearchTemplate of ElasticsearchOperations to work with Elastic search server. How this will help in interacting with the server, lets understand in the next section.
Working with QueryDSL
A Query DSL could be considered as an AST (Abstract Syntax Tree), Query DSL is used to define a query. To do so we will be using a Query Builder called NativeSearchQueryBuilder. Lets define a class called QueryDSLBuilder, and autowire the ElasticsearchTemplate.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.stereotype.Service;
@Service
public class QueryDSLBuilder {
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
}
Multi-Match search
Lets say we want to search a keyword/text in name or email, if the given keyword matches in either name or email, it will return those customer list. Lets have a look on Code −
//Find all customer whose name or email is as per given text
public List<Customer> fetchCustomerWithMultiTextMatch(String text) {
QueryBuilder queryBuilder = QueryBuilders.boolQuery()
.must(QueryBuilders.multiMatchQuery(text, "name", "email"));
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(queryBuilder).build();
return elasticsearchTemplate.queryForList(searchQuery, Customer.class);
}
The above operation can be considered as OR operation between two or more fields.
Multi-Field search
Lets say we want to search two keyword in the field name and email . If the keyword is available in both of the field, then it will return the result with Customer list. Lets have a look on code −
// Find all the customer whose name and salary is matched
public List<Customer> fetchCustomerByMultiFieldMatch(String text, Double salary) {
QueryBuilder queryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("name", text))
.must(QueryBuilders.matchQuery("salary", salary));
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(queryBuilder).build();
return elasticsearchTemplate.queryForList(searchQuery, Customer.class);
}
The above operation can be considered as AND operation between two or more fields.
Searching a part of word/data (Wildcard/Partial)
Lets say we want to search a partial text and want to check any String matching with this text in the field name should return that customer details. Lets have a look on the code −
// Fetch all the customer whose name matches with fully or partially with given text
public List<Customer> fetchCustomerByPartilaMatch(String text) {
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withFilter(QueryBuilders.regexpQuery("name", ".*" + text + ".*")).build();
return elasticsearchTemplate.queryForList(searchQuery, Customer.class);
}
We can consider above operation as LIKE in RDBMS or contains() method in Java.
Updating an Object
Lets say we want to update a customer name from X to Y. This can be done by fetching the customer details by name and then updating his name. Have a look on the code.
// Update a customer whose name is ? to ?
@Transactional
public void updateHavingName(String from, String to) {
// First find the customer whose name is ?
QueryBuilder queryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("name", from));
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(queryBuilder).build();
List<Customer> customers = elasticsearchTemplate.queryForList(searchQuery, Customer.class);
// Iterate through each customer whose name is ?
for (Customer customer : customers) {
// Update Customer name
customer.setName(to);
customerRepository.save(customer);
}
}
In the above code first we are finding the customer and then updating the name.
Deleting an Object
Lets say we want to delete a customer whose name is X, This operation also similar to above one, and requires finding the customer and then deleting it. Lets have a look on code.
// Delete a customer whose name is ?
public void deleteByName(String name) {
QueryBuilder queryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("name", name));
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(queryBuilder).build();
List<Customer> customers = elasticsearchTemplate.queryForList(searchQuery, Customer.class);
for (Customer customer : customers) {
customerRepository.delete(customer);
}
}
In the above code, first we are finding the customer with name X and then deleting it. We can call the above code from our controller class by adding respective end points. Here is the final controller code.
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.tutorialspoint.es.document.Customer;
import com.tutorialspoint.es.service.CustomerService;
import com.tutorialspoint.es.service.impl.QueryDSLBuilder;
@RestController
@RequestMapping("/rest")
public class CustomerController {
@Autowired
private CustomerService customerService;
@Autowired
private QueryDSLBuilder queryDSLBuilder;
// Persisting a customer to ElasticSearch
@PostMapping("/customer/save")
public Customer persistCustomer(@RequestBody Customer customer) {
return customerService.save(customer);
}
// Retrieving a customer from ElasticSearch
@GetMapping("/customer/find-by-id/{id}")
public Customer fetchCustomer(@PathVariable Long id) {
Optional<Customer> customerOpt = customerService.findById(id);
return customerOpt.isPresent() ? customerOpt.get() : null;
}
// Deleting a customer from elasticsearch
@DeleteMapping("/customer/delete/{id}")
public void deleteObject(@PathVariable Long id) {
customerService.deleteById(id);
}
// Retrieving a customer from ElasticSearch by Name
@GetMapping("/customer/find-by-name/{name}")
public List<Customer> fetchCustomerByNAme(@PathVariable String name) {
Page<Customer> customerOpt = customerService.findByName(name);
return customerOpt.get().collect(Collectors.toList());
}
// Retrieving a customer from ElasticSearch by City name
@GetMapping("/customer/find-by-address-city/{city}")
public List<Customer> fetchCustomerByCity(@PathVariable String city) {
Page<Customer> customerOpt = customerService.findByAddressCity(city);
return customerOpt.get().collect(Collectors.toList());
}
// Retrieving a customer from ElasticSearch whose name or email matches with given keywords
@GetMapping("/customer/multi-match/{text}")
public List<Customer> fetchCustomerWithTextMatch(@PathVariable String text) {
return queryDSLBuilder.fetchCustomerWithMultiTextMatch(text);
}
// Retrieving a customer from ElasticSearch whose name and salary are as per given search keywords
@GetMapping("/customer/multi-field/{name}/{salary}")
public List<Customer> fetchCustomerByFieldMatch(@PathVariable String name, @PathVariable Double salary) {
return queryDSLBuilder.fetchCustomerByMultiFieldMatch(name, salary);
}
/*
* Retrieving a customer from ElasticSearch whose name matches fully or
* partially with given text
*/
@GetMapping("/customer/partial-match/{text}")
public List<Customer> fetchCustomerByPartilaMatch(@PathVariable String text) {
return queryDSLBuilder.fetchCustomerByPartilaMatch(text);
}
// Updating a customer name from X to Y
@PostMapping("/customer/update/{from}/{to}")
public void updateCustomerHavingTextInName(@PathVariable String from, @PathVariable String to) {
queryDSLBuilder.updateHavingName(from, to);
}
// Deleting a customer from elasticsearch by name
@DeleteMapping("/customer/delete/by-name/{name}")
public void deleteCustometHavingName(@PathVariable String name) {
queryDSLBuilder.deleteByName(name);
}
}