本文基于 SpringBoot 3 + ES High Level Rest Client,详细介绍精确匹配、模糊检索、布尔多条件、范围、排序分页、聚合、高亮、批量操作的使用。
前置实体
1)商品文档实体 GoodsDoc
importlombok.Data;importorg.springframework.data.annotation.Id;importorg.springframework.data.elasticsearch.annotations.Document;importorg.springframework.data.elasticsearch.annotations.Field;importorg.springframework.data.elasticsearch.annotations.FieldType;@Data@Document(indexName="goods")publicclassGoodsDoc{@IdprivateStringid;@Field(type=FieldType.Text,analyzer="ik_max_word")privateStringtitle;@Field(type=FieldType.Keyword)privateStringcategory;@Field(type=FieldType.Double)privateDoubleprice;@Field(type=FieldType.Integer)privateIntegerstock;}2)注入模板对象
importorg.springframework.data.elasticsearch.client.elc.ElasticsearchRestTemplate;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;@ServicepublicclassEsSearchService{@AutowiredprivateElasticsearchRestTemplaterestTemplate;}term 精确查询(keyword)
DSL
GET/goods/_search{"query":{"term":{"category":{"value":"手机"}}}}Java 代码
importco.elastic.clients.elasticsearch.core.SearchResponse;importco.elastic.clients.elasticsearch.core.search.Hit;importjava.util.List;publicList<GoodsDoc>searchByCategory(StringcategoryVal){NativeSearchQueryBuilderbuilder=newNativeSearchQueryBuilder();builder.withQuery(QueryBuilders.termQuery("category",categoryVal));SearchHits<GoodsDoc>hits=restTemplate.search(builder.build(),GoodsDoc.class);returnhits.stream().map(h->h.getContent()).toList();}match 全文分词检索(text)
DSL
GET/goods/_search{"query":{"match":{"title":"华为手机"}}}Java
publicList<GoodsDoc>searchByTitle(Stringkeyword){NativeSearchQueryBuilderbuilder=newNativeSearchQueryBuilder();builder.withQuery(QueryBuilders.matchQuery("title",keyword));SearchHits<GoodsDoc>hits=restTemplate.search(builder.build(),GoodsDoc.class);returnhits.stream().map(Hit::getContent).toList();}bool 组合多条件(must + filter)
需求:标题含手机,价格 2000~6000,库存>0
GET/goods/_search{"query":{"bool":{"must":[{"match":{"title":"手机"}}],"filter":[{"range":{"price":{"gte":2000,"lte":6000}}},{"range":{"stock":{"gt":0}}}]}}}Java
publicList<GoodsDoc>searchBoolCombination(StringtitleKey,doubleminPrice,doublemaxPrice){NativeSearchQueryBuilderbuilder=newNativeSearchQueryBuilder();BoolQueryBuilderbool=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);returnhits.stream().map(Hit::getContent).toList();}分页 + 排序
GET/goods/_search{"from":0,"size":10,"sort":[{"price":"desc"}]}Java
publicList<GoodsDoc>searchPageSort(intpageNum,intpageSize){NativeSearchQueryBuilderbuilder=newNativeSearchQueryBuilder();// 分页builder.withPageable(PageRequest.of(pageNum-1,pageSize));// 价格降序builder.withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC));SearchHits<GoodsDoc>hits=restTemplate.search(builder.build(),GoodsDoc.class);returnhits.stream().map(Hit::getContent).toList();}聚合统计(按分类分组,求均价、商品数量)
GET/goods/_search{"size":0,"aggs":{"group_category":{"terms":{"field":"category"},"aggs":{"avg_price":{"avg":{"field":"price"}}}}}}Java 完整聚合代码
importco.elastic.clients.elasticsearch.core.SearchResponse;importco.elastic.clients.elasticsearch.core.aggregation.Aggregate;importco.elastic.clients.elasticsearch.core.aggregation.bucket.TermsAggregate;importco.elastic.clients.elasticsearch.core.aggregation.metrics.AvgAggregate;publicvoidaggCategoryStat(){NativeSearchQueryBuilderbuilder=newNativeSearchQueryBuilder();builder.withSize(0);// 不需要原始文档TermsAggregationBuildertermsAgg=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);Aggregateagg=resp.aggregations().get("group_category");TermsAggregate<?>terms=agg.terms();for(varbucket:terms.buckets().array()){Stringcategory=bucket.keyAsString();longcount=bucket.docCount();AvgAggregateavgPriceAgg=bucket.aggregations().get("avg_price").avg();doubleavgPrice=avgPriceAgg.value();System.out.printf("分类:%s,商品数:%d,均价:%.2f%n",category,count,avgPrice);}}关键词高亮
publicList<GoodsDoc>searchHighlight(Stringkeyword){NativeSearchQueryBuilderbuilder=newNativeSearchQueryBuilder();builder.withQuery(QueryBuilders.matchQuery("title",keyword));HighlightBuilderhighlight=newHighlightBuilder();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=newArrayList<>();for(SearchHit<GoodsDoc>hit:hits){GoodsDocdoc=hit.getContent();// 取出高亮片段List<String>highlightTitles=hit.getHighlightFields().get("title");if(highlightTitles!=null&&!highlightTitles.isEmpty()){doc.setTitle(highlightTitles.get(0));}result.add(doc);}returnresult;}批量新增 / 批量更新
publicvoidbulkInsert(List<GoodsDoc>docList){NativeBulkQueryBuilderbulkBuilder=newNativeBulkQueryBuilder();for(GoodsDocdoc:docList){bulkBuilder.withOperation(BulkOperationBuilder.index(IndexQueryBuilder.withId(doc.getId()).withObject(doc)));}restTemplate.bulk(bulkBuilder.build(),GoodsDoc.class);}关键说明
NativeSearchQueryBuilder用来链式拼装 DSL,不用手写 JSON 字符串;termQuery只适用于keyword,matchQuery对应text分词检索;filter不会计算打分,性能优于must,过滤条件尽量放 filter;- 聚合查询设置
size(0),不返回原始文档,减少网络传输; - 深分页不要无限
from/size,改用searchAfter游标分页;
调用测试示例
// 精确查询esSearchService.searchByCategory("手机");// 多条件组合esSearchService.searchBoolCombination("华为",2000,6000);// 分页esSearchService.searchPageSort(1,10);// 聚合统计esSearchService.aggCategoryStat();// 高亮esSearchService.searchHighlight("华为");