ES商品搜索实战 Easy-Es搭配原生SearchSourceBuilder

这篇具有很好参考价值的文章主要介绍了ES商品搜索实战 Easy-Es搭配原生SearchSourceBuilder。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.背景

在使用Easy-Es发现有很多功能可能不是很好使用,或者说我自己不太会使用。
这里分享自己使用EE和原生API一起搭配使用的商品搜索功能,包含keyword指定分词器搜索,商品分类,商品品牌,价格区间,嵌套分组搜索,权重排序,高亮,以及品牌去重等功能。

2.直接上代码

商品实体类

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@IndexName(value = "product", aliasName = "product")
public class Product {

	@IndexId(type = IdType.CUSTOMIZE)
	private Long id;

	private Long brandId;

	private String brandLogo;

	private Long productCategoryId;

	private Long freightTemplateId;

	private Long productAttributeCategoryId;

	private String channelCode;

	private String channelProductCode;

	private String name;

	private String productType;

	private String pic;

	private String productCode;

	private Integer publishStatus;

	private Integer initStatus;

	private Integer verifyStatus;

	private Integer sort;

	private BigDecimal price;

	private BigDecimal costPrice;

	private BigDecimal crossedPrice;

	private BigDecimal profitMargin;

	private BigDecimal profitMarginPercent;

	private BigDecimal priceProfitMargin;

	private BigDecimal priceProfitMarginPercent;

	private Integer productRealSales;

	private Integer productVirtualSales;

	private String subTitle;

	private String description;

	private BigDecimal originalPrice;

	private String albumPics;

	private String detailTitle;

	private String detailDesc;

	private String detailHtml;

	private String detailMobileHtml;

	private String detailWechatHtml;

	private Integer sellType;

	private String keywords;

	private String note;
	
	private String brandName;

	private String channelProductCategoryPath;

	private String productCategoryName;

	private String productCategoryPath;

	private LocalDateTime joinTime;

	private Integer blacklist;

	private String blackReason;

	private Boolean delFlag;

	@IndexField(fieldType = FieldType.NESTED, nestedClass = SkuStock.class)
	private List<SkuStock> skuInfos;

	@IndexField(fieldType = FieldType.NESTED, nestedClass = ProductGroup.class)
	private List<ProductGroup> groupInfos;

	@IndexField(fieldType = FieldType.DATE, fieldData = true, dateFormat = "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis")
	private LocalDateTime createTime;

	@IndexField(fieldType = FieldType.DATE, fieldData = true, dateFormat = "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis")
	private LocalDateTime updateTime;
}

SKU实体类

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SkuStock {

	private Long skuId;

	private Long productId;

	private String productCode;

	private String productType;

	private String skuCode;

	private String channelCode;

	private String channelSkuCode;

	private String skuName;

	private BigDecimal platformPrice;

	private BigDecimal platformSellPrice;

	private BigDecimal originalPrice;

	private BigDecimal crossedPrice;

	private Integer stock;

	private LocalDateTime warningTime;

	private Integer lowStock;

	private String pic;

	private Integer orderNum;

	private Integer backOrderNum;

	private Integer lockStock;

	private Integer publishStatus;

	private Integer warningStatus;

	private Integer initStatus;

	private String priceTypeCode;

	private String saleAttributes;

	private String marker;

	private String remark;

	private Integer blacklist;

	private String channelProductPoolCode;

	private Boolean skuDelFlag;

	private String productArea;

	private Integer lowestBuy;

	private String warrantDesc;

	private String capacity;

	private String weight;

	private Integer logisticsType;

	private BigDecimal taxRatePercentage;

	private Integer deliveryTime;

	private String taxCode;

	private String unit;

	private String wareInfo;

	private String albumPics;

	private String cubeParam;

	private Integer canInvoice;

	private Integer saleState;

	private Integer returnRuleType;

	private Integer noReasonToReturn;

	private String specificationAttributes;

	private String categoryAttributes;

	private Boolean extraDelFlag;
}

搜索的入参

@Data
public class EntProductSearchDTO implements Serializable {

    @ApiModelProperty(value = "商品名称")
    private String keyword;

    @ApiModelProperty(value = "商品价格")
    private BigDecimal priceMax;

    @ApiModelProperty(value = "商品价格")
    private BigDecimal priceMin;

    @ApiModelProperty(value = "品牌ID")
    private List<Long> brandIds;

    @ApiModelProperty(value = "商品分类ID")
    private List<Long> productCategoryIds;

    @ApiModelProperty(value = "分组ID")
    private List<String> groupingIdList;

    @ApiModelProperty(value = "商品类型 :real->实物商品;call->话费商品;coupon->卡券;recharge->直充商品")
    private String productType;

    @ApiModelProperty("发布状态 0-下架 1-上架 2三方下架")
    private List<Integer> publishStatus;

    @ApiModelProperty("分类地址 ,分割")
    private String productCategoryPath;

    /**
     * 页码
     */
    @ApiModelProperty("页码")
    private Integer page;

    /**
     * 每页记录数
     */
    @ApiModelProperty("每页记录数")
    private Integer size;

    @NotNull(message = "sortType cannot be null")
    @Min(value = 0, message = "sortType mix is 0")
    @Max(value = 3, message = "sortType max is 3")
    @ApiModelProperty(value = "排序方式:0->默认排序;1->价格大;2->价格小")
    private Integer sortType;

    public int getPage() {
        return page == null ? 1 : page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public int getSize() {
        return size == null ? 20 : size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    /**
     * 返回偏移量
     *
     * @return
     */
    public int getOffset() {
        int offset = (getPage() - 1) * getSize();
        return Math.max(offset, 0);
    }

}

3.进入正题,搜索模块

组装查询条件LambdaEsQueryWrapper,包含查询条件,排序,分页,高亮,去重

LambdaEsQueryWrapper<Product> wrapper = new LambdaEsQueryWrapper<>();
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(decorateBp(dto));

        //排序
        if (dto.getSortType() != null) {
            switch (dto.getSortType()) {
                case 1:
                    sourceBuilder.sort(SortBuilders.fieldSort(FieldUtils.val(Product::getPrice)).order(SortOrder.DESC));
                    break;
                case 2:
                    sourceBuilder.sort(SortBuilders.fieldSort(FieldUtils.val(Product::getPrice)).order(SortOrder.ASC));
                    break;
                case 3:
                    sourceBuilder.sort(SortBuilders.fieldSort(FieldUtils.val(Product::getSort)).order(SortOrder.DESC));
                    break;
                default:
                    sourceBuilder.sort("_score", SortOrder.DESC);
                    sourceBuilder.sort(SortBuilders.fieldSort(FieldUtils.val(Product::getSort)).order(SortOrder.DESC));
            }
        }
        //分页
        sourceBuilder.from(dto.getOffset());
        sourceBuilder.size(dto.getSize());

        //高亮
        if (StringUtils.isNotBlank(dto.getKeyword())) {
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field(FieldUtils.val(Product::getName));
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");
            sourceBuilder.highlighter(highlightBuilder);
        }
        //品牌信息要去重
        if (brandCon) {
            sourceBuilder.collapse(new CollapseBuilder(FieldUtils.val(Product::getBrandId)));
        }
        wrapper.setSearchSourceBuilder(sourceBuilder);
        log.info("搜索条件{}", brandCon ? "品牌" : "商品");
        return wrapper;

这里是搜索的条件组装,封装BoolQueryBuilder

 /**
     * 组装搜索条件
     *
     * @param dto 入参
     */
    private BoolQueryBuilder decorateBp(EntSearchDTO dto) {
        BoolQueryBuilder bp = QueryBuilders.boolQuery();
        //关键字查询
        if (StringUtils.isNotBlank(dto.getKeyword())) {
            bp.must(QueryBuilders.multiMatchQuery(dto.getKeyword(), FieldUtils.val(Product::getBrandName),
                    FieldUtils.val(Product::getName)).analyzer("ik_smart"));
        }

        //分类ID
        if (!CollectionUtils.isEmpty(dto.getProductCategoryIds())) {
            bp.filter(QueryBuilders.termQuery(FieldUtils.val(Product::getProductCategoryId), dto.getProductCategoryIds()));
        }

        //品牌ID
        if (!CollectionUtils.isEmpty(dto.getBrandIds())) {
            bp.filter(QueryBuilders.termsQuery(FieldUtils.val(Product::getBrandId), dto.getBrandIds()));
        }
        if (StringUtils.isNotBlank(dto.getProductType())) {
            bp.filter(QueryBuilders.termQuery(FieldUtils.val(Product::getProductType), dto.getProductType()));
        }
        //上下架
        if (!CollectionUtils.isEmpty(dto.getPublishStatus())) {
            bp.filter(QueryBuilders.termsQuery(FieldUtils.val(Product::getPublishStatus), dto.getPublishStatus()));
        }

        //价格
        if (dto.getPriceMax() != null) {
            bp.must(QueryBuilders.rangeQuery(FieldUtils.val(Product::getPrice)).lte(dto.getPriceMax()));
        }
        if (dto.getPriceMin() != null) {
            bp.must(QueryBuilders.rangeQuery(FieldUtils.val(Product::getPrice)).gte(dto.getPriceMin()));
        }
        if (StringUtils.isNotBlank(dto.getProductCategoryPath())) {
            bp.must(QueryBuilders.wildcardQuery(FieldUtils.val(Product::getProductCategoryPath), dto.getProductCategoryPath() + "*"));
        }

        //分组
        if (!CollectionUtils.isEmpty(dto.getGroupingIdList())) {
            //俺也不想这样写,后面在优化吧。
            String groupId = FieldUtils.val(Product::getGroupInfos) + "." + FieldUtils.val(ProductGroup::getGroupId);
            String delFlag = FieldUtils.val(Product::getGroupInfos) + "." + FieldUtils.val(ProductGroup::getDelFlag);

            //嵌套查询
            bp.must(QueryBuilders.nestedQuery(FieldUtils.val(Product::getGroupInfos), new TermsQueryBuilder(groupId, dto.getGroupingIdList()), ScoreMode.None));
            bp.must(QueryBuilders.nestedQuery(FieldUtils.val(Product::getGroupInfos), new TermsQueryBuilder(delFlag, false), ScoreMode.None));
        }
        bp.must(QueryBuilders.termQuery(FieldUtils.val(Product::getDelFlag), false));
        bp.must(QueryBuilders.termQuery(FieldUtils.val(Product::getInitStatus), CommonConstants.TWO));
        bp.mustNot(QueryBuilders.termQuery(FieldUtils.val(Product::getBrandName), ProductConstant.DEFUTE_BRAND_NAME_ZH));

        return bp;

    }

这里就是方法的入口,productMapper.selectList(wrapper)和Mybatis-Plus的写法基本上一摸一样,如果不懂可以去看一下EE官方使用方法
Easy-Es文章来源地址https://www.toymoban.com/news/detail-584802.html

public Page<EntProductSearchVO> searchProduct(EntProductSearchDTO dto) {
      
        if (dto == null) {
            return new Page<>();
        }
        //查询条件
        LambdaEsQueryWrapper<Product> wrapper = buildSearchCondition(dto, false);
        List<EntProductSearchVO> vos = new ArrayList<>();

        //是否需要查询数据 默认查询数据
        if (dto.getNeedData() == null || dto.getNeedData()) {
            List<Product> products = productMapper.selectList(wrapper);
            products.forEach(product -> {
                EntProductSearchVO vo = BeanConvertUtil.convert(product,EntProductSearchVO.class);
                vo.setPromotionPrice(product.getOriginalPrice());

                //查询最小的价格的sku
                SkuStock skuStock = product.getSkuInfos().stream().min(Comparator.comparing(SkuStock::getPlatformSellPrice)).orElse(new SkuStock());
                vo.setChannelPriceCode(skuStock.getChannelCode());
                vo.setChannelPriceSkuCode(skuStock.getChannelSkuCode());
                List<EntProductSearchVO.EntSkuVO> skuVOList = new ArrayList<>();
                product.getSkuInfos().forEach(sku->{
                    EntProductSearchVO.EntSkuVO skuVO = BeanConvertUtil.convert(sku,EntProductSearchVO.EntSkuVO.class);
                    skuVO.setPrice(sku.getPlatformSellPrice());
                    skuVO.setPromotionPrice(sku.getOriginalPrice());
                    skuVOList.add(skuVO);
                });
                vo.setSkuList(skuVOList);
                vos.add(vo);
            });
        }
        Long total = productMapper.selectCount(wrapper);

        return new Page<EntProductSearchVO>().setCurrent(dto.getOffset()).setSize(dto.getSize()).setRecords(vos).setTotal(total);
    }

到了这里,关于ES商品搜索实战 Easy-Es搭配原生SearchSourceBuilder的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Springboot整合Easy-Es

    Springboot 2.7.5 JDK 17 Elasticsearch 7.14.0 Easy-Es 1.1.1 《点我进入Easy-Es官网》 PS:目前Easy-Es暂不支持SpringBoot3.X 《安装Elasticsearch教程》 pom.xml application.yml Document实体类 DocumentMapper (官方建议:Easy-Es的Mapper和MyBatis-Plus分开存放) 和MyBatis-Plus差不多都是继承mapper,但是Easy-Es不需要继承Se

    2024年02月10日
    浏览(32)
  • easy-es使用详解与源码解析

    1.git clone后,easy-es-core中的pom中需要引入: 2.easy-es-sample 中提供了基本案例,可以用来解析源码。 3.easy-es-common中的pom里可以看到,它是基于elasticsearch-rest-high-level-client的。 如果不熟悉elasticsearch-rest-high-level-client,建议先熟悉一下。 查询所有:match_all (一般也就是测试用用)

    2024年02月09日
    浏览(49)
  • SpringBoot整合Easy-ES操作演示文档

    1.1 官网 Easy-ES官网: https://www.easy-es.cn/ 官方示例: https://gitee.com/dromara/easy-es/tree/master/easy-es-sample 参考链接: https://blog.51cto.com/yueshushu/6193710 1.2 主要特性 **零侵入:**针对ES官方提供的RestHighLevelClient只做增强不做改变,引入EE不会对现有工程产生影响,使用体验如丝般顺滑。 *

    2024年02月07日
    浏览(40)
  • springboot整合easy-es实现数据的增删改查

    背景 目前公司的一个老项目,查询贼慢,需要想办法提升一下速度,于是就想到了ES,现在尝试一下将ES整合到项目中来提升检索效率。 ES是基于倒排索引实现的,倒排索引中一个表相当于一个索引,表中的每条记录都是一个文档(JSON数据),系统会先对字段数据进行分词,

    2024年01月20日
    浏览(41)
  • 若依整合Easy-Es实现文章列表分页查询

    Easy-Es(简称EE)是一款基于ElasticSearch(简称Es)官方提供的RestHighLevelClient打造的ORM开发框架,在 RestHighLevelClient 的基础上,只做增强不做改变,为简化开发、提高效率而生,您如果有用过Mybatis-Plus(简称MP),那么您基本可以零学习成本直接上手EE,EE是MP的Es平替版,在有些方面甚至比M

    2024年01月16日
    浏览(34)
  • 关于这款开源的ES的ORM框架-Easy-Es适合初学者入手不?

    最近笔者为了捡回以前自学的ES知识,准备重新对ES的一些基础使用做个大致学习总结。然后在摸鱼逛开源社区时无意中发现了一款不错的ElasticSearch插件-Easy-ES,可称之为“ES界的MyBatis-Plus”。联想到之前每次用RestHighLevelClient写一些DSL操作时都很麻烦(复杂点的搜索代码量确实

    2024年02月07日
    浏览(45)
  • Easy-Es框架实践测试整理 基于ElasticSearch的ORM框架

    Easy-Es(简称EE)是一款基于ElasticSearch(简称Es)官方提供的RestHighLevelClient打造的ORM开发框架,在 RestHighLevelClient 的基础上,只做增强不做改变,为简化开发、提高效率而生。EE是Mybatis-Plus的Es平替版,在有些方面甚至比MP更简单,同时也融入了更多Es独有的功能,助力您快速实现各种场

    2024年01月16日
    浏览(45)
  • 【别再做XX外卖啦!和我从零到1编写Mini版Easy-ES】完成一个Mapper模型

    作者:沈自在 代码仓库:https://gitee.com/tian-haoran/mini-easy-es 本节教程分支:https://gitee.com/tian-haoran/mini-easy-es/tree/course_02_create_mapper/ ⚠️注意:本项目会持续更新,直到功能完善 1.1.1 什么是 FactoryBean接口? 很多同学都知道 BeanFactory 接口,这个是大名鼎鼎的Spring中的核心接口,

    2024年02月04日
    浏览(44)
  • 运用easy-es保存数据时,报错:cn.easyes.common.exception.EasyEsException: no such method:

    cn.easyes.common.exception.EasyEsException: no such method:     at cn.easyes.common.utils.ExceptionUtils.eee(ExceptionUtils.java:39)     at cn.easyes.core.cache.BaseCache.lambda$setterMethod$6(BaseCache.java:127)     at cn.easyes.core.cache.BaseCache$$Lambda$2307/809171830.get(Unknown Source)     at java.util.Optional.orElseThrow(Optional.java:29

    2024年02月03日
    浏览(29)
  • elasticsearch(ES)分布式搜索引擎03——(RestClient查询文档,ES旅游案例实战)

    文档的查询同样适用昨天学习的 RestHighLevelClient对象,基本步骤包括: 1)准备Request对象 2)准备请求参数 3)发起请求 4)解析响应 我们以match_all查询为例 3.1.1.发起查询请求 代码解读: 第一步,创建 SearchRequest 对象,指定索引库名 第二步,利用 request.source() 构建DSL,DSL中可

    2024年02月07日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包