Integrating Elasticsearch with Spring Boot for Full-Text Product Search
This guide walks through installing Elasticsearch and Kibana, configuring a Chinese analyzer, defining Spring Data Elasticsearch annotations, creating repository and service layers, building a REST controller, and testing product search functionality within a Spring Boot mall application.
Project Framework Overview
The article explains how to integrate Elasticsearch into a mall project to enable full‑text search for product information. It covers installation, configuration, data mapping, repository creation, service implementation, and API testing.
Elasticsearch
Elasticsearch is a distributed, scalable, real‑time search and analytics engine. It provides full‑text search and real‑time data statistics from the start of a project.
Installation and Usage
Download the Elasticsearch 6.2.2 zip package from https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-2-2 and unzip it.
Install the Chinese analysis plugin:
elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.2.2/elasticsearch-analysis-ik-6.2.2.zipStart Elasticsearch by running bin\elasticsearch.bat.
Kibana
Download Kibana 6.2.2 zip from https://artifacts.elastic.co/downloads/kibana/kibana-6.2.2-windows-x86_64.zip and unzip it.
Start Kibana with bin\kibana.bat and open http://localhost:5601 in a browser.
Spring Data Elasticsearch
Spring Data Elasticsearch provides a Spring‑Data‑style way to operate on Elasticsearch, reducing boilerplate code.
Common Annotations
@Document – maps a class to an Elasticsearch document.
/**
* Marks the class as an Elasticsearch document.
*/
@Document(indexName = "pms", type = "product", shards = 1, replicas = 0)
public class EsProduct implements Serializable {
private static final long serialVersionUID = -1L;
@Id
private Long id;
@Field(type = FieldType.Keyword)
private String productSn;
@Field(type = FieldType.Keyword)
private String brandName;
@Field(type = FieldType.Keyword)
private String productCategoryName;
@Field(analyzer = "ik_max_word", type = FieldType.Text)
private String name;
@Field(analyzer = "ik_max_word", type = FieldType.Text)
private String subTitle;
@Field(analyzer = "ik_max_word", type = FieldType.Text)
private String keywords;
// other fields omitted for brevity
}@Id – marks the field as the document ID.
@Field – defines field type, analyzer, indexing, and storage options.
@Field(type = FieldType.Keyword) // not tokenized, suitable for exact matches
private String productSn;
@Field(analyzer = "ik_max_word", type = FieldType.Text) // tokenized for full‑text search
private String name;Derived Queries
By extending ElasticsearchRepository, common CRUD methods are available, and query methods can be defined directly from the method name.
public interface EsProductRepository extends ElasticsearchRepository<EsProduct, Long> {
Page<EsProduct> findByNameOrSubTitleOrKeywords(String name, String subTitle, String keywords, Pageable page);
}Service Layer
The EsProductService interface defines operations for importing, creating, and deleting product documents.
public interface EsProductService {
/** Import all products from the database into Elasticsearch */
int importAll();
/** Delete a product by ID */
void delete(Long id);
/** Create a product document by ID */
EsProduct create(Long id);
/** Batch delete products */
void delete(List<Long> ids);
/** Search by keyword with pagination */
Page<EsProduct> search(String keyword, Integer pageNum, Integer pageSize);
}The implementation uses a DAO to fetch product data, then saves it via the repository.
@Service
public class EsProductServiceImpl implements EsProductService {
private static final Logger LOGGER = LoggerFactory.getLogger(EsProductServiceImpl.class);
@Autowired
private EsProductDao productDao;
@Autowired
private EsProductRepository productRepository;
@Override
public int importAll() {
List<EsProduct> esProductList = productDao.getAllEsProductList(null);
Iterable<EsProduct> saved = productRepository.saveAll(esProductList);
int count = 0;
for (EsProduct p : saved) {
count++;
}
return count;
}
@Override
public void delete(Long id) {
productRepository.deleteById(id);
}
@Override
public EsProduct create(Long id) {
List<EsProduct> list = productDao.getAllEsProductList(id);
if (!list.isEmpty()) {
EsProduct esProduct = list.get(0);
return productRepository.save(esProduct);
}
return null;
}
@Override
public void delete(List<Long> ids) {
List<EsProduct> esProductList = new ArrayList<>();
for (Long id : ids) {
EsProduct esProduct = new EsProduct();
esProduct.setId(id);
esProductList.add(esProduct);
}
productRepository.deleteAll(esProductList);
}
@Override
public Page<EsProduct> search(String keyword, Integer pageNum, Integer pageSize) {
Pageable pageable = PageRequest.of(pageNum, pageSize);
return productRepository.findByNameOrSubTitleOrKeywords(keyword, keyword, keyword, pageable);
}
}Controller Layer
The REST controller exposes endpoints for importing data, deleting, creating, and searching products.
@Controller
@Api(tags = "EsProductController", description = "Product search management")
@RequestMapping("/esProduct")
public class EsProductController {
@Autowired
private EsProductService esProductService;
@ApiOperation("Import all products from DB to ES")
@RequestMapping(value = "/importAll", method = RequestMethod.POST)
@ResponseBody
public CommonResult<Integer> importAllList() {
int count = esProductService.importAll();
return CommonResult.success(count);
}
@ApiOperation("Delete product by ID")
@RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
@ResponseBody
public CommonResult<Object> delete(@PathVariable Long id) {
esProductService.delete(id);
return CommonResult.success(null);
}
@ApiOperation("Batch delete products by IDs")
@RequestMapping(value = "/delete/batch", method = RequestMethod.POST)
@ResponseBody
public CommonResult<Object> delete(@RequestParam("ids") List<Long> ids) {
esProductService.delete(ids);
return CommonResult.success(null);
}
@ApiOperation("Create product document by ID")
@RequestMapping(value = "/create/{id}", method = RequestMethod.POST)
@ResponseBody
public CommonResult<EsProduct> create(@PathVariable Long id) {
EsProduct esProduct = esProductService.create(id);
if (esProduct != null) {
return CommonResult.success(esProduct);
}
return CommonResult.failed();
}
@ApiOperation("Simple keyword search")
@RequestMapping(value = "/search/simple", method = RequestMethod.GET)
@ResponseBody
public CommonResult<CommonPage<EsProduct>> search(
@RequestParam(required = false) String keyword,
@RequestParam(required = false, defaultValue = "0") Integer pageNum,
@RequestParam(required = false, defaultValue = "5") Integer pageSize) {
Page<EsProduct> esProductPage = esProductService.search(keyword, pageNum, pageSize);
return CommonResult.success(CommonPage.restPage(esProductPage));
}
}Interface Testing
Import Data from Database to Elasticsearch
Product Search
Project Source Code
https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-06
Recommended Reading
mall architecture and feature overview
Required knowledge for mall learning (recommended resources)
Integrating SpringBoot+MyBatis to build a basic skeleton
Integrating Swagger‑UI for online API documentation
Integrating Redis for caching
SpringSecurity + JWT authentication (part 1)
SpringSecurity + JWT authentication (part 2)
Integrating SpringTask for scheduled jobs
Follow us and give a like!
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.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
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.
