ElasticsearchRestTemplate使用方法详解!!!

本文基于 SpringBoot 3 + ES High Level Rest Client,详细介绍精确匹配、模糊检索、布尔多条件、范围、排序分页、聚合、高亮、批量操作的使用。

前置实体

1)商品文档实体 GoodsDoc

java 复制代码
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

@Data
@Document(indexName = "goods")
public class GoodsDoc {
    @Id
    private String id;

    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String title;

    @Field(type = FieldType.Keyword)
    private String category;

    @Field(type = FieldType.Double)
    private Double price;

    @Field(type = FieldType.Integer)
    private Integer stock;
}

2)注入模板对象

java 复制代码
import org.springframework.data.elasticsearch.client.elc.ElasticsearchRestTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class EsSearchService {

    @Autowired
    private ElasticsearchRestTemplate restTemplate;
}

term 精确查询(keyword)

DSL

json 复制代码
GET /goods/_search
{
  "query": {
    "term": {
      "category": {
        "value": "手机"
      }
    }
  }
}

Java 代码

java 复制代码
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import java.util.List;

public List<GoodsDoc> searchByCategory(String categoryVal) {
    NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
    builder.withQuery(QueryBuilders.termQuery("category", categoryVal));

    SearchHits<GoodsDoc> hits = restTemplate.search(
            builder.build(), GoodsDoc.class
    );
    return hits.stream().map(h -> h.getContent()).toList();
}

match 全文分词检索(text)

DSL

json 复制代码
GET /goods/_search
{
  "query": {
    "match": {
      "title": "华为手机"
    }
  }
}

Java

java 复制代码
public List<GoodsDoc> searchByTitle(String keyword) {
    NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
    builder.withQuery(QueryBuilders.matchQuery("title", keyword));

    SearchHits<GoodsDoc> hits = restTemplate.search(builder.build(), GoodsDoc.class);
    return hits.stream().map(Hit::getContent).toList();
}

bool 组合多条件(must + filter)

需求:标题含手机,价格 2000~6000,库存>0

json 复制代码
GET /goods/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {"title": "手机"}}
      ],
      "filter": [
        {"range": {"price": {"gte":2000,"lte":6000}}},
        {"range": {"stock": {"gt":0}}}
      ]
    }
  }
}

Java

java 复制代码
public List<GoodsDoc> searchBoolCombination(String titleKey, double minPrice, double maxPrice) {
    NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

    BoolQueryBuilder bool = QueryBuilders.boolQuery()
            .must(QueryBuilders.matchQuery("title", titleKey))
            .filter(QueryBuilders.rangeQuery("price").gte(minPrice).lte(maxPrice))
            .filter(QueryBuilders.rangeQuery("stock").gt(0));

    builder.withQuery(bool);
    SearchHits<GoodsDoc> hits = restTemplate.search(builder.build(), GoodsDoc.class);
    return hits.stream().map(Hit::getContent).toList();
}

分页 + 排序

json 复制代码
GET /goods/_search
{
  "from": 0,
  "size": 10,
  "sort": [{"price": "desc"}]
}

Java

java 复制代码
public List<GoodsDoc> searchPageSort(int pageNum, int pageSize) {
    NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
    // 分页
    builder.withPageable(PageRequest.of(pageNum - 1, pageSize));
    // 价格降序
    builder.withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC));

    SearchHits<GoodsDoc> hits = restTemplate.search(builder.build(), GoodsDoc.class);
    return hits.stream().map(Hit::getContent).toList();
}

聚合统计(按分类分组,求均价、商品数量)

json 复制代码
GET /goods/_search
{
  "size": 0,
  "aggs": {
    "group_category": {
      "terms": {"field": "category"},
      "aggs": {
        "avg_price": {"avg": {"field": "price"}}
      }
    }
  }
}

Java 完整聚合代码

java 复制代码
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.aggregation.Aggregate;
import co.elastic.clients.elasticsearch.core.aggregation.bucket.TermsAggregate;
import co.elastic.clients.elasticsearch.core.aggregation.metrics.AvgAggregate;

public void aggCategoryStat() {
    NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
    builder.withSize(0); // 不需要原始文档

    TermsAggregationBuilder termsAgg = AggregationBuilders
            .terms("group_category").field("category")
            .subAggregation(AggregationBuilders.avg("avg_price").field("price"));

    builder.withAggregations(termsAgg);

    SearchResponse<GoodsDoc> resp = restTemplate.search(builder.build(), GoodsDoc.class);
    Aggregate agg = resp.aggregations().get("group_category");
    TermsAggregate<?> terms = agg.terms();

    for (var bucket : terms.buckets().array()) {
        String category = bucket.keyAsString();
        long count = bucket.docCount();
        AvgAggregate avgPriceAgg = bucket.aggregations().get("avg_price").avg();
        double avgPrice = avgPriceAgg.value();

        System.out.printf("分类:%s,商品数:%d,均价:%.2f%n", category, count, avgPrice);
    }
}

关键词高亮

java 复制代码
public List<GoodsDoc> searchHighlight(String keyword) {
    NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
    builder.withQuery(QueryBuilders.matchQuery("title", keyword));

    HighlightBuilder highlight = new HighlightBuilder();
    highlight.field("title")
            .preTags("<span style='color:red'>")
            .postTags("</span>");
    builder.withHighlightBuilder(highlight);

    SearchHits<GoodsDoc> hits = restTemplate.search(builder.build(), GoodsDoc.class);

    List<GoodsDoc> result = new ArrayList<>();
    for (SearchHit<GoodsDoc> hit : hits) {
        GoodsDoc doc = hit.getContent();
        // 取出高亮片段
        List<String> highlightTitles = hit.getHighlightFields().get("title");
        if (highlightTitles != null && !highlightTitles.isEmpty()) {
            doc.setTitle(highlightTitles.get(0));
        }
        result.add(doc);
    }
    return result;
}

批量新增 / 批量更新

java 复制代码
public void bulkInsert(List<GoodsDoc> docList) {
    NativeBulkQueryBuilder bulkBuilder = new NativeBulkQueryBuilder();
    for (GoodsDoc doc : docList) {
        bulkBuilder.withOperation(BulkOperationBuilder.index(
                IndexQueryBuilder.withId(doc.getId()).withObject(doc)
        ));
    }
    restTemplate.bulk(bulkBuilder.build(), GoodsDoc.class);
}

关键说明

  1. NativeSearchQueryBuilder 用来链式拼装 DSL,不用手写 JSON 字符串;
  2. termQuery 只适用于 keywordmatchQuery 对应 text 分词检索;
  3. filter 不会计算打分,性能优于 must,过滤条件尽量放 filter;
  4. 聚合查询设置 size(0),不返回原始文档,减少网络传输;
  5. 深分页不要无限 from/size,改用 searchAfter 游标分页;

调用测试示例

java 复制代码
// 精确查询
esSearchService.searchByCategory("手机");

// 多条件组合
esSearchService.searchBoolCombination("华为", 2000, 6000);

// 分页
esSearchService.searchPageSort(1, 10);

// 聚合统计
esSearchService.aggCategoryStat();

// 高亮
esSearchService.searchHighlight("华为");
相关推荐
独隅1 小时前
IntelliJ IDEA 在 Linux 上的完整安装与使用指南
java·linux·intellij-idea
SimonKing1 小时前
别再自己写脚本了!DeepSeek三秒生成,豆包直接出片
java·后端·程序员
飞天狗1111 小时前
零基础JavaWeb入门——第4课:表单处理 —— 浏览器怎么把数据发给服务器
java·开发语言·前端·后端·servlet
多彩电脑1 小时前
向AIDE(安卓设备上的Android Studio)导入aar库
android·java·开发语言·androidx
阿维的博客日记2 小时前
Windows自由切换jdk版本
java·windows
摇滚侠2 小时前
MyBatis 入门到项目实战 MyBatis 逆向工程 62
java·开发语言·mybatis
ch.ju2 小时前
Java Programming Chapter 4——Multi-level inheritance
java·开发语言
yuezhilangniao2 小时前
2026删除K8s命名空间 卡 Terminating 的 ns
java·容器·kubernetes
GZ_TOGOGO2 小时前
Spring AI Alibaba 格式化输出
java·人工智能·spring