一、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测试
3.7 搜索速度的提升
之前:是需要把数据库做一个全表的扫描
现在:直接通过计算hash值定位 值,在非常理想的情况下。他的速度,只计算一次
3.8 新的问题
我们的一句话会分出来很多的关键字,都给他建立<K,List> 我们的Map 里面将容纳非常多的元素!
那我们往Map 集合里面放元素时,将有29 个被放进去!若成千上万的商品,那Map 集合非常大!怎么解决Map 集合无限扩大的问题?