SpingDataElasticSearch项目使用代码块

本文介绍了一个基于Elasticsearch的商品搜索服务实现,通过分析搜索关键字、过滤条件和分页排序需求,构建了高效的搜索查询,实现了商品信息的快速检索与展示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

package com.pinyougou.search.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.pinyougou.es.pojo.EsItem;
import com.pinyougou.search.dao.EsItemDao;
import com.pinyougou.service.ItemSearchService;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 商品搜素服务实现类
 */
@Service
@Transactional
public class ItemSearchServiceImpl implements ItemSearchService{

    //注入elasticsearch模板对象
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;
    @Autowired
    private EsItemDao esItemDao;

    @Override
    public Map<String, Object> search(Map<String, Object> params) {
        try {
            //定义原生搜索构建对象
            NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

            //默认搜索全部数据(查询条件多,不适合用if..else判断,直接设为默认搜索)
            builder.withQuery(QueryBuilders.matchAllQuery());

             //获取搜索关键字
            String keywords = (String) params.get("keywords");

            //################## 关键字查询 ##################
            //判断搜索关键字是否为空,不为空,根据条件搜索(会覆盖之前的数据)
            if (StringUtils.isNoneBlank(keywords)){
                // builder.withQuery(QueryBuilders.matchQuery("Keyword",keywords));   高亮显示不能用这种copy域
                builder.withQuery(QueryBuilders.multiMatchQuery(keywords,"title", "category", "brand", "seller"));
                //构建高亮对象
                HighlightBuilder.Field field = new HighlightBuilder
                        .Field("title")            //需要高亮的字段
                        .preTags("<font color='red'>")   //高亮前缀
                        .postTags("</font>")             //高亮后缀
                        .fragmentSize(50);               //截断字段
                //设置高亮字段
                builder.withHighlightFields(field);
            }

            /**
             * ################## 过滤条件 ##################
             * 页面传参格式:
             * {keywords: "华为", category: "手机", brand: "苹果", price: "500-1000", spec: {网络: "移动3G", 机身内存: "32G"}}
             *  brand: "苹果"
             *  category: "手机"
             *  keywords: "华为"
             *  price: "500-1000"
             *  spec: {网络: "移动3G", 机身内存: "32G"}
             */

            //定义组合查询构建对象
            BoolQueryBuilder bqBuilder = QueryBuilders.boolQuery();

            //1.商品分类过滤
            //获取分类字符串
            String category = (String) params.get("category");
            //判断是否为空
            if (StringUtils.isNoneBlank(category)){
                //组合过滤条件(termQuery 词条查询 不分词 / (must 必须的方式))
                bqBuilder.must(QueryBuilders.termQuery("category", category));
            }

            //2.商品品牌过滤
            //获取品牌字符串
            String brand = (String) params.get("brand");
            //判断是否为空
            if (StringUtils.isNoneBlank(brand)){
                //组合过滤条件
                bqBuilder.must(QueryBuilders.termQuery("brand",brand));
            }

            //3.商品价格过滤
            String price = (String) params.get("price");
            //如果不为空
            if (StringUtils.isNoneBlank(price)){
                //将传入的价格字符串转为数组(例:500-1000 -> [500,1000])
                String[] split = price.split("-");
                //定义区间查询构造对象
                RangeQueryBuilder rqBuilder = QueryBuilders.rangeQuery("price");
                //如果传入的为最高价格选线(例:3000-*)
                if ("*".equals(split[1])){
                    // 添加条件 :< xxxx
                    rqBuilder.gt(split[0]);
                }else {
                    //添加条件 :xxx - xxx (默认包含头尾)
                    rqBuilder.from(split[0],true).to(split[1],true);
                }
                //组合过滤条件
                bqBuilder.must(rqBuilder);
            }

            /**
             * 4.商品规格过滤
             * 前端传输格式:spec: {网络: "移动3G", 机身内存: "32G"}
             * 到后台转为Map集合(controller接收时设置)
             */
            Map<String,String> spec = (Map<String, String>) params.get("spec");
            //判断是否为空
            if (spec.size() > 0 && spec != null){
                //获取Key
                for (String key : spec.keySet()) {
                    //构建嵌套Field
                    String field = "spec." + key + ".keyword";
                    /**
                     * 组合过滤条件(nestedQuer 嵌套查询 不分词 / (must 必须的方式))
                     * 参数: 嵌套路径  查询构建对象   分词模式
                     */
                    bqBuilder.must(QueryBuilders.nestedQuery("spec",QueryBuilders.termQuery(field,spec.get(key)), ScoreMode.Max));
                }
            }

            //原生搜索查询对象添加过滤查询
            builder.withFilter(bqBuilder);

            //################## 分页查询 ##################
            //创建搜索查询对象
            SearchQuery build = builder.build();
            //获取当前页页码
            Integer page = (Integer) params.get("page");
            //如果为空,默认设为第一页
            if (page == null){
                page = 1;
            }
            //设置分页数据
            build.setPageable(PageRequest.of(page,10));

            //################## 排序查询 ##################
            //获取传输的‘需要排序的字段’ 和 ‘排序方式’
            String sortField = (String) params.get("sortField");
            String sortValue = (String) params.get("sortValue");
            //如果不为空
            if (StringUtils.isNoneBlank(sortField) && StringUtils.isNoneBlank(sortValue)){
                //定义排序对象
                Sort sort = new Sort("ASC".equalsIgnoreCase(sortValue)? Sort.Direction.ASC : Sort.Direction.DESC,sortField);
                build.addSort(sort);
            }

            /**
             * 分页搜索,获得合计分页对象
             * build:查询条件
             * EsItem:转换类型
             * SearchResultMapper:搜索结果转换接口(高亮内容转换)
             */
            AggregatedPage<EsItem> esItems = elasticsearchTemplate.queryForPage(build, EsItem.class, new SearchResultMapper() {
                @Override
                public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                    //定义集合封装搜索数据
                    List<EsItem> content = new ArrayList<>();
                    //迭代合计分页对象(查询到的数据)
                    for (SearchHit hit : searchResponse.getHits()) {
                        /**
                         * 获取JSON字符串,转为对象
                         * hit.getSourceAsString() :  {"sepc":{"网络":"移动4G","机身内存":"64G"},"id":1369356,....
                         */
                        EsItem esItem = JSON.parseObject(hit.getSourceAsString(), EsItem.class);
                        /**
                         * 获取标题高亮字段
                         * [title], fragments[[<font color='red'>华为</font> HUAWEI Mate 10 Pro 移动4G 64G]]
                         */
                        HighlightField highlightField = hit.getHighlightFields().get("title");
                        //判断高亮对象是否为空
                        if (highlightField != null){
                            /**
                             * 获取高亮字段内容
                             * ighlightField.getFragments()[0].toString():  <font color='red'>华为</font> HUAWEI Mate 10 Pro 移动4G 64G
                             */
                            String string = highlightField.getFragments()[0].toString();
                            //设置
                            esItem.setTitle(string);
                        }
                        //封装
                        content.add(esItem);
                    }
                    //返回
                    return new AggregatedPageImpl(content,pageable,searchResponse.getHits().getTotalHits());
                }
            });
            //定义Map集合 构建返回值
            Map<String,Object> map = new HashMap<>();
            //设置总记录数
            map.put("total",esItems.getTotalElements());
            //设置分页数据
            map.put("rows",esItems.getContent());
            //设置总页数
            map.put("totalPages",esItems.getTotalPages());
            //返回
            return map;
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}

搜索分组

/** 商品分类分组搜索 */
private List<String> categoryGroup(String keywords){
    // 定义List集合封装分组的商品分类
    List<String> categoryList = new ArrayList<>();
    // 判断搜索关键是否为空
    if (StringUtils.isNoneBlank(keywords)){
        // 1. 创建原生的搜索查询构建对象
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        // 2. 添加多条件匹配查询
        builder.withQuery(QueryBuilders.multiMatchQuery(keywords,
                            "category","brand","title","seller"));

        // 3. 创建词条分组构建对象
        // categoryGroup: 分组名称,用于存储分组后的数据
        // category: 根据商品分类分组(分组的Field)
        TermsAggregationBuilder aggregationBuilder = AggregationBuilders
                .terms("categoryGroup").field("category");
        // 4. 添加分组构建对象
        builder.addAggregation(aggregationBuilder);

        // 5. 搜索,得到多分组封装对象
        Aggregations aggregations = esTemplate.query(builder.build(),
                new ResultsExtractor<Aggregations>() {
            @Override
            public Aggregations extract(SearchResponse sr) {
                return sr.getAggregations();
            }
        });

        // 6. 获取指定的分组结果
        StringTerms terms = aggregations.get("categoryGroup");
        // 6.1 迭代分组结果集合
        for (StringTerms.Bucket bucket : terms.getBuckets()) {
            categoryList.add(bucket.getKeyAsString());
        }
    }
    return categoryList;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值