How Elasticsearch Product Search Works in the mall‑search Module

The mall‑search module implements a full‑text product search layer using Spring Data Elasticsearch, synchronizing data from MySQL to Elasticsearch and exposing REST endpoints for keyword search, filtered queries, recommendations, and aggregations.

The mall-search module in the macrozheng/mall repository provides the search infrastructure for the e‑commerce platform. It bridges the relational MySQL database with Elasticsearch to deliver fast, relevance‑scored product discovery. Understanding this implementation reveals how to build scalable search features using Spring Data Elasticsearch and the Elasticsearch Java client.

Architecture Overview

The module follows a three‑layer pattern separating data synchronization, query construction, and HTTP exposure. The data‑sync layer pulls records from MySQL via MyBatis, the search service layer builds native Elasticsearch queries using NativeSearchQueryBuilder, and the API layer exposes REST endpoints through EsProductController.

Key components include:

Data Synchronization from MySQL to Elasticsearch

Before any search executes, product data must be indexed. The importAll() method in EsProductServiceImpl orchestrates a full import:

// EsProductServiceImpl.java lines 61-71
int importAll() {
    List<EsProduct> esProductList = esProductDao.getAllEsProductList(null);
    Iterable<EsProduct> esProductIterable = productRepository.saveAll(esProductList);
    // ... count and return
}

EsProductDao executes a MyBatis query defined in mall-search/src/main/resources/dao/EsProductDao.xml to select from the pms_product table. The resulting list is bulk‑stored via productRepository.saveAll, a Spring Data Elasticsearch method that performs a _bulk index operation.

Search Implementation Strategies

The module offers two search modes: a simple derived query for basic keyword matching and a programmatic NativeSearchQuery for complex filtering and relevance tuning.

For unfiltered keyword searches, the service delegates to a derived query method in EsProductRepository:

// EsProductRepository.java
Page<EsProduct> findByNameOrSubTitleOrKeywords(
    String name, String subTitle, String keywords, Pageable page);

Spring Data Elasticsearch automatically generates a bool should query that matches the keyword against the name, subTitle, or keywords fields. This approach requires no manual query construction but offers limited relevance tuning.

Advanced Search with Filtering and Sorting

The search(...) method in EsProductServiceImpl (lines 110–170) builds a sophisticated NativeSearchQuery combining full‑text scoring, term filters, and configurable sorting.

Query Construction Logic:

  1. Term Filters – Optional brandId and productCategoryId parameters are added to a BoolQueryBuilder as term clauses to narrow results.
  2. Function Scoring – When a keyword is provided, three match queries target name (weight 10), subTitle (weight 5), and keywords (weight 2). These are wrapped in a function_score query using ScoreFunctionBuilders.weightFactorFunction with a minimum score threshold of 2.
  3. Sorting Strategy – The integer sort parameter selects the final SortBuilder:
    • 1 → id desc (newest)
    • 2 → sale desc (best‑selling)
    • 3 → price asc (price low‑to‑high)
    • 4 → price desc (price high‑to‑low)
    • Default → _score desc (relevance)

The query executes via ElasticsearchRestTemplate.search, and hits are mapped to EsProduct entities before being wrapped in a Spring Page object.

Product Recommendations

The recommend(Long id, Integer pageNum, Integer pageSize) method generates "similar products" by analyzing a reference item. Located in EsProductServiceImpl, it:

  1. Retrieves the source product by ID
  2. Builds a functionScore query matching the product's name, subTitle, and keywords
  3. Adds boosted term matches: brandId (weight 5) and productCategoryId (weight 3)
  4. Excludes the original product using a mustNot filter on the id field

This approach surfaces items sharing brand or category while maintaining textual relevance, executed through the same ElasticsearchRestTemplate pattern as the advanced search.

Aggregations for Filter Data

To populate UI filter panels, searchRelatedInfo(String keyword) (lines 190+) executes three aggregations:

  • Brand aggregation – terms on brandName to collect available brands
  • Category aggregation – terms on productCategoryName for category facets
  • Attribute aggregation – A nested aggregation on attrValueList (type = 1) collecting attribute IDs, values, and names

The raw Aggregations object is transformed in convertProductRelatedInfo(...) into an EsProductRelatedInfo DTO, providing structured data for faceted navigation without returning full product documents.

REST API Endpoints

Clients interact with the search capabilities through EsProductController endpoints:

GET /esProduct/search/simple?keyword=phone&pageNum=0&pageSize=10

Triggers the simple derived query through findByNameOrSubTitleOrKeywords.

GET /esProduct/search?keyword=phone&brandId=3&sort=3&pageNum=0&pageSize=10

Executes the advanced function‑score search with brand filtering and price‑ascending sort.

GET /esProduct/recommend/42?pageNum=0&pageSize=5

Returns similar products while excluding the original item (ID 42).

GET /esProduct/search/relate?keyword=phone

Returns aggregation results containing distinct brandNames, productCategoryNames, and product attributes for the keyword "phone".

Summary

  • Data flow – EsProductDao queries MySQL, importAll() bulk‑indexes into Elasticsearch via EsProductRepository.saveAll
  • Simple search – Uses Spring Data’s derived query findByNameOrSubTitleOrKeywords for basic keyword matching
  • Advanced search – Programmatic NativeSearchQuery with BoolQueryBuilder for filters, functionScore for weighted field relevance (10/5/2), and dynamic sorting
  • Recommendations – Function‑score query boosting brandId (5) and productCategoryId (3) while excluding the source product
  • Aggregations – Multi‑bucket terms aggregations on brands, categories, and nested attributes to drive filter UIs

Frequently Asked Questions

How does the mall‑search module synchronize product data with Elasticsearch?

The synchronization occurs through the importAll() method in EsProductServiceImpl.java. This method calls EsProductDao.getAllEsProductList(null) to fetch all product records from MySQL using MyBatis, then invokes productRepository.saveAll() to perform a bulk index operation into Elasticsearch. This establishes the initial searchable document corpus.

What relevance scoring algorithm does the advanced product search use?

The advanced search implements a function score query defined in EsProductServiceImpl.java. It assigns weight factors to three matched fields: name receives a weight of 10, subTitle receives 5, and keywords receives 2. These individual match scores are summed, and a minimum score of 2 is enforced to filter out low‑relevance results, ensuring brand and title matches rank higher than keyword matches.

How are product recommendations generated in the recommendation endpoint?

The recommend(Long id, ...) method retrieves the reference product, extracts its textual fields and categorical data, then constructs a function score query that boosts matches on the same brandId (weight 5) and productCategoryId (weight 3). A mustNot clause excludes the original product by ID, returning a ranked list of similar items based on shared brand affinity, category, and textual content.

What sorting options are available in the product search API?

The sort parameter in the advanced search endpoint accepts integer values controlling the SortBuilder: 1 sorts by ID descending (newest), 2 by sales volume descending (best‑selling), 3 by price ascending (low‑to‑high), 4 by price descending (high‑to‑low), and any other value defaults to _score descending (relevance). This allows users to switch between discovery modes programmatically.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →