搜索引擎-02-分词与全文索引

拓展阅读

搜索引擎-01-概览

搜索引擎-02-分词与全文索引

搜索引擎-03-搜索引擎原理

Crawl htmlunit 模拟浏览器动态 js 爬虫入门使用简介

Crawl jsoup 爬虫使用 jsoup 无法抓取动态 js 生成的内容

Crawl WebMagic 爬虫入门使用简介 webmagic

搜索引擎

查询,想必你不会陌生。

如何更好的查询,本文将持续记录一些点滴。

对于大部分不是专门研究搜索引擎的同学,记住以下几点即可:

1) 全网搜索引擎系统由spider, search&index, rank三个子系统构成

2) 站内搜索引擎与全网搜索引擎的差异在于,少了一个spider子系统

3) spider和search&index系统是两个工程系统,rank系统的优化却需要长时间的调优和积累

4) 正排索引(forward index)是由网页url_id快速找到分词后网页内容list<item>的过程

5) 倒排索引(inverted index)是由分词item快速寻找包含这个分词的网页list<url_id>的过程

6) 用户检索的过程,是先分词,再找到每个item对应的list<url_id>,最后进行集合求交集的过程

7) 有序集合求交集的方法有

a) 二重for循环法,时间复杂度O(n*n)

b) 拉链法,时间复杂度O(n)

c) 水平分桶,多线程并行

d) bitmap,大大提高运算并行度,时间复杂度O(n)

e) 跳表,时间复杂度为O(log(n))

Original Design

原来自己的博客进行检索。采用的是如下方式

分词->拼音->全文检索(MySQL)

友情提示:

  1. 本段较长,主要用来日后查看,可自行过滤性查看。

分词

分词-知乎

例子:

当用户输入个人博客这个关键词检索时,可以在后台进一步分为个人博客

  • jar
<!--结巴分词-->
<dependency><groupId>com.huaban</groupId><artifactId>jieba-analysis</artifactId><version>${jieba.version}</version>
</dependency>
  • FenCiUtil.java
import com.huaban.analysis.jieba.JiebaSegmenter;
import com.huaban.analysis.jieba.SegToken;
import com.ryo.blog.service.util.constant.AppConstant;
import org.apache.commons.lang3.StringUtils;import java.util.List;/*** @author houbinbin* @since 1.7*/
public class FenCiUtil {/*** 分词器*/private static final JiebaSegmenter SEGMENTER = new JiebaSegmenter();/*** 中文分词, 并转成拼音** @param data* @return*/public static String process(String data) {if (StringUtils.isEmpty(data)) {return StringUtils.EMPTY;}data = data.replaceAll("\\s", AppConstant.BLANK);data = data.replaceAll(AppConstant.SINGLE_QUOTES, AppConstant.BLANK);List<SegToken> list = SEGMENTER.process(data, JiebaSegmenter.SegMode.INDEX);StringBuilder sb = new StringBuilder();for(SegToken segToken : list) {sb.append(PinYinUtil.converterToSpellWithMuti(segToken.word)).append(AppConstant.COMMA);}sb.deleteCharAt(sb.lastIndexOf(AppConstant.COMMA));return sb.toString();}
}
  • AppConstant.java
/*** @author houbinbin* @since 1.7*/
public class AppConstant {/*** 默认编码格式*/public static final String DEFAULT_CHARSET = "UTF-8";/*** 逗号*/public static final String COMMA = ",";/*** 圆点*/public static final String DOT = ".";/*** 空格*/public static final String BLANK = " ";/*** 单引号*/public static final String SINGLE_QUOTES = "'";/*** 下划线*/public static final String UNDERLINE = "_";}

拼音

例子:

比如查询用户老马啸西风,为了更好的用户体验。用户输入laomaxiaoxifenglmxxf都应该返回对应的结果。

  • jar
<!--拼音4j-->
<dependency><groupId>com.belerweb</groupId><artifactId>pinyin4j</artifactId><version>${pinyin4j.version}</version>
</dependency>
  • PinYinUtil.java
import com.ryo.blog.service.util.constant.AppConstant;
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;/*** @author houbinbin* @since 1.7*/
public class PinYinUtil {/*** 汉字转换位汉语拼音首字母,英文字符不变,特殊字符丢失 支持多音字,生成方式如(长沙市长:cssc,zssz,zssc,cssz)** @param chines 汉字* @return 拼音*/public static String converterToFirstSpell(String chines) {StringBuffer pinyinName = new StringBuffer();char[] nameChar = chines.toCharArray();HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);for (int i = 0; i < nameChar.length; i++) {if (nameChar[i] > 128) {try {// 取得当前汉字的所有全拼String[] strs = PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat);if (strs != null) {for (int j = 0; j < strs.length; j++) {// 取首字母pinyinName.append(strs[j].charAt(0));if (j != strs.length - 1) {pinyinName.append(AppConstant.COMMA);}}}// else {// pinyinName.append(nameChar[i]);// }} catch (BadHanyuPinyinOutputFormatCombination e) {e.printStackTrace();}} else {pinyinName.append(nameChar[i]);}pinyinName.append(AppConstant.BLANK);}return parseTheChineseByObject(discountTheChinese(pinyinName.toString()));}/*** 汉字转换位汉语全拼,英文字符不变,特殊字符丢失* 不支持多音字,生成方式如(重当参:zhongdangcen)** @param chines 汉字* @return 拼音*/public static String converterToSpell(String chines) {StringBuffer pinyinName = new StringBuffer();char[] nameChar = chines.toCharArray();HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);for (int i = 0; i < nameChar.length; i++) {if (nameChar[i] > 128) {try {// 取得当前汉字的所有全拼String[] strs = PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat);if (strs != null && strs.length > 0) {pinyinName.append(strs[0]);}} catch (BadHanyuPinyinOutputFormatCombination e) {e.printStackTrace();}} else {pinyinName.append(nameChar[i]);}pinyinName.append(AppConstant.BLANK);}return parseTheChineseByObject(discountTheChinese(pinyinName.toString()));}/*** 汉字转换位汉语全拼,英文字符不变,特殊字符丢失* 支持多音字,生成方式如(重当参:zhongdangcen,zhongdangcan,chongdangcen* ,chongdangshen,zhongdangshen,chongdangcan)** @param chines 汉字* @return 拼音*/public static String converterToSpellWithMuti(String chines) {StringBuffer pinyinName = new StringBuffer();char[] nameChar = chines.toCharArray();HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);for (int i = 0; i < nameChar.length; i++) {if (nameChar[i] > 128) {try {// 取得当前汉字的所有全拼String[] strs = PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat);if (strs != null) {for (int j = 0; j < strs.length; j++) {pinyinName.append(strs[j]);if (j != strs.length - 1) {pinyinName.append(AppConstant.COMMA);}}}} catch (BadHanyuPinyinOutputFormatCombination e) {e.printStackTrace();}} else {pinyinName.append(nameChar[i]);}pinyinName.append(AppConstant.BLANK);}return parseTheChineseByObject(discountTheChinese(pinyinName.toString()));}/*** 去除多音字重复数据** @param theStr* @return*/private static List<Map<String, Integer>> discountTheChinese(String theStr) {// 去除重复拼音后的拼音列表List<Map<String, Integer>> mapList = new ArrayList<Map<String, Integer>>();// 用于处理每个字的多音字,去掉重复Map<String, Integer> onlyOne = null;String[] firsts = theStr.split(AppConstant.BLANK);// 读出每个汉字的拼音for (String str : firsts) {onlyOne = new Hashtable();String[] china = str.split(AppConstant.COMMA);// 多音字处理for (String s : china) {Integer count = onlyOne.get(s);if (count == null) {onlyOne.put(s, NumberUtils.INTEGER_ONE);} else {onlyOne.remove(s);count++;onlyOne.put(s, count);}}mapList.add(onlyOne);}return mapList;}/*** 解析并组合拼音,对象合并方案(推荐使用)** @return*/private static String parseTheChineseByObject(List<Map<String, Integer>> list) {Map<String, Integer> first = null; // 用于统计每一次,集合组合数据// 遍历每一组集合for (int i = 0; i < list.size(); i++) {// 每一组集合与上一次组合的MapMap<String, Integer> temp = new Hashtable<String, Integer>();// 第一次循环,first为空if (first != null) {// 取出上次组合与此次集合的字符,并保存for (String s : first.keySet()) {for (String s1 : list.get(i).keySet()) {String str = s + s1;temp.put(str, 1);}}// 清理上一次组合数据if (temp != null && temp.size() > 0) {first.clear();}} else {for (String s : list.get(i).keySet()) {String str = s;temp.put(str, 1);}}// 保存组合数据以便下次循环使用if (temp != null && temp.size() > 0) {first = temp;}}String returnStr = StringUtils.EMPTY;if (first != null) {// 遍历取出组合字符串for (String str : first.keySet()) {returnStr += (str + AppConstant.COMMA);}}if (returnStr.length() > 0) {returnStr = returnStr.substring(0, returnStr.length() - 1);}return returnStr;}
}
  • 全文检索

1、博客表

原来设计如下的表存储自己写的博客文章

DROP TABLE IF EXISTS `blog_view`.`article`;
CREATE TABLE `blog_view`.`article`
(id            BIGINT(20) AUTO_INCREMENT NOT NULLCOMMENT '主键, 自增',title         VARCHAR(256)              NOT NULLCOMMENT '标题',summary       VARCHAR(512)              NOT NULL         DEFAULT ''COMMENT '摘要',body          LONGTEXT                  NOT NULLCOMMENT '内容',category_code VARCHAR(16)               NOT NULLCOMMENT '栏目代码',category_name VARCHAR(32)               NOT NULLCOMMENT '栏目名称',created_time  DATETIME                  NOT NULLCOMMENT '创建时间',updated_time  DATETIME                                   DEFAULT NULLCOMMENT '最后更新时间',PRIMARY KEY (`id`)
)COMMENT '文章表';

2、索引表

为自己的博客表建立对应的索引表

使用方式:

每次修改文章时,对文章的titlesummarycategory_namebody 使用分词分成多个单词,在用拼音工具保存在article_index表。

DROP TABLE IF EXISTS `blog_view`.`article_index`;
CREATE TABLE `blog_view`.`article_index`
(id            BIGINT(20) AUTO_INCREMENT NOT NULLCOMMENT '主键, 自增',article_id    BIGINT(20)                NOT NULLCOMMENT '文章ID',title         LONGTEXT                  NOT NULLCOMMENT '标题',summary       LONGTEXT                  NOT NULL DEFAULT ''COMMENT '摘要',category_name LONGTEXT                  NOT NULLCOMMENT '栏目名称',body          LONGTEXT                  NOT NULLCOMMENT '内容',PRIMARY KEY (`id`),UNIQUE KEY `uk_article_id` (article_id)
)ENGINE = MyISAMCOMMENT '文章检索表';-- 只修复索引树
ALTER TABLE `blog_view`.`article_index`ADD FULLTEXT INDEX (`title`, `category_name`, `body`, `summary`);
REPAIR TABLE `blog_view`.`article_index` QUICK;
  • 查询实战

比如用户输入关键词个人博客

1、分词

个人 博客

2、变为拼音

geren、boke

3、检索

SELECT a.id, a.title, a.summary, a.category_code, a.category_name, a.created_time
FROM article a
WHERE AND a.id IN(SELECTai.article_idFROMarticle_index aiWHERE match(ai.title, ai.body, ai.category_name, ai.summary) against(#{key} IN BOOLEAN MODE))
ORDER BY
a.id DESC

  1. 文章的检索表是设定为 MyISAM 的, 原来我也以为 MySQL只有这个才支持 全文检索。INNODB 在5.6之后是支持的,且自带分词器。

More

架构师之路

MySQL索引背后的数据结构及算法原理

深入浅出搜索架构引擎、方案与细节(上)

个人小结

  1. 对于最简单的查询,可以使用 jiebe 进行分词。结合 pinyin 对存储的核心字段的拼音全拼写,简拼写进行存储,然后索引即可。

  2. 对于文章等常见信息,如果想使用全文索引,就可以采用数据库自带的全文索引。

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

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

相关文章

保持ssh断开后,程序不会停止执行

保持ssh断开后&#xff0c;程序不会停止执行 一、前言 笔者做远程部署搞了一阵子&#xff0c;快结项时发现一旦我关闭了ssh连接窗口&#xff0c;远程服务器会自动杀掉我在ssh连接状态下运行的程序。 这怎么行&#xff0c;岂不是想要它一直运行还得要一台电脑一直打开ssh连接咯…

linux设置中文输入法

要在Linux上设置中文输入法&#xff0c;可以按照以下步骤进行操作&#xff1a; 安装中文输入法软件。在大多数Linux发行版中&#xff0c;可以通过软件包管理器来安装中文输入法。常见的中文输入法软件包括fcitx、ibus以及sogou等。以Debian/Ubuntu发行版为例&#xff0c;可以使…

曲线降采样之道格拉斯-普克算法Douglas–Peucker

曲线降采样之道格拉斯-普克算法Douglas–Peucker 该算法的目的是&#xff0c;给定一条由线段构成的曲线&#xff0c;找到一条点数较少的相似曲线&#xff0c;来近似描述原始的曲线&#xff0c;达到降低时间、空间复杂度和平滑曲线的目的。 附赠自动驾驶学习资料和量产经验&…

【C语言】“vid”Microsoft Visual Studio安装及应用(检验内存泄露)

文章目录 前言安装包获取配置VLD完成 前言 我们在写代码时往往容易存在内存泄漏的情况&#xff0c;所以存在这样一个名为VLD的工具用来检验内存泄漏&#xff0c;现在我来教大家安装一下 安装包获取 vld下载网址&#xff1a;https://github.com/KindDragon/vld/releases/tag/…

YOLOv8结合SCI低光照图像增强算法!让夜晚目标无处遁形!【含端到端推理脚本】

这里的"SCI"代表的并不是论文等级,而是论文采用的方法 — “自校准光照学习” ~ 左侧为SCI模型增强后图片的检测效果,右侧为原始v8n检测效果 这篇文章的主要内容是通过使用SCI模型和YOLOv8进行算法联调,最终实现了如上所示的效果:在增强图像可见度的同时,对图像…

【中文视觉语言模型+本地部署 】23.08 阿里Qwen-VL:能对图片理解、定位物体、读取文字的视觉语言模型 (推理最低12G显存+)

项目主页&#xff1a;https://github.com/QwenLM/Qwen-VL 通义前问网页在线使用——&#xff08;文本问答&#xff0c;图片理解&#xff0c;文档解析&#xff09;&#xff1a;https://tongyi.aliyun.com/qianwen/ 论文v3. : 一个全能的视觉语言模型 23.10 Qwen-VL: A Versatile…

读取信息boot.bin和xclbin命令

bootgen读Boot.bin命令 johnjohn-virtual-machine:~/project_zynq/kv260_image_ubuntu22.04$ bootgen -read BOOT-k26-starter-kit-202305_2022.2.bin xclbinutil读xclbin命令 johnjohn-virtual-machine:~/project_zynq/kv260_image_ubuntu22.04$ xclbinutil -i kv260-smartca…

服务器配置Huggingface并git clone模型和文件

服务器配置Huggingface并git clone模型和文件 参考&#xff1a;https://huggingface.co/welcome 1 注册hugging face 官网注册&#xff0c;并获取token【https://huggingface.co/settings/tokens】&#xff0c;用于登录 2 安装 2.1 安装lfs https://stackoverflow.com/qu…

Linux权限提升总结

几个信息收集的项目推荐 运行这几个项目就会在目标主机上收集一些敏感信息供我们参考和使用 一个综合探针&#xff1a;traitor 一个自动化提权&#xff1a;BeRoot(gtfo3bins&lolbas) 使用python2运行beroot.py就可以运行程序&#xff0c;然后就可以收集到系统中的大量信…

mysql锁表问题

问题描述 偶尔应用日志会打印锁表超时回滚 org.springframework.dao.CannotAcquireLockException: ### Error updating database. Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transactionmysql锁…

vue-ueditor-wrap上传图片报错:后端配置项没有正常加载,上传插件不能正常使用

如图所示,今天接收一个项目其中富文本编辑器报错 此项目为vue2项目,富文本编辑器为直接下载好的资源存放在public目录下的 经过排查发现报错的函数在ueditor.all.min.js文件内,但是ueditor.all.min.js文件夹是经过压缩的 所以直接,将index.html中的引用路径修改为ueditor…

商城网站-礼品网站首页html+css+js+说明文档

网页设计与网站建设作业htmlcssjs 预览 说明 单页面&#xff0c;轮播图 获取&#xff1a;https://hpc.baicaitang.cn/2077.html

代码随想录算法训练营第42天 | 704. 二分查找、27. 移除元

今天的背包问题能听懂&#xff0c;但自己做的时候有点迷糊&#xff0c;还需要好好消化一下 01背包问题 二维 代码随想录 视频讲解&#xff1a;带你学透0-1背包问题&#xff01;| 关于背包问题&#xff0c;你不清楚的地方&#xff0c;这里都讲了&#xff01;| 动态规划经典问题…

java的警示之有危险的行为

&#x1f468;‍&#x1f4bb;作者简介&#xff1a;&#x1f468;&#x1f3fb;‍&#x1f393;告别&#xff0c;今天 &#x1f4d4;高质量专栏 &#xff1a;☕java趣味之旅 欢迎&#x1f64f;点赞&#x1f5e3;️评论&#x1f4e5;收藏&#x1f493;关注 &#x1f496;衷心的希…

06 | Swoole 源码分析之 Coroutine 协程模块

首发原文链接&#xff1a;Swoole 源码分析之 Coroutine 协程模块 大家好&#xff0c;我是码农先森。 引言 协程又称轻量级线程&#xff0c;但与线程不同的是&#xff1b;协程是用户级线程&#xff0c;不需要操作系统参与。由用户显式控制&#xff0c;可以在需要的时候挂起、或…

视频号小店怎么选品?货源怎么找?核心玩法分享!

我是电商珠珠 视频号小店发展到现在不过不到两年的时间&#xff0c;所以目前正是它的风口红利期。很多做视频号小店的商家并不知道怎么选品&#xff0c;怎么去找无货源&#xff0c;根据我做店的经验&#xff0c;这就来教大家如何做视频号小店。 视频号小店怎么选品&#xff1…

getRawMany()和getMany()的区别

解决SELECT list is not in GROUP BY clause and contains nonaggregated column ‘testdata.logs.id’ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_modeonly_full_group_by 前言&#xff1a; nest/typeorm报错 SE…

Redis中的复制功能(三)

复制 服务器运行ID 除了复制偏移量和复制积压缓冲区之外&#xff0c;实现部分重同步还需要用到服务器运行ID(run ID): 1.每隔Redis服务器&#xff0c;不论主服务器还是从服务&#xff0c;都会有自己的运行ID2.运行ID在服务器启动时自动生成&#xff0c;由40个随机的十六进制…

迈向数字化医疗:互联网医院APP开发中的设计思路与技术要点

在开发互联网医院APP时&#xff0c;需要综合考虑设计思路和技术要点&#xff0c;确保用户体验和医疗服务质量的提升。接下来&#xff0c;小编将从设计思路和技术要点两个方面进行讲解。 一、设计思路 用户导向&#xff1a;在设计互联网医院APP时&#xff0c;需要将用户体验放在…

RocketMQ 消费者源码解读:消费过程、负载原理、顺序消费原理

B站学习地址 上一遍学习了三种常见队列的消费原理&#xff0c;本次我们来从源码的角度来证明上篇中的理论。 1、准备 RocketMQ 版本 <!-- RocketMQ --> <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-s…