jieba分词(1):入门案例

news2024/9/25 3:28:53

1 场景介绍

大数据量的查询问题

假设我们要从商品的表里面查询一个商品

我们的数据库里面肯定有个t_goods的表,我们现在利用商品的名称做模糊查询

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<ID>>

我们在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 深海蓝 游戏老人手机

1.4 老师问你一个问题:请说出包含 明月的古诗?

明月几时有,把酒问青天(苏东坡《水调歌头》)

海上升明月,天涯共此时(张九龄《望月怀远》)

暗尘随马去,明月逐人来(苏昧道《正月十五夜》)

三五明月满,四五蟾兔缺(无名氏《孟冬寒气至》)

白云还自散,明月落谁家(李白《忆东山二首》)

明月却多情,随人处处行(张先《菩萨蛮》)

明月净松林,千峰同一色(欧阳修《自菩提步月归广化寺》)

明月几时有,把酒问青天(苏轼《水调歌头》)

明月出天山,茫茫人海间(李白《关山月》)

明月照高楼,流光正徘徊(曹植《怨歌行》)

明月隐高树,长河没晓天(陈子昂《春夜别友人》)

举杯邀明月,对影成三人(李白《月下独酌》)

举头望明月,低头思故乡(李白《静夜思》)

深林人不知,明月来相照(王维《竹里馆》)

明月松间照,清泉石上流(王维《山居秋暝》)

如果在使用数据库查询,你只能遍历你学过的每一首诗,看看里面有没有《明月》两个字

如果使用索引:

明月---List<以上所有>

白云---List<忆东山二首>

青天---List<水调歌头>

2 分词实现操作

新建一个maven项目

2.1 导入jieba分词依赖

<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.2 分词器测试

package com.example.demo;

import com.huaban.analysis.jieba.JiebaSegmenter;
import com.huaban.analysis.jieba.SegToken;

import java.util.List;

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());


    }
}

启动后结果如下:

分词器引入成功。 

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

一下模拟商品查询的过程

3.1 商品实体类

package com.example.demo.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Goods {
    private Integer id;//商品ID

    private String goodsName;//商品名称

    private Double goodsPrice;//商品价格
}

3.2 数据库工具类

这边只是模拟数据库,没有进行数据库的连接

package com.example.demo.util;

import com.example.demo.domain.Goods;

import java.util.*;

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:
     */
    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

package com.example.demo.service;

import com.example.demo.domain.Goods;

import java.util.List;

public interface GoodsService {
    /**
     * @Description: 添加商品
     * @Param: [goods]
     * @return: void
     */
    void insert(Goods goods);

    /**
     * @Description: 根据商品名称模糊查询商品
     * @Param: [goodsName]
     * @return: java.util.List<com.leige.solr.test.domain.Goods>
     */
    List<Goods> findByGoodsName(String goodsName);
}

3.4 商品服务的实现类(GoodsServiceImpl)

package com.example.demo.service.Impl;

import com.example.demo.domain.Goods;
import com.example.demo.service.GoodsService;
import com.example.demo.util.DBUtils;
import com.huaban.analysis.jieba.JiebaSegmenter;
import com.huaban.analysis.jieba.SegToken;

import java.util.*;

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
     */
    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>
     */
    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>
     */
    @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 测试类

package com.example.demo;

import com.example.demo.domain.Goods;
import com.example.demo.service.GoodsService;
import com.example.demo.service.Impl.GoodsServiceImpl;

import java.util.List;

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 搜索结果如下

4 缺陷解决

以上代码我们会发现一个问题,我们在搜索红米手机或者其他手机的全名的时候,搜索不出来结果

原因:

分词器对查询的关键字进行分词的时候,对关键字进行了拆分,没有保留原来的完整关键字,

解决方案:

搜索的时候也进行分词

操作如下:

(1)修改GoodsService

/**
 * @Description: 根据商品名称模糊查询商品
 * @Param: [goodsName]
 * @return: java.util.List<com.leige.solr.test.domain.Goods>
 */
List<Goods> findByKeyWord(String keyword);

(2)修改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);
    }
}

(3)修改测试类

package com.example.demo;

import com.example.demo.domain.Goods;
import com.example.demo.service.GoodsService;
import com.example.demo.service.Impl.GoodsServiceImpl;

import java.util.List;

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.findByKeyWord("红米手机");
        for (Goods goodsTest : goodss) {
            System.out.println(goodsTest);
        }
    }
}

结果如下

3.7 对比直接查询

使用分词器之前:是需要把数据库做一个全表的扫描

使用分词器之后:直接通过计算hash值定位 值,在非常理想的情况下。他的速度,只计算一次

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/498143.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSDN | 好久不见,甚是想念

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后端的开发语言A…

JavaScript实现在键盘输入按键,浏览器进行显示的代码

以下为实现在键盘输入按键&#xff0c;浏览器进行显示的代码和运行截图 目录 前言 一、在键盘输入按键&#xff0c;浏览器进行显示 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择&#xff0c;您可以在目录里进行快速查找&#xf…

《灰盒模型在非侵入式体外估计糖化血红蛋白百分比和数字脉搏波形的推导和验证》阅读笔记

目录 一、论文摘要 二、论文十问 Q1&#xff1a;论文试图解决什么问题&#xff1f; Q2&#xff1a;这是否是一个新的问题&#xff1f; Q3&#xff1a;这篇文章要验证一个什么科学假设&#xff1f; Q4&#xff1a;有哪些相关研究&#xff1f;如何归类&#xff1f;谁是这一课…

84.python input输入函数知识拓展

文章目录 1. input函数知识回顾2. input常犯错误解析3. 用函数转换从终端输入的数据3.1 输入的数为整数&#xff0c;则用int转换为整数3.2 输入的数为浮点数&#xff0c;则用float转换为浮点数3.3 不考虑输入的数据类型&#xff0c;则用eval函数转换 4. 变量的多种赋值方式4.1 …

OpenCV教程——OpenCV环境配置及第一个测试代码

1.OpenCV简介 OpenCV是一个计算机视觉的开源库。英文全称是&#xff1a;Open Source Computer Vision Library。 常用的OpenCV的核心模块&#xff1a; Image ProcessCamera Calibration and 3D ReconstructionVideo AnalysisObject DetectionMachine LearningDeep LearningG…

【Linux】Linux安装Git(图文解说详细版)

文章目录 前言第一步&#xff0c;官网下载安装包第二步&#xff0c;解压安装包第三步&#xff0c;安装编译环境第四步&#xff0c;编译源码第五步&#xff0c;安装git第六步&#xff0c;配置环境变量 前言 服务器版本&#xff1a;CentOS7.8 git官网&#xff1a;https://git-sc…

汽车出租系统【纯控制台】(Java课设)

系统类型 纯控制台类型&#xff08;没有用到数据库&#xff09; 使用范围 适合作为Java课设&#xff01;&#xff01;&#xff01; 部署环境 jdk1.8Idea或eclipse 运行效果 本系统源码地址&#xff1a;https://download.csdn.net/download/qq_50954361/87753364 更多系统…

Qt5.14.2安装教程

之所以选择安装Qt5.14.2&#xff0c;是因为从5.15.0起&#xff0c;对于开源用户&#xff0c;Qt官方不再提供独立安装文件&#xff0c;源码安装听说很繁琐&#xff0c;并且还要激活码。 官网下载链接&#xff1a;https://download.qt.io/archive/qt/5.14/5.14.2/ 1、Windows用户…

智能优化算法:基于驾驶训练的优化算法-附代码

智能优化算法&#xff1a;基于驾驶训练的优化算法 文章目录 智能优化算法&#xff1a;基于驾驶训练的优化算法1. 基于驾驶训练优化算法1.1 初始化1.2 阶段一&#xff1a;驾驶教练培训&#xff08;探索阶段&#xff09;1.3 阶段二&#xff1a;学员学习&#xff08;探索阶段&…

Jupyter notebook 如何设定默认的保存目录?

前言&#xff1a; 做智能车的时候&#xff0c;Jupter Notebook的默认保存在可怜的C盘&#xff0c;本来就很紧张的C肯定受不了&#xff0c;要改到别的地方&#xff0c;网上找了一些参考&#xff0c;说变更一下配置地址就可以了&#xff0c;照着做&#xff0c;99%的博客说&#x…

Linux驱动开发笔记(一):helloworld驱动源码编写、makefile编写以及驱动编译基本流程

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/130534343 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…

算法和算法竞赛的知识点

2023年5月7日&#xff0c;周日早上&#xff1a; 虽然今天早上我作出了改变学习算法方式的决定&#xff0c;但是知识点有哪些、具体该怎么做还没搞清楚&#xff0c;于是去刷题网站截图了它们的标签。 或许看相关书籍的知识点和题单也不错。 LeetCode的知识点 力扣 蓝桥杯的知识…

2023招商Fintech数据赛道rank33 赛后分享

赛题需求&#xff1a; 本次比赛为参赛选手提供了两个数据集&#xff0c;即训练数据集(train)和测试数据集(test_A榜/test_B榜)。参赛选手需要基于训练数据集&#xff0c;通过有效的特征提取&#xff0c;构建客户进取类产品配置发生时点预测模型,并将模型应用于测试数据集上,输出…

力扣sql中等篇练习(十六)

力扣sql中等篇练习(十六) 1 不同性别每日分数统计 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 a 示例输入 b 示例输出 1.2 示例sql语句 # 分数是往后累加的 SELECT s2.gender,s2.day,sum(s1.score_points) total FROM Scores s1 CROSS JOIN Scores s2 ON s2.gen…

java创建多线程的方法

Java中是可以创建多个线程的&#xff0c;每个线程都有自己的名字和时间戳。下面我们来看看创建多个线程的方法。 创建多个线程&#xff0c;需要使用到 Thread类中的 create方法。需要注意的是&#xff0c;不是所有的线程都可以使用 create方法来创建&#xff0c;只有当这些线程…

目前可用的ChatGPT网站

本文意在整理可用gpt-3.5、gpt-4.0等网站。 本文主要是方便自己翻阅&#xff0c;如对您也有所帮助&#xff0c;不胜荣幸~ 文章目录 chatgpt.qdymys.cngpttalkchatgpt-cn.cobing.com总结 chatgpt.qdymys.cn 网址&#xff1a;https://chatgpt.qdymys.cn/限制&#xff1a;三小时只…

【新星计划-2023】TCP/IP协议讲解

相信大家在学习的过程中一定听到过TCP/IP这个协议&#xff0c;那么&#xff0c;TCP/IP协议是什么&#xff1f;为什么会有TCP/IP协议&#xff1f; 一、TCP/IP是什么&#xff1f; TCP/IP是用于计算机通信的一组协议&#xff0c;我们通常称它为TCP/IP协议族。它是70年代中期美国…

Spring Boot项目瘦身

目录 1&#xff0c;什么是瘦身&#xff1f;2&#xff0c;为什么要瘦身&#xff1f;3&#xff0c;如何瘦身&#xff1f;3.1&#xff0c;瘦身思路&#xff1a;3.2&#xff0c;瘦身方法 4&#xff0c;瘦身后运行 1&#xff0c;什么是瘦身&#xff1f; 瘦身&#xff1a;thinBody&a…

CSS进阶

01-复合选择器 定义&#xff1a;由两个或多个基础选择器&#xff0c;通过不同的方式组合而成。 作用&#xff1a;更准确、更高效的选择目标元素&#xff08;标签&#xff09;。 后代选择器 后代选择器&#xff1a;选中某元素的后代元素。 选择器写法&#xff1a;父选择器 …

Spring AOP续--织入

上篇讲到SpringAOP的一些用法以及概念&#xff0c;这里我们单独讲一下AOP中的“织入”。 我们知道&#xff0c;SpringAOP是基于动态代理实现的技术&#xff0c;而织入则是一个生成动态代理对象并且将切面和目标对象方法编织成为约定流程的过程。 对于通知&#xff0c;上篇文章…