Sea-Search03总结&&un finish

发布时间 2023-10-23 20:55:39作者: 海山了-

使用到的设计模式

Facade门面模式

为何使用?

在搜索项目中,由于使用Mvc架构且数据库中各种不同类型的数据源并没有放在同一张表,于是我们不可避免的在Controller中需要注入多个service,各种service眼花缭乱,而搜索中台提供的内容又及其单一(只负责返回搜索数据),于是采用Facade来统一接口以及装载service

代码呈现

需要注意的是,我们还需要一个统一的return对象(SearchVo),有两种思路:

  • SearchVo声明成接口,由其他如PostVo来实现
  • 直接聚合:SearchVo聚合各个如PostVo的内容(对于内容我们也可采用抽象类来实现)等
@Component
public class SearchFacade {
    @Autowired
    private PictureService pictureService;
    @Autowired
    private PostService postService;
    @Autowired
    private UserService userService;
    @Autowired
    private DataSourceRegistry dataSourceRegistry;
    public SearchVo getAll(SearchQueryRequest searchQueryRequest, HttpServletRequest request) {
        long current = searchQueryRequest.getCurrent();
        long size = searchQueryRequest.getPageSize();
        String searchText = searchQueryRequest.getSearchText();
        String type = searchQueryRequest.getType();
        SearchTypeEnum searchTypeEnum = SearchTypeEnum.getEnumByValue(type);
        ThrowUtils.throwIf(type != null && StringUtils.isBlank(type), ErrorCode.PARAMS_ERROR);
        SearchVo searchVo = new SearchVo();
        if (searchTypeEnum == null) {
            CompletableFuture<Void> picCF = CompletableFuture.runAsync(() -> {
                List<Picture> pictures = pictureService.searchPicture(searchQueryRequest.getSearchText(), searchQueryRequest.getCurrent(), searchQueryRequest.getPageSize());
                searchVo.setPicList(pictures);
            });
            CompletableFuture<Void> userCF = CompletableFuture.runAsync(() -> {
                UserQueryRequest userQueryRequest = new UserQueryRequest();
                userQueryRequest.setUserName(searchText);
                userQueryRequest.setCurrent(current);
                userQueryRequest.setPageSize(size);
                Page<UserVO> userVOPage = userService.listUserVoByPage(userQueryRequest);
                searchVo.setUserList(userVOPage.getRecords());
            });
            CompletableFuture<Void> postCF = CompletableFuture.runAsync(() -> {
                PostQueryRequest postQueryRequest = new PostQueryRequest();
                postQueryRequest.setSearchText(searchText);
                postQueryRequest.setCurrent(current);
                postQueryRequest.setPageSize(size);
                Page<PostVO> postPage = postService.listPostVOByPage(postQueryRequest, request);

                searchVo.setPostList(postPage.getRecords());

            });
            try {
                CompletableFuture.allOf(postCF, userCF, picCF).get();
            } catch (Exception e) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "查询数据异常");
            }
        } else {

            DataSource dataSource = dataSourceRegistry.dataSourceMap.get(type);
            List<?> list = dataSource.doSearch(searchText, current, size);
            searchVo.setDataList(list);
        }
        return searchVo;
    }
}

tips:

这里的真实有效的仅有dataList(其他三个是第二版的残留物)

/**
 * @author:天才玩家M
 * @date:2023/10/4 11:32
 * @description:TODO
 */
@Data
public class SearchVo {
    private List<Picture> picList;
    private List<PostVO> postList;
    private List<UserVO> userList;
    private List<?> dataList;
}

适配器模式

为何使用?:

搜索中台中,我们之后可能会接入各种各样的type的搜索数据源,比如说我自己,有时候,几乎是同样功能的service只是数据源不一样,不复制粘贴就常常弄得各有各的接口,就很乱,所以出现了适配器模式,一方面,如果这个service不是我们写的,我们在其之后写,可以利用适配器尝试将对方改为我们的适配的api,或者我们先定了适配器,让对方按照我们的标准来实现

public interface DataSource<T> {
    List<T> doSearch(String searchText,Long current,Long pageSize);
}

其他数据源:

public class PostDataSource implements DataSource<PostVO>{...}

注册器模式

为何使用?

承接上面的门面,以及适配器,在实际场景中我们肯定要根据不同的searchType更换,那么用switch?
太复杂了,那用一个map封装一下,之后get,差不多,这可大大节省了代码量

@Component
public class DataSourceRegistry {
    public   Map<String,DataSource> dataSourceMap=null;
    @PostConstruct
    public void initMap(){
        dataSourceMap=new HashMap();
        dataSourceMap.put(SearchTypeEnum.POST.getValue(),new PostDataSource());
        dataSourceMap.put(SearchTypeEnum.PICTURE.getValue(),new PictureDataSource());
        dataSourceMap.put(SearchTypeEnum.USER.getValue(),new UserDataSource());
    }
}

ES的概念

倒排索引

正向索引:字典or通常书籍的目录

倒排索引:

输入数据时

会根据我们输入es的内容,先分词,然后统计哪个分词在哪个doc出现过,通过内容来检索到哪个doc

查找数据时

同样会通过分词,然后根据总共分词在哪些doc出现过,返回对应的数据

用户搜:“鱼皮rapper”

ES 先切词:鱼皮,rapper

去倒排索引表找对饮的文章:文章A,B

内容 id
你好 文章A,B
我是 文章A,B
rapper 文章A
鱼皮 文章B
coder 文章B

一个待升级点

es会通过我们输入的searchText来分词然后返回docs,这些doc同时带有一定的评分,我们可以通过这些评分来倒序返回给用户,实现对应的功能

(类似:百度:查找的时候第一页的内容和我们搜索的相关性较高(抛开广告不谈的话),之后相关性逐渐降低)

待解决点:

page改成cur_point

能够优化ES性能

尤其是像我们手机电商这种页码不明显的情况下,当然明显也能替换