hmall | 引入ES实现高效搜索与同步双写

在gitee、飞书、百度云、B站中,黑马都没有上传该部分资料,以下皆为个人观点,如有纰漏欢迎指正

 

1.先把item-service中的searchcontroller抽出来,抽到一个模块中并将其设为hmall的子模块

2.引入依赖common,nacos,bootstrap,es

        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--common-->
        <dependency>
            <groupId>com.heima</groupId>
            <artifactId>hm-common</artifactId>
            <version>1.0.0</version>
        </dependency>
        <!--nacos 服务注册发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--nacos配置管理-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!--读取bootstrap文件-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>
        <!--es -->
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
        </dependency>

3.修改配置文件,bootstrap只改服务名,不需要的云配置不要写;application中添加上配置,实现自动装填

spring:
  elasticsearch:
    uris: http://192.168.88.95:9200

4.这时启动一下服务,看看nacos里有米有注册服务

5.我把文件直接CV过来了,因为这些东西应该不太公共,只是搜索相关会用到,因此没有抽取到common中

这里新增了一个ItemVO,其中字段就是为了对标输出内容中的total,pages,list三个数据,以便于传给前端

6.简单的实现了一下service层中的查询(可能没写全,不过差不多了)

@Service
@Slf4j
@RequiredArgsConstructor
public class ItemServiceImpl implements ItemService {
    private final RestHighLevelClient client;

    /*
     * total:"88475"
     * pages:"4424"
     * list[{},{},{}]
     * */
    public ItemVO search(ItemPageQuery query) throws IOException {
        Integer pageNo = Integer.valueOf(query.getPageNo());
        Integer pageSize = Integer.valueOf(query.getPageSize());
        SearchRequest request = new SearchRequest("hmall");
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        if (query.getKey() != null && !query.getKey().isEmpty()) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("name", query.getKey()));
        }else {
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        }

        if(query.getCategory()!=null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("category", query.getCategory()));
        }
        if(query.getBrand()!=null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand", query.getBrand()));
        }

        if(query.getSortBy()!=null&&query.getIsAsc().equals("false")){
            request.source().sort(query.getSortBy(), SortOrder.DESC);
        }else if(query.getSortBy()!=null&&query.getIsAsc().equals("true")){
            request.source().sort(query.getSortBy(), SortOrder.ASC);
        }
        if(query.getMinPrice()!=null&&query.getMaxPrice()!=null){
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(query.getMinPrice()).lte(query.getMaxPrice()));
        }

        // 分页
        request.source().query(boolQueryBuilder).from((pageNo - 1) * pageSize).size(pageSize);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        SearchHits hit = response.getHits();

        SearchHit[] hits = hit.getHits();
        List<ItemDoc>list=new ArrayList<>();
        for (SearchHit documentFields : hits) {
            String sourceAsString = documentFields.getSourceAsString();
            ItemDoc itemDoc= JSONUtil.toBean(sourceAsString,ItemDoc.class);
            list.add(itemDoc);
        }
        long total=hit.getTotalHits().value;
        return ItemVO.builder()
                .total(total)
                .list(list)
                .pages(total/(list.size()+1))
                .build();
    }
}

7.为了保持mysql与es的同步,我选择了最简单的同步双写

在item-service的controller中修改controller方案,当商品上架的时候(status==1),我们把mysql的这条数据写到es中,当商品下架的时候(status==2),我们把es中的数据删除掉。因为es存储没有涉及到库存问题,因此我只是简单的改了改controller层,发生了代码侵入

    @ApiOperation("新增商品,如果status是1就写入es")
    @PostMapping
    public void saveItem(@RequestBody ItemDTO item) throws IOException {
        // 新增
        itemService.save(BeanUtils.copyBean(item, Item.class));
        if(item.getStatus()==1){
            IndexRequest request=new IndexRequest("hmall");
            ItemDoc itemDoc = BeanUtils.copyBean(itemService.getById(item.getId()), ItemDoc.class);
            request.source(JSONUtil.toJsonStr(itemDoc),XContentType.JSON);
            client.index(request, RequestOptions.DEFAULT);
        }
    }

    @ApiOperation("更新商品状态,如果是上架,就添加到es中,下架就在es中删除")
    @PutMapping("/status/{id}/{status}")
    public void updateItemStatus(@PathVariable("id") Long id, @PathVariable("status") Integer status) throws IOException {
        Item item = new Item();
        item.setId(id);
        item.setStatus(status);
        itemService.updateById(item);
        //1 正常 需上传
        if(status==1){
            IndexRequest request=new IndexRequest("hmall");
            ItemDoc itemDoc = BeanUtils.copyBean(itemService.getById(id), ItemDoc.class);
            request.source(JSONUtil.toJsonStr(itemDoc),XContentType.JSON);
            client.index(request, RequestOptions.DEFAULT);
        }
        //2 下架 需删除
        else if(status==2||status==3){
            DeleteRequest request=new DeleteRequest("hmall",id.toString());
            client.delete(request, RequestOptions.DEFAULT);
        }
    }

    @ApiOperation("根据id删除商品 同步双删")
    @DeleteMapping("{id}")
    public void deleteItemById(@PathVariable("id") Long id) throws IOException {
        itemService.removeById(id);
        // es中也要同步删除
        DeleteRequest request=new DeleteRequest("hmall",id.toString());
        client.delete(request, RequestOptions.DEFAULT);
    }

 

posted on 2024-05-11 21:24  天启A  阅读(1)  评论(0编辑  收藏  举报

导航