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:
<code>elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.2.2/elasticsearch-analysis-ik-6.2.2.zip</code>Start 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.batand 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.
<code>/**
* 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
}
</code>@Id – marks the field as the document ID.
@Field – defines field type, analyzer, indexing, and storage options.
<code>@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;
</code>Derived Queries
By extending
ElasticsearchRepository, common CRUD methods are available, and query methods can be defined directly from the method name.
<code>public interface EsProductRepository extends ElasticsearchRepository<EsProduct, Long> {
Page<EsProduct> findByNameOrSubTitleOrKeywords(String name, String subTitle, String keywords, Pageable page);
}
</code>Service Layer
The
EsProductServiceinterface defines operations for importing, creating, and deleting product documents.
<code>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);
}
</code>The implementation uses a DAO to fetch product data, then saves it via the repository.
<code>@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);
}
}
</code>Controller Layer
The REST controller exposes endpoints for importing data, deleting, creating, and searching products.
<code>@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));
}
}
</code>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!
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.