Springboot中使用elasticsearch

引言

在我们很多业务场景中,都需要进行检索和匹配,尤其是在电商的商品以及日志搜索中尤为明显,而现在成熟的搜索工具我更喜欢使用elasticsearch,它的搜索功能特别好用,并且易上手,而且springboot中也有对应的starter,使用简单方便

依赖引入

这里的版本就不做多的讨论了,可以使用springboot的parent进行版本管理

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>

配置文件

spring:
  elasticsearch:
    rest:
      uris: 替换成你的es (ip+端口)
      username: elastic
      password: Hxxx
test:
  es:
    index-name: product_test1

开始使用

其他的配置就不需要列举出来了,这里直接开始使用

1、实体创建

/**
 * indexName要全小写
 * 如果需要使用spel表达式,只能获取配置类中的属性,不能直接使用${}获取配置文件中的值
 */
@Data
// @Document(indexName = "#{@esConfig.getIndexName()}")
@Document(indexName = "product")
public class ProductVO {

    /**
     * 商品id
     */
    @Id
    private String spuId;

    /**
     * 分类id
     * 指定为关键词不会进行分词
     */
    @Field(type = FieldType.Keyword, store = true)
    private String categoryId;

    /**
     * 商品名称
     * 指定为文档,并且分词器为ik,查询分词为ik_smart
     */
    @Field(type = FieldType.Text, analyzer = "ik_max_word", store = true, searchAnalyzer = "ik_smart")
    private String spuName;

    /**
     * 属性值
     * 指定为实体
     */
    @Field(type = FieldType.Nested, store = true)
    private List<ProductAttrVO> attrEntities;

    /**
     * 序号
     */
    @Field(type = FieldType.Keyword)
    private Integer seq;

    /**
     * 商品卖点
     */
    @Field(type = FieldType.Text, analyzer = "ik_max_word", store = true)
    private String sellingPoint;

    /**
     * 商品价格
     */
    private Long priceFee;

    /**
     * 分区code
     */
    @Field(type = FieldType.Keyword, store = true)
    private String partitionCode;

    /**
     * 标签
     */
    @Field(type = FieldType.Keyword, store = true)
    private List<String> labels;
}

2、查询

要说查询,无非就是几种精确匹配,模糊匹配,分词匹配,或者条件,排序,而es中多了一个名叫做分数的东西,我们可以通过权重来提高他的分数

 public Page<ProductVO> page(ProductPageQueryDTO dto) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        // 关键词
        String keywords = dto.getKeywords();
        if (StrUtil.isNotBlank(keywords)) {
        	// 多条件or查询,boost是设置权重,并不是越高得分就越高,默认为1,越高比例越高,只是加分
        	// wildcardQuery是通配符匹配,"*测试*" 就相当于mysql中的like '%测试%'
        	// matchQuery会进行分词处理,termQuery不会进行分词(指查询条件分词)查询的关键词字段可以为数组,比如说 lables
        	// minimumShouldMatch 指的是最少匹配多少个,1就是为至少匹配一个
            BoolQueryBuilder keywordShouldQuery = QueryBuilders.boolQuery()
                    .should(QueryBuilders.wildcardQuery("spuName", StrUtil.concat(Boolean.TRUE, "*", keywords.toLowerCase(), "*")).boost(2))
                    .should(QueryBuilders.matchQuery("spuName", keywords).boost(3))
                    .should(QueryBuilders.wildcardQuery("labels", StrUtil.concat(Boolean.TRUE, "*", keywords, "*")).boost(6))
                    .minimumShouldMatch(1);
            // 多条件
            queryBuilder.must(keywordShouldQuery);
        }
        // 属性匹配
        NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrEntities", QueryBuilders.matchQuery("attrEntities.attrValueName", keywords), ScoreMode.None);
        // 价格
        // must() 为必须满足当前条件
        // rangeQuery() 就是指的大于小于(范围匹配)
        Long max = dto.getMax();
        Long min = dto.getMin();
        if (ObjectUtil.isNotNull(max) && max > 0 && (ObjectUtil.isNull(min) || min == 0 || min > max)) {
            queryBuilder.must(QueryBuilders.rangeQuery("priceFee").lte(max));
        } else if (ObjectUtil.isNotNull(min) && min > 0 && (ObjectUtil.isNull(max) || max == 0 || max < min)) {
            queryBuilder.must(QueryBuilders.rangeQuery("priceFee").gte(min));
        } else if (ObjectUtil.isNotNull(max) && ObjectUtil.isNotNull(min) && max >= min && min > 0) {
            queryBuilder.must(QueryBuilders.rangeQuery("priceFee").lte(max).gte(min));
        }    
        if (ObjectUtil.isNotNull(dto.getPriceAsc())) {
            FieldSortBuilder fieldSortBuilder = SortBuilders.fieldSort("priceFee").order(dto.getPriceAsc() ? SortOrder.ASC : SortOrder.DESC);
            nativeSearchQueryBuilder.withSort(fieldSortBuilder);
        }
        // 排序
        if (StrUtil.isNotEmpty(partitionCode) && StrUtil.equalsIgnoreCase("MRTJ", partitionCode)) {
            FieldSortBuilder fieldSortBuilder = SortBuilders.fieldSort("seq").order(SortOrder.DESC);
            nativeSearchQueryBuilder.withSort(fieldSortBuilder);
        } else {
            nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC));
        }
        // 分页查询
        nativeSearchQueryBuilder
                .withPageable(PageRequest.of(dto.getPageNum(), dto.getPageSize()))
                .withQuery(queryBuilder);
        NativeSearchQuery query = nativeSearchQueryBuilder.build();
        return queryPage(query, dto.getPageNum(), dto.getPageSize());
    }
    
    /**
     * 构建成mybatis-plus的分页
     */
	private Page<ProductVO> queryPage(NativeSearchQuery query, Integer pageNum, Integer pageSize) {
        AggregatedPage<ProductVO> result = elasticsearchRestTemplate.queryForPage(query, ProductVO.class);
        Page<ProductVO> page = new Page<>(pageNum, pageSize);
        page.setRecords(result.getContent());
        page.setTotal(result.getTotalElements());
        return page;
    }

这里就只介绍一下分页查询,其他的查询提供了直接的方法进行查询比如说列表和单个

注意

  • 权重只是增加得分,并不一定能够拿到自己想要的
  • ik分词器只是按照他的规则来分词,要想达到自己想要的效果,只能自己维护分词器
  • 使用ik分词器要在es中安装
  • 写入和删除时,一定要注意默认1000,一定要注意这个
  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值