【Solr】之使用结巴分词模拟搜索商品1

简介: 【Solr】之使用结巴分词模拟搜索商品1

一、Solr入门


1.1 对于数据库的查询的


select * from t_goods where goodsName like “%手机%” ;

问题:

1,这个查询速度快不快?

2,对于goodsName 是否添加了索引(假设我们添加了)

3,对于上面的sql 语句,是否会走索引?

索引的本质是一颗树,若我们使用(“%手机%” ) 查询时,它无法去比较大小,无法比较,就无法走索引!

那种场景走索引:最左匹配原则 goodsName like “手机%”,它会走索引。goodsName like “%手机” 它不会走索引。

既然不会走索引,它的查询速度,就需要来一个全表的扫描。它的速度会非常慢!

假设我们的数据有百万级别的,查询一个商品,可能就需要20s 左右!


1.2 使用Map 集合来做查询


Map<String,List>

我们在Map 集合的Key 放商品的关键字,value放商品的id的集合。

到时我们使用关键字查询商品的ids就可以了


1.3 怎么得到商品的关键字?


商品名称Eg:


【小米10 旗舰新品2月13日14点发布】小米10 骁龙865 5G 抢先预约抽壕礼

荣耀20S 李现同款 3200万人像超级夜景 4800万超广角AI三摄 麒麟810 全网通版

荣耀20i 3200万AI自拍 超广角三摄 全网通版6GB+64GB 渐变红 移动联通电信4G

Redmi 8A 5000mAh 骁龙八核处理器 AI人脸解锁 4GB+64GB 深海蓝 游戏老人手机

二、结巴分词模拟搜索商品


2.1 新建项目


IDEA创建空项目【solr-demo】->新建maven的moudule【sole-fenci-demo】–>添加依赖

dependencies>
    <dependency>
        <groupId>com.huaban</groupId>
        <artifactId>jieba-analysis</artifactId>
        <version>1.0.2</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
    </dependency>
</dependencies>

2.3 测试项目

public class TestJieBa {
        //声明一个分词对象
        private static JiebaSegmenter jiebaSegmenter = new JiebaSegmenter();
        public static void main(String[] args) {
            String content="锤子(smartisan) 坚果Pro3 8GB+128GB 黑色 骁龙855PLUS 4800万四摄 全网通双卡双待 全面屏游戏手机";
            /***
             * @Description:
             * 参数1  要分词的内容
             * 参数1:分词模式
             */
            List<SegToken> tokens = jiebaSegmenter.process(content, JiebaSegmenter.SegMode.SEARCH);
            for (SegToken token : tokens) {
                System.out.println(token.word);
            }
            System.out.println("分词完成"+tokens.size());
        }
}

三、使用商品搜索案例来展示我们的Map集合

3.1 商品的模拟

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Goods {
    private Integer id;//商品ID
    private String goodsName;//商品名称
    private Double goodsPrice;//商品价格
}


3.2 模拟数据库

public class DBUtils {
    private static Map<Integer, Goods> db=new HashMap<>();
    public static void insert(Goods goods){
        db.put(goods.getId(),goods);
    }
    public static Goods getById(Integer id){
        return db.get(id);
    }
    /***
     * @Description:
     * @Param: 提供一个根据ids的集合查询商品的方法    key--->多个商品ID
     * @return:
     * @Author: 
     * @Date: 2020/4/2
     */
    public static List<Goods> getByIds(Set<Integer> ids){
        if(null==ids||ids.isEmpty()){
            return Collections.emptyList();
        }
        List<Goods> goods=new ArrayList<>();
        for (Integer id : ids) {
            Goods g = db.get(id);
            if(null!=g){
                goods.add(g);
            }
        }
        return goods;
    }
}

3.3 商品服务的接口GoodsService

public interface GoodsService {
    void insert(Goods goods);
    List<Goods> findByGoodsName(String goodsName);
}

3.4 商品服务的实现(GoodsServiceImpl)

public class GoodsServiceImpl implements GoodsService {
    //模拟一个索引库
    private Map<String, Set<Integer>> indexs=new HashMap<>();
    private JiebaSegmenter jiebaSegmenter=new JiebaSegmenter();
    @Override
    public void insert(Goods goods) {
        /***
        * 我们在插入商品时,要构造一个Map集合
         * Map<String,List<ID>/>
        */
        //分词
        List<String> keywords= this.fenci(goods.getGoodsName());
        //插入数据
        DBUtils.insert(goods);
        //保存到分词的关键字和ids的映射关系
        saveKeyWords(keywords,goods.getId());
    }
    /**
    * @Description: 保存分词和id的关系
    * @Param: [keywords, id]
    * @return: void
    * @Author:
    * @Date: 2020/4/2
    */
    private void saveKeyWords(List<String> keywords, Integer id) {
        if(null!=keywords&&!keywords.isEmpty()){
            for (String keyword : keywords) {
                if(indexs.containsKey(keyword)){//先看关键字在索引里面是否存在
                    Set<Integer> integers = indexs.get(keyword);//得到这个关键字对应该的已存在的ids集合
                    integers.add(id);//把新插入的id放入
                }else{//这是一个新词,之前的索引库不存在
                    HashSet<Integer> ids = new HashSet<>();
                    ids.add(id);
                    indexs.put(keyword,ids);
                }
            }
        }
    }
    /***
    * @Description: 完成分词
    * @Param: [goodsName]
    * @return: java.util.List<java.lang.String>
    * @Author: 
    * @Date: 2020/4/2
    */
    private List<String> fenci(String goodsName) {
        List<SegToken> tokens = jiebaSegmenter.process(goodsName, JiebaSegmenter.SegMode.SEARCH);
        List<String> keywords=new ArrayList<>(tokens.size());
        for (SegToken token : tokens) {
            keywords.add(token.word);
        }
        return keywords;
    }
    /***
    * @Description: 
    * @Param: [goodsName]
    * @return: java.util.List<com.leige.solr.test.domain.Goods>
    * @Author: 
    * @Date: 2020/4/2
    */
    @Override
    public List<Goods> findByGoodsName(String goodsName) {
        //直接从Map里面取有没有
        if(indexs.containsKey(goodsName)){
            Set<Integer> ids = indexs.get(goodsName);//取出有goodsName里面有传过来的goodsName商品的ID
            List<Goods> goodsList = DBUtils.getByIds(ids);
            return goodsList;
        }
        return Collections.emptyList();
    }
}

3.5 测试类

public class TestApp {
    public static void main(String[] args) {
        GoodsService goodsService = new GoodsServiceImpl();
        Goods goods = new Goods(1,"苹果手机",10.00) ;
        Goods goods1 = new Goods(2,"华为手机",11.00) ;
        Goods goods2 = new Goods(3,"红米手机",5.00) ;
        Goods goods3 = new Goods(4,"联想手机",6.00) ;
        goodsService.insert(goods);
        goodsService.insert(goods1);
        goodsService.insert(goods2);
        goodsService.insert(goods3);
        List<Goods> goodss = goodsService.findByGoodsName("手机");
        for (Goods goodsTest : goodss) {
            System.out.println(goodsTest);
        }
    }
}

3.6 我们在搜索苹果手机时,搜索不出来结果


修改GoodsService

修改GoodsService
List<Goods> findByKeyWord(String keyword);

修改GoosServiceImpl

@Override
public List<Goods> findByKeyWord(String keyword) {
    //先分词  再查询
    List<String> stringList = this.fenci(keyword);
    Set<Integer> idsSet = new HashSet<>();
    for (String kw : stringList) {
        //直接从Map里面取有没有
        if(indexs.containsKey(kw)){
            Set<Integer> ids = indexs.get(kw);//取出有goodsName里面有传过来的goodsName商品的ID
            idsSet.addAll(ids);
        }
    }
    if(idsSet.isEmpty()){
        return Collections.emptyList();
    }else{
        return DBUtils.getByIds(idsSet);
    }
}

修改TestApp测试

20200712202548770.png

3.7 搜索速度的提升


之前:是需要把数据库做一个全表的扫描

现在:直接通过计算hash值定位 值,在非常理想的情况下。他的速度,只计算一次


3.8 新的问题


我们的一句话会分出来很多的关键字,都给他建立<K,List> 我们的Map 里面将容纳非常多的元素!


那我们往Map 集合里面放元素时,将有29 个被放进去!若成千上万的商品,那Map 集合非常大!怎么解决Map 集合无限扩大的问题?

目录
相关文章
|
2月前
|
数据采集 存储 搜索推荐
使用Python构建自定义搜索引擎:从数据抓取到索引与搜索
使用Python构建自定义搜索引擎:从数据抓取到索引与搜索
76 0
|
4月前
|
JSON Java API
关键词搜索1688商品数据接口Python
关键词搜索1688商品数据接口Python
36 2
|
10月前
|
XML JSON 缓存
Java实现关键词搜索获取淘宝商品列表数据方法
Java实现关键词搜索获取淘宝商品列表数据方法
128 0
|
12月前
|
SQL Java
白话Elasticsearch04- 结构化搜索之使用terms query搜索多个值以及多值搜索结果优化
白话Elasticsearch04- 结构化搜索之使用terms query搜索多个值以及多值搜索结果优化
461 0
|
12月前
|
SQL JSON 自然语言处理
白话Elasticsearch01- 结构化搜索之使用term query来搜索数据
白话Elasticsearch01- 结构化搜索之使用term query来搜索数据
267 0
|
12月前
|
小程序 数据库
小程序搜索功能,云开发搜索,小程序云开发模糊搜索,同时搜索多个字段
小程序搜索功能,云开发搜索,小程序云开发模糊搜索,同时搜索多个字段
242 0
|
存储 自然语言处理 前端开发
基于solr实现通用:输入提示、纠错、拼音搜索、繁体搜索方案
假期重新把之前在新浪博客里面的文字梳理了下,搬到这里。文本介绍基于solr实现通用:输入提示、纠错、拼音搜索、繁体搜索方案。
314 0
基于solr实现通用:输入提示、纠错、拼音搜索、繁体搜索方案
|
机器学习/深度学习 搜索推荐 数据处理
这就是搜索引擎读书笔记-day3-5.检索模型与搜索排序
搜索结果排序融合了上百种排序因子,而重要两因素是:用户查询和网页内容相关性 及 网页链接情况。本节介绍内容相关性介绍网页排序
这就是搜索引擎读书笔记-day3-5.检索模型与搜索排序