• Spring data elasticsearch使用


    一、准备

    1.添加依赖

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

    2.application.yml

    spring:
      application:
        name: search-service
      data:
        elasticsearch:
          cluster-name: elasticsearch
          cluster-nodes: 192.168.25.129:9300

    3.实体类

    @Data
    @Document(indexName = "goods", type = "_doc", shards = 1, replicas = 0)
    public class Goods {
        @Idprivate Long id;
        @Field(type = FieldType.text, analyzer = "ik_max_word")
        private String all;
        @Field(type = FieldType.keyword, index = false)
        private String subTitle;private Long brandId;private Long cid1;private Long cid2;private Long cid3;private Date createTime;private List<Long> price;
        @Field(type = FieldType.keyword, index = false)
        private String skus;private Map<String, Object> specs;
    }
    @Data
    @Document(indexName = "bulk_goods", type = "_doc", shards = 1, replicas = 0)
    public class Goods {
        @Id
        private Long id;
        @ApiModelProperty("商品名称")
        @Field(type = FieldType.text, analyzer = "ik_max_word")
        private String name;
        @Field(type = FieldType.text)
        @ApiModelProperty("供应商名称")
        private String SellerName;
        @Field(type = FieldType.keyword)
        @ApiModelProperty("规格")
        private String packsize;
        @ApiModelProperty("自提或配送价格")
        private BigDecimal price;
        @ApiModelProperty("单位名称")
        private BigDecimal unitName;
        @ApiModelProperty("类型 1:自提 2:配送")
        private String type;
        @ApiModelProperty("起批数量")
        private Integer wholesaleNum;
        @ApiModelProperty("商家地址 自提:提货地址 配送:供应商地址")
        private String merchAddress;
        @ApiModelProperty("我的卖价")
        private String mySellPrice;
        @ApiModelProperty("最小批量预计可获利")
        private String profit;
        @ApiModelProperty("商品图片")
        private String headUrl;
    }
    @Data
    @Document(indexName = "bulk_goods_bank", type = "_doc", shards = 1, replicas = 0)
    public class GoodsBank {
        @Id
        private Long id;
        @ApiModelProperty("模板名称")
        @Field(type = FieldType.text, analyzer = "ik_max_word")
        private String name;
        @Field(type = FieldType.keyword)
        @ApiModelProperty("品牌")
        private String brand;
        @ApiModelProperty("规格")
        private String packsize;
        @ApiModelProperty("原图")
        private String headurl;
        @ApiModelProperty("缩略图")
        private String smallheadurl;
    }
    • @Document 作用在类,标记实体类为文档对象,一般有两个属性
      • indexName:对应索引库名称
      • type:对应在索引库中的类型
      • shards:分片数量,默认5
      • replicas:副本数量,默认1
    • @Id 作用在成员变量,标记一个字段作为id主键
    • @Field 作用在成员变量,标记为文档的字段,并指定字段映射属性:
      • type:字段类型,取值是枚举:FieldType
      • index:是否索引,布尔类型,默认是true
      • store:是否存储,布尔类型,默认是false
      • analyzer:分词器名称

    二.、索引操作

    首先注入ElasticsearchTemplate

        @Resource
        private ElasticsearchTemplate elasticsearchTemplate;

    ● 创建索引

    elasticsearchTemplate.createIndex(Goods.class);

    ● 配置映射

    elasticsearchTemplate.putMapping(Goods.class);

    ● 删除索引

    //根据类
    elasticsearchTemplate.deleteIndex(Goods.class);
    //根据索引名
    elasticsearchTemplate.deleteIndex("goods");

    三、文档操作

    1.定义接口。也是SpringData风格

    public interface ItemRepository extends ElasticsearchRepository<Item,Long> {
    }

    2.注入

    @Autowired
    private ItemRepository itemRepository;

    ● 新增文档

    Item item = new Item(1L, "小米手机7", " 手机",
                             "小米", 3499.00, "http://image.leyou.com/13123.jpg");
    itemRepository.save(item);

    ● 批量新增

        List<Item> list = new ArrayList<>();
        list.add(new Item(2L, "坚果手机R1", " 手机", "锤子", 3699.00, "http://image.leyou.com/123.jpg"));
        list.add(new Item(3L, "华为META10", " 手机", "华为", 4499.00, "http://image.leyou.com/3.jpg"));
        // 接收对象集合,实现批量新增
        itemRepository.saveAll(list);

    四、 基本搜索

    ● 基本查询。

    例:

        // 查询全部,并安装价格降序排序
        Iterable<Item> items = this.itemRepository.findAll(Sort.by(Sort.Direction.DESC, "price"));
        items.forEach(item-> System.out.println(item));

    ● 自定义查询

    KeywordSampleElasticsearch Query String
    And findByNameAndPrice {"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
    Or findByNameOrPrice {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
    Is findByName {"bool" : {"must" : {"field" : {"name" : "?"}}}}
    Not findByNameNot {"bool" : {"must_not" : {"field" : {"name" : "?"}}}}
    Between findByPriceBetween {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
    LessThanEqual findByPriceLessThan {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
    GreaterThanEqual findByPriceGreaterThan {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
    Before findByPriceBefore {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
    After findByPriceAfter {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
    Like findByNameLike {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
    StartingWith findByNameStartingWith {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
    EndingWith findByNameEndingWith {"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}}
    Contains/Containing findByNameContaining {"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}}
    In findByNameIn(Collection<String>names) {"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}
    NotIn findByNameNotIn(Collection<String>names) {"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}}
    Near findByStoreNear Not Supported Yet !
    True findByAvailableTrue {"bool" : {"must" : {"field" : {"available" : true}}}}
    False findByAvailableFalse {"bool" : {"must" : {"field" : {"available" : false}}}}
    OrderBy findByAvailableTrueOrderByNameDesc {"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}}

    例:

    public interface ItemRepository extends ElasticsearchRepository<Item,Long> {
    
        /**
         * 根据价格区间查询
         * @param price1
         * @param price2
         * @return
         */
        List<Item> findByPriceBetween(double price1, double price2);
    }
    public interface GoodsBankRepository extends ElasticsearchRepository<GoodsBank, Long> {
        /**
         * 根据商品模板名称或品牌搜索
         *
         * @param name  模板名称
         * @param brand 品牌
         * @return
         */
        List<GoodsBank> findByNameOrBrand(String name, String brand);
    }

    五、高级查询

    ● 词条查询

            MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("all", "小米");
            // 执行查询
            Iterable<Goods> goods = this.goodsRepository.search(queryBuilder);

    ● 自定义查询

            // 构建查询条件
            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
            // 添加基本的分词查询
            queryBuilder.withQuery(QueryBuilders.matchQuery("all", "小米"));
            // 执行搜索,获取结果
            Page<Goods> goods = this.goodsRepository.search(queryBuilder.build());
            // 打印总条数
            System.out.println(goods.getTotalElements());
            // 打印总页数
            System.out.println(goods.getTotalPages());

    ● 分页查询

            // 构建查询条件
            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
            // 添加基本的分词查询
            queryBuilder.withQuery(QueryBuilders.termQuery("all", "手机"));
    
            // 初始化分页参数
            int page = 0;
            int size = 3;
            // 设置分页参数
            queryBuilder.withPageable(PageRequest.of(page, size));
    
            // 执行搜索,获取结果
            Page<Goods> goods = this.goodsRepository.search(queryBuilder.build());
            // 打印总条数
            System.out.println(goods.getTotalElements());
            // 打印总页数
            System.out.println(goods.getTotalPages());
            // 每页大小
            System.out.println(goods.getSize());
            // 当前页
            System.out.println(goods.getNumber());

    ● 排序

            // 构建查询条件
            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
            // 添加基本的分词查询
            queryBuilder.withQuery(QueryBuilders.termQuery("all", "手机"));
    
            // 排序
            queryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC));
    
            // 执行搜索,获取结果
            Page<Goods> goods = this.goodsRepository.search(queryBuilder.build());
            // 打印总条数
            System.out.println(goods.getTotalElements());

    ● 聚合为桶

            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
            // 不查询任何结果
            queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
            // 1、添加一个新的聚合,聚合类型为terms,聚合名称为brands,聚合字段为brand
            queryBuilder.addAggregation(AggregationBuilders.terms("brands").field("brandId"));
            // 2、查询,需要把结果强转为AggregatedPage类型
            AggregatedPage<Goods> aggPage = (AggregatedPage<Goods>) this.goodsRepository.search(queryBuilder.build());
            // 3、解析
            // 3.1、从结果中取出名为brands的那个聚合,
            // 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
            LongTerms agg = (LongTerms) aggPage.getAggregation("brands");
            // 3.2、获取桶
            List<LongTerms.Bucket> buckets = agg.getBuckets();
            // 3.3、遍历
            for (LongTerms.Bucket bucket : buckets) {
                // 3.4、获取桶中的key,即品牌名称
                System.out.println(bucket.getKeyAsString());
                // 3.5、获取桶中的文档数量
                System.out.println(bucket.getDocCount());
            }

    ● 嵌套聚合,求平均值

            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
            // 不查询任何结果
            queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
            // 1、添加一个新的聚合,聚合类型为terms,聚合名称为brands,聚合字段为brand
            queryBuilder.addAggregation(AggregationBuilders.terms("brands").field("brandId")
                            .subAggregation(AggregationBuilders.avg("priceAvg").field("price"))); // 在品牌聚合桶内进行嵌套聚合,求平均值
            // 2、查询,需要把结果强转为AggregatedPage类型
            AggregatedPage<Goods> aggPage = (AggregatedPage<Goods>) this.goodsRepository.search(queryBuilder.build());
            // 3、解析
            // 3.1、从结果中取出名为brands的那个聚合,
            // 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
            LongTerms agg = (LongTerms) aggPage.getAggregation("brands");
            // 3.2、获取桶
            List<LongTerms.Bucket> buckets = agg.getBuckets();
            // 3.3、遍历
            for (LongTerms.Bucket bucket : buckets) {
                // 3.4、获取桶中的key,即品牌名称  3.5、获取桶中的文档数量
                System.out.println(bucket.getKeyAsString() + ",共" + bucket.getDocCount() + "台");
    
                // 3.6.获取子聚合结果:
                InternalAvg avg = (InternalAvg) bucket.getAggregations().asMap().get("priceAvg");
                System.out.println("平均售价:" + avg.getValue());
            }

    附:配置搜索结果不显示为null字段:

    spring:
      jackson:
        default-property-inclusion: non_null # 配置json处理时忽略空值
  • 相关阅读:
    线段树优化dp——牛客多校第一场I(好题)
    字符串dp——牛客多校第五场G
    凑出和相等的k组数,玄学结论——hdu6616
    主席树/线段树模拟归并排序+二分答案(好题)——hdu多校第4场08
    思维题+贪心——牛客多校第一场C
    线性基算贡献——19牛客多校第一场H
    俞敏洪语录
    睡眠长短决定寿命!人每天应该睡多少小时
    Java程序员从笨鸟到菜鸟之(三十一)大话设计模式(一)设计模式遵循的七大原则
    spring源码剖析(五)利用AOP实现自定义Spring注解
  • 原文地址:https://www.cnblogs.com/naixin007/p/12231993.html
Copyright © 2020-2023  润新知