个人笔记谷粒商城项目第八节(162集~180集)

第162集,第163集 读写锁

读写者逻辑:保证一定能读到最新数据,修改期间,写锁是一个排它锁(互斥锁、独享锁),读锁是一个共享锁 写锁没释放读锁必须等待。

读 + 读 :相当于无锁,并发读,只会在Redis中记录好,所有当前的读锁。他们都会同时加锁成功

写 + 读 :必须等待写锁释放

写 + 写 :阻塞方式。

读 + 写 :有读锁。

写也需要等待 只要有读或者写的存都必须等待。

/**
     * 保证一定能读到最新数据,修改期间,写锁是一个排它锁(互斥锁、独享锁),读锁是一个共享锁
     * 写锁没释放读锁必须等待
     * 读 + 读 :相当于无锁,并发读,只会在Redis中记录好,所有当前的读锁。他们都会同时加锁成功
     * 写 + 读 :必须等待写锁释放
     * 写 + 写 :阻塞方式
     * 读 + 写 :有读锁。写也需要等待
     * 只要有读或者写的存都必须等待
     * @return
     */
    @GetMapping(value = "/write")
    @ResponseBody
    public String writeValue() {
        String s = "";
        RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");
        RLock rLock = readWriteLock.writeLock();
        try {
            //1、改数据加写锁,读数据加读锁
            rLock.lock();
            s = UUID.randomUUID().toString();
            ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
            ops.set("writeValue",s);
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
        }

        return s;
    }

    @GetMapping(value = "/read")
    @ResponseBody
    public String readValue() {
        String s = "";
        RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");
        //加读锁
        RLock rLock = readWriteLock.readLock();
        try {
            rLock.lock();
            ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
            s = ops.get("writeValue");
            try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
        }

        return s;
    }

第164集 闭锁测试

类似于放学,锁门,必须等所有人都走了,才能进行锁门。



    /**
     * 放假、锁门
     * 1班没人了
     * 5个班,全部走完,我们才可以锁大门
     * 分布式闭锁
     */

    @GetMapping(value = "/lockDoor")
    @ResponseBody
    public String lockDoor() throws InterruptedException {

        RCountDownLatch door = redisson.getCountDownLatch("door");
        door.trySetCount(5);
        door.await();       //等待闭锁完成

        return "放假了...";
    }

    @GetMapping(value = "/gogogo/{id}")
    @ResponseBody
    public String gogogo(@PathVariable("id") Long id) {
        RCountDownLatch door = redisson.getCountDownLatch("door");
        door.countDown();       //计数-1

        return id + "班的人都走了...";
    }

第165集 信号量机制

就是操作系统的PV操作。

/**
     * 车库停车
     * 3车位
     * 信号量也可以做分布式限流
     */
    @GetMapping(value = "/park")
    @ResponseBody
    public String park() throws InterruptedException {

        RSemaphore park = redisson.getSemaphore("park");
        park.acquire();     //获取一个信号、获取一个值,占一个车位
        boolean flag = park.tryAcquire();

        if (flag) {
            //执行业务
        } else {
            return "error";
        }

        return "ok=>" + flag;
    }

    @GetMapping(value = "/go")
    @ResponseBody
    public String go() {
        RSemaphore park = redisson.getSemaphore("park");
        park.release();     //释放一个车位
        return "ok";
    }

第166集 缓存与数据库一致性问题

由于,我们只是给缓存加上锁,要求其同一时间只有一个进入数据库。但是如果有有人修改数据库,我们是不管的。这时我们要注意缓存与数据库的一致性问题。

解决方案,有缺陷,没缺陷的是锁的方式:

1、双写方式

由于卡顿等原因,导致写缓存2在最前,写缓存1在后面就出现了不一致脏数据问题:这是暂时性的脏数据问题,但是在数据稳定,缓存过期以后,又能得到最新的正确数据

2、失效模式 

这样流程导致更新的缓存得到的是,错误的数据。

总结

论是双写模式还是失效模式,都会导致缓存的不一致问题。即多个实例同时更新会出事。怎么办?如果是用户纬度数据(订单数据、用户数据),这种并发几率非常小,不用考虑这个问题,缓存数据加上过期时间,每隔一段时间触发读的主动更新即可 2、如果是菜单,商品介绍等基础数据,也可以去使用canal订阅binlog的方式。3、缓存数据+过期时间也足够解决大部分业务对于缓存的要求。4、通过加锁保证并发读写,写写的时候按顺序排好队。读读无所谓。所以适合使用读写锁。(业务不关心脏数据,允许临时脏数据可忽略);

·总结:·我们能放入缓存的数据本就不应该是实时性、一致性要求超高的。所以缓存数据的时候加上过期时间,保证每天拿到当前最新数据即可。·我们不应该过度设计,增加系统的复杂性·遇到实时性、一致性要求高的数据,就应该查数据库,即使慢点。

第167集 springcache简介

缓存管理器,这个就是管理缓存的东西

第168集 整合

pom文件

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
spring
  cache:
    type: redis
@EnableCaching
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients("com.buu.gulimall.product.feign")
@MapperScan("com.buu.gulimall.product.dao")
@Slf4j
public class GulimallProductApplication {
    public static void main(String[] args) {
        SpringApplication.run(GulimallProductApplication.class, args);
    }
}
@Cacheable("{category}")
    @Override
    public List<CategoryEntity> getLevel1Categorys() {
        System.out.println("getLevel1Categorys........");
        long l = System.currentTimeMillis();
        List<CategoryEntity> categoryEntities = this.baseMapper.selectList(
                new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
        System.out.println("消耗时间:"+ (System.currentTimeMillis() - l));
        return categoryEntities;
    }

注解

@Cacheable:Triggers

@CacheEvict:Triggerscacheeviction.:触发将数据从缓存删除的操作

@CachePut:Updates the cache without interfering with the method execution.:不影响方法执行更新缓存

@Caching: Regroups multiple cache operationsstobeappliedonamethod.:组合以上多个操作

@CacheConfig: Sharessomecommon cache-related settings at cLass-Level.:在类级别共享缓存的相同配置

成功运行

第169集 @cacheable细节

默认行为、

1)、如果缓存中有,方法不用调用。

2)、key默认自动生感:存的名字(自主生成的hey值)

3)、存的value的值。默认使用序列化机制。将序列化后的数据存到redis

4)、默认时间-1

要将其转化为自定义的,指定生成的key,指定存的数据的存活时间,数据保存为json

spring:
    cache:
        type: redis
            redis:
              time-to-live: 3600000
@Cacheable(value={"category"},key = "'levelCategory'")
    @Override
    public List<CategoryEntity> getLevel1Categorys() {
        System.out.println("getLevel1Categorys........");
        long l = System.currentTimeMillis();
        List<CategoryEntity> categoryEntities = this.baseMapper.selectList(
                new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
        System.out.println("消耗时间:"+ (System.currentTimeMillis() - l));
        return categoryEntities;
    }

第170集 转化为json的方式

用配置类的方式进行处理,同时设置空值也进行保存。

@EnableConfigurationProperties(CacheProperties.class)
@Configuration
@EnableCaching
public class MyCacheConfig {

    // @Autowired
    // public CacheProperties cacheProperties;

    /**
     * 配置文件的配置没有用上
     * @return
     */
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {

        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        // config = config.entryTtl();
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        //将配置文件中所有的配置都生效
        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixKeysWith(redisProperties.getKeyPrefix());
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }

        return config;
    }

}
spring.cache.type=redis

#spring.cache.cache-names=qq,毫秒为单位
spring.cache.redis.time-to-live=3600000

#如果指定了前缀就用我们指定的前缀,如果没有就默认使用缓存的名字作为前缀
#spring.cache.redis.key-prefix=CACHE_
spring.cache.redis.use-key-prefix=true

#是否缓存空值,防止缓存穿透
spring.cache.redis.cache-null-values=true

第171集 测试springcache的双写,删除模式

@CacheEvict(value = "category",key = "'getLevel1Categorys'")意思就是方法执行完之后执行删除缓存getLevel1Categorys,删除模式

@CachePut双写模式

@Override
    @CacheEvict(value = "category",key = "'getLevel1Categorys'")
    @Transactional
    public void updateCascade(CategoryEntity category) {
        this.updateById(category);
        categoryBrandRelationService.updateCategory(category.getCatId(),category.getName());
    }

 组合多种注解进行操作,更新完之后同时删除两个缓存。

@Override
    @Caching(evict = {
            @CacheEvict(value = "category",key = "'getLevel1Categorys'")
            @CacheEvict(value = "category",key = "'getCategoryJson'")
    })
    @Transactional
    public void updateCascade(CategoryEntity category) {
        this.updateById(category);
        categoryBrandRelationService.updateCategory(category.getCatId(),category.getName());
    }

第172集 springcache的不足

总结:常规缓存的使用可以用springcache,读多写少的情况可以用,但是要求高的数据,特殊数据要特殊处理。读模式的缓存击穿,穿透,雪崩都基础的解决了,但是写模式的一致性没处理。

第173集 检索服务的搭建

这个和之前配置首页面的操作类似

nginx改个nginx.conf

 该spring-gateway的配置,直接用这个,以后也不用改了

spring:
  cloud:

#    sentinel:
#      transport:
#        #配置sentinel dashboard地址
#        dashboard: localhost:8080

    gateway:
      routes:
        #        - id: test_route
        #          uri: https://www.baidu.com
        #          predicates:
        #            - Query=uri,baidu
        #
        #        - id: qq_route
        #          uri: https://www.qq.com
        #          predicates:
        #            - Query=uri,qq

        - id: product_route
          uri: lb://gulimall-product
          predicates:
            - Path=/api/product/**
          filters:
            - RewritePath=/api/(?<segment>/?.*),/$\{segment}

        - id: coupon_route
          uri: lb://gulimall-coupon
          predicates:
            - Path=/api/coupon/**
          filters:
            - RewritePath=/api/(?<segment>/?.*),/$\{segment}

        - id: third_party_route
          uri: lb://gulimall-third-party
          predicates:
            - Path=/api/thirdparty/**
          filters:
            - RewritePath=/api/thirdparty/(?<segment>/?.*),/$\{segment}

        - id: member_route
          uri: lb://gulimall-member
          predicates:
            - Path=/api/member/**
          filters:
            - RewritePath=/api/(?<segment>/?.*),/$\{segment}

        - id: ware_route
          uri: lb://gulimall-ware
          predicates:
            - Path=/api/ware/**
          filters:
            - RewritePath=/api/(?<segment>/?.*),/$\{segment}

        - id: order_route
          uri: lb://gulimall-order
          predicates:
            - Path=/api/order/**
          filters:
            - RewritePath=/api/(?<segment>/?.*),/$\{segment}

        - id: admin_route
          uri: lb://renren-fast
          predicates:
            - Path=/api/**
          filters:
            - RewritePath=/api/(?<segment>/?.*), /renren-fast/$\{segment}

        - id: gulimall_host_route
          uri: lb://gulimall-product
          predicates:
            - Host=gulimall.com,item.gulimall.com

        - id: gulimall_search_route
          uri: lb://gulimall-search
          predicates:
            - Host=search.gulimall.com

        - id: gulimall_auth_route
          uri: lb://gulimall-auth-server
          predicates:
            - Host=auth.gulimall.com

        - id: gulimall_cart_route
          uri: lb://gulimall-cart
          predicates:
            - Host=cart.gulimall.com

        - id: gulimall_order_route
          uri: lb://gulimall-order
          predicates:
            - Host=order.gulimall.com

        - id: gulimall_member_route
          uri: lb://gulimall-member
          predicates:
            - Host=member.gulimall.com

        - id: gulimall_seckill_route
          uri: lb://gulimall-seckill
          predicates:
            - Host=seckill.gulimall.com



#暴露所有端点
management:
  endpoints:
    web:
      exposure:
        include: '*'

  ## 前端项目,/api
## http://localhost:88/api/captcha.jpg   http://localhost:8080/renren-fast/captcha.jpg
## http://localhost:88/api/product/category/list/tree http://localhost:10000/product/category/list/tree

改hosts文件 C:\Windows\System32\drivers\etc;

之后还要修改前端文件,就按照的步骤改好就行, 由于太长我就不附了。

成功

第174集 调整页面跳转

有些报错问题解决

放到nginx里面的catelog.js文件出错,改为gulimall.com,

product服务的index.html修改

之后要跟老师修改的保持一致,修改完这些过后,如果还不起效果,则使用无痕浏览器 ,再试试。

第175集 检索查询参数模型

这一节主要分析有哪些请求参数

keyword=‘小米’&sort=saleCount_desc/asc&hasStock=0/1&skuPrice=400_1900&brandld=1&catalog3ld=1&attrs=1_3G:4G:5G&attrs=2_骁龙845&attrs=4_高清屏

1、全文检索:skuTitle -> keyword

2、排序:saleCount(销量)、hotScoree(热度分)、skuPrice(价格)

3、过滤:hasStock、skuPrice区间、brandld、catalog3ld、attrs

4、聚合:attrs

构造一个请求体类

@Data
public class SearchParam {

    /**
     * 页面传递过来的全文匹配关键字
     */
    private String keyword;

    /**
     * 品牌id,可以多选
     */
    private List<Long> brandId;

    /**
     * 三级分类id
     */
    private Long catalog3Id;

    /**
     * 排序条件:sort=price/salecount/hotscore_desc/asc
     */
    private String sort;

    /**
     * 是否显示有货
     */
    private Integer hasStock;

    /**
     * 价格区间查询
     */
    private String skuPrice;

    /**
     * 按照属性进行筛选
     */
    private List<String> attrs;

    /**
     * 页码
     */
    private Integer pageNum = 1;

    /**
     * 原生的所有查询条件
     */
    private String _queryString;


}

第176集 返回结果模型

这就是页面需要返回的结果。

@Data
public class SearchResult {

    /**
     * 查询到的所有商品信息
     */
    private List<SkuEsModel> product;


    /**
     * 当前页码
     */
    private Integer pageNum;

    /**
     * 总记录数
     */
    private Long total;

    /**
     * 总页码
     */
    private Integer totalPages;

    private List<Integer> pageNavs;

    /**
     * 当前查询到的结果,所有涉及到的品牌
     */
    private List<BrandVo> brands;

    /**
     * 当前查询到的结果,所有涉及到的所有属性
     */
    private List<AttrVo> attrs;

    /**
     * 当前查询到的结果,所有涉及到的所有分类
     */
    private List<CatalogVo> catalogs;


    //===========================以上是返回给页面的所有信息============================//


    /* 面包屑导航数据 */
    private List<NavVo> navs;

    @Data
    public static class NavVo {
        private String navName;
        private String navValue;
        private String link;
    }


    @Data
    public static class BrandVo {

        private Long brandId;

        private String brandName;

        private String brandImg;
    }


    @Data
    public static class AttrVo {

        private Long attrId;

        private String attrName;

        private List<String> attrValue;
    }


    @Data
    public static class CatalogVo {

        private Long catalogId;

        private String catalogName;
    }
}

第177集 检索DSL测试

我这里出现个bug

就是发现我没有product索引,后来发现我用的老师资料的代码,他用的是gulimall-product作为索引,所以我在商品上架的时候,product索引没有数据。我将这里改为product,并且重新添加一般索引mapping,并且重新上架商品。

 之后编写DSL语句

bool可以搭配must,should等内容进行多条件查询,搜索出标题为小米的数据

 http://127.0.0.1:9200/gulimall_product/_search

{
	"query":{
		"bool":{
			"must":[
                {
				"match":{
					"skuTitle":"小米"
				}
			}
            ]
		}
    }
}

 之后我们规定不参与评分的放筛选里面,成功,这代表搜索catalogId=225(手机),以及品牌id为1,2,10,且题目为小米的数据。这个逻辑其实有点怪,你都查小米了,还要查手机品牌为1,2,10的数据。可能小米代表全局搜索吧,可能有吃的小米,手机小米,他们品牌不一样。

 http://127.0.0.1:9200/product/_search

{
	"query":{
		"bool":{
			"must":[
                {
				"match":{
					"skuTitle":"小米"
				}
			}
            ],
            "filter":[
                {
                    "term":{
                        "catalogId":"225"
                    }
                },
                {
                    "terms":{
                        "brandId":["1","2","10"]
                    }
                }
            ]
	    }
    }
}

下一轮筛选 

这里由于太长了,把nasted的处理挑出来了。terms或 term且

{
  "query": {
    "bool": {
      "filter":{
          "nested":{
              "path":"attrs",
              "query":{
                  "bool":{
                      "must":[
                          {
                              "term":{
                                  "attrs.attrId":{
                                      "value": 15
                                  }
                              }
                          },
                          {
                              "terms":{
                                  "attrs.attrValue":[
                                      "海思(Hisilicon)","HUAWEI Kirin 980"
                                  ]
                              }
                          }
                      ]
                  }
              }
          }
      }
    }
  }
}

排序

{
    "query": {
        "match_all": {}
    },
    "sort": {
        "skuPrice": {
            "order": "desc"
        }
    }
}

范围区间

{
    "query": {
        "match_all": {}
        "range": {
            "skuPrice": {
                "gte": 0,
                "lte": 6000
            }
        }
    }
}

 分页

{
    "query": {
        "match_all": {}
    },
    "from":0,
    "size":20
}

高亮

{
    "query": {
        "match_all": {}
    },
    "highlight":{
        "fields":{
            "skuTitle":{}
        }
    }
}

将这些所有都合并成一个DSL

get  http://127.0.0.1:9200/product/_search
{
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "skuTitle": "锤子手机"
                    }
                }
            ],
            "filter": [
                {
                    "term": {
                        "catalogId": "225"
                    }
                },
                {
                    "term": {
                        "hasStock": "false"
                    }
                },
                {
                    "terms": {
                        "brandId": [
                            "1",
                            "2",
                            "10"
                        ]
                    }
                },
                {
                    "nested": {
                        "path": "attrs",
                        "query": {
                            "bool": {
                                "must": [
                                    {
                                        "term": {
                                            "attrs.attrId": {
                                                "value": 15
                                            }
                                        }
                                    },
                                    {
                                        "terms": {
                                            "attrs.attrValue": [
                                                "海思(Hisilicon)",
                                                "HUAWEI Kirin 980"
                                            ]
                                        }
                                    }
                                ]
                            }
                        }
                    }
                },
                {
                    "range": {
                        "skuPrice": {
                            "gte": 0,
                            "lte": 6000
                        }
                    }
                }
            ]
        }
    },
    "sort": {
        "skuPrice": {
            "order": "desc"
        }
    },
    "from":0,
    "size":20,
    "highlight":{
        "fields":{
            "skuTitle":{}
        }
    }
}

上面内容包括:模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存),排序,分页,高亮等等内容。

第178集 聚合分析

聚合操作

get http://127.0.0.1:9200/product/_search
{
    "query": {
        "match_all": {}
    },
    "aggs": {
        "brand_agg": {
            "terms": {
                "field": "catalogId",
                "size": 10
            }
        }
    }
}

之后要改映射了,终于知道为啥上一集的老师的代码索引是gulimall_product了,设置gulimall_product的索引。

{
  "mappings": {
    "properties": {
      "skuId": {
        "type": "long"
      },
      "spuId": {
        "type": "long"
      },
      "skuTitle": {
        "type": "text",
        "analyzer": "ik_smart"
      },
      "skuPrice": {
        "type": "keyword"
      },
      "skuImg": {
        "type": "keyword"
      },
      "saleCount": {
        "type": "long"
      },
      "hosStock": {
        "type": "boolean"
      },
      "hotScore": {
        "type": "long"
      },
      "brandId": {
        "type": "long"
      },
      "catelogId": {
        "type": "long"
      },
      "brandName": {
        "type": "keyword"
      },
      "brandImg": {
        "type": "keyword"
      },
      "catalogName": {
        "type": "keyword"
      },
      "attrs": {
        "type": "nested",
        "properties": {
          "attrId": {
            "type": "long"
          },
          "attrName": {
            "type": "keyword"
          },
          "attrValue": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

迁移数据代码,或者修改数据库表pms_spu_info中上架字段改成0,前端重新上架即可。

post http://127.0.0.1:9200/_reindex

{
    "source": {
        "index": "product"
    },
    "dest": {
        "index": "gulimall_product"
    }
}

根据brandId,brandName,brandImg这三个聚合,就是有相同数据的合并到一起,相当于group by

嵌入式聚合,反正也是合并。大致了解即可。

汇总DSL

{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "skuTitle": "华为"
          }
        }
      ],
      "filter": [
        {
          "term": {
            "catalogId": "225"
          }
        },
        {
          "terms": {
            "brandId": [
              "1",
              "2",
              "9"
            ]
          }
        },
        {
          "nested": {
            "path": "attrs",
            "query": {
              "bool": {
                "must": [
                  {
                    "term": {
                      "attrs.attrId": {
                        "value": "9"
                      }
                    }
                  },
                  {
                    "terms": {
                      "attrs.attrValue": [
                        "高通",
                        "海思"
                      ]
                    }
                  }
                ]
              }
            }
          }
        },
        {
          "term": {
            "hasStock": {
              "value": "true"
            }
          }
        },
        {
          "range":{
            "skuPrice" : {
              "gte" :0,
              "lte" :6500
            }
          }
        }

      ]
    }
  },
  "sort": [
    {
      "skuPrice": {
        "order": "desc"
      }
    }
  ],
  "from": 0,
  "size": 5,
  "highlight": {
    "fields": {"skuTitle": {}},
    "pre_tags": "<b style='color:red'>",
    "post_tags": "</b>"
  },
  "aggs": {
    "brand_agg": {
      "terms": {
        "field": "brandId",
        "size": 10
      },
      "aggs": {
        "brand_name_agg": {
          "terms": {
            "field": "brandName",
            "size": 10
          }
        },
        "brand_img_agg": {
          "terms": {
            "field": "brandImg",
            "size": 10
          }
        }
      }
    },
    "catalog_agg": {
      "terms": {
        "field": "catalogId",
        "size": 10
      },
      "aggs": {
        "catalog_name_agg": {
          "terms": {
            "field": "catalogName",
            "size": 10
          }
        }
      }
    },
    "attr_agg": {
      "nested": {
        "path": "attrs"
      },
      "aggs": {
        "attr_id_agg": {
          "terms": {
            "field": "attrs.attrId",
            "size": 100
          },
          "aggs": {
            "attr_name_agg": {
              "terms": {
                "field": "attrs.attrName",
                "size": 10
              }
            },
            "attr_value_agg": {
              "terms": {
                "field": "attrs.attrValue",
                "size": 10
              }
            }
          }
        }
      }
    }
  }
}

第179集 searchrequest的构建

总体来说,java的代码就是根据DSL语句根据json一层一层的剥离开进行编写的。这块需要自己手动写一次印象更深。逻辑不难。

SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        /**
         * 模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存)
         */
        //1. 构建bool-query
        BoolQueryBuilder boolQueryBuilder=new BoolQueryBuilder();

        //1.1 bool-must
        if(!StringUtils.isEmpty(param.getKeyword())){
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle",param.getKeyword()));
        }

        //1.2 bool-fiter
        //1.2.1 catelogId
        if(null != param.getCatalog3Id()){
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId",param.getCatalog3Id()));
        }

        //1.2.2 brandId
        if(null != param.getBrandId() && param.getBrandId().size() >0){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",param.getBrandId()));
        }

        //1.2.3 attrs
        if(param.getAttrs() != null && param.getAttrs().size() > 0){

            param.getAttrs().forEach(item -> {
                //attrs=1_5寸:8寸&2_16G:8G
                BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();


                //attrs=1_5寸:8寸
                String[] s = item.split("_");
                String attrId=s[0];
                String[] attrValues = s[1].split(":");//这个属性检索用的值
                boolQuery.must(QueryBuilders.termQuery("attrs.attrId",attrId));
                boolQuery.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));

                NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrs",boolQuery, ScoreMode.None);
                boolQueryBuilder.filter(nestedQueryBuilder);
            });

        }

        //1.2.4 hasStock
        if(null != param.getHasStock()){
            boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock",param.getHasStock() == 1));
        }


        //1.2.5 skuPrice
        if(!StringUtils.isEmpty(param.getSkuPrice())){
            //skuPrice形式为:1_500或_500或500_
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice");
            String[] price = param.getSkuPrice().split("_");
            if(price.length==2){
                rangeQueryBuilder.gte(price[0]).lte(price[1]);
            }else if(price.length == 1){
                if(param.getSkuPrice().startsWith("_")){
                    rangeQueryBuilder.lte(price[1]);
                }
                if(param.getSkuPrice().endsWith("_")){
                    rangeQueryBuilder.gte(price[0]);
                }
            }
            boolQueryBuilder.filter(rangeQueryBuilder);
        }

        //封装所有的查询条件
        searchSourceBuilder.query(boolQueryBuilder);

第180集 构建排序,分页,高亮

/**
         * 排序,分页,高亮
         */

        //排序
        //形式为sort=hotScore_asc/desc
        if(!StringUtils.isEmpty(param.getSort())){
            String sort = param.getSort();
            String[] sortFileds = sort.split("_");

            SortOrder sortOrder="asc".equalsIgnoreCase(sortFileds[1])?SortOrder.ASC:SortOrder.DESC;

            searchSourceBuilder.sort(sortFileds[0],sortOrder);
        }

        //分页
        searchSourceBuilder.from((param.getPageNum()-1)*EsConstant.PRODUCT_PAGESIZE);
        searchSourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);

        //高亮
        if(!StringUtils.isEmpty(param.getKeyword())){

            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");

            searchSourceBuilder.highlighter(highlightBuilder);
        }

  • 13
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值