# How Elasticsearch Product Search Works in the mall‑search Module

> Explore how Elasticsearch product search powers the mall-search module. Learn about data synchronization from MySQL, REST endpoint functionality, and advanced search capabilities. Optimize your e-commerce search today.

- Repository: [macro/mall](https://github.com/macrozheng/mall)
- Tags: how-to-guide
- Published: 2026-02-28

---

**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](https://github.com/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:
- [`EsProductServiceImpl.java`](https://github.com/macrozheng/mall/blob/main/EsProductServiceImpl.java) – Core search logic and query construction
- [`EsProductRepository.java`](https://github.com/macrozheng/mall/blob/main/EsProductRepository.java) – Spring Data Elasticsearch repository interface
- [`EsProductController.java`](https://github.com/macrozheng/mall/blob/main/EsProductController.java) – REST endpoints at `/esProduct`
- [`EsProductDao.java`](https://github.com/macrozheng/mall/blob/main/EsProductDao.java) – MyBatis DAO for database reads

## Data Synchronization from MySQL to Elasticsearch

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

```java
// 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`](https://github.com/macrozheng/mall/blob/main/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.

### Simple Keyword Search

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

```java
// 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:

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

```

Triggers the simple derived query through `findByNameOrSubTitleOrKeywords`.

```http
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.

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

```

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

```http
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`](https://github.com/macrozheng/mall/blob/main/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`](https://github.com/macrozheng/mall/blob/main/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.