在上一篇文章:【信息检索】Java简易搜索引擎原理及实现(四)利用布尔模型和向量模型计算权值,我们已经计算出了最终数据呈现所需使用到的大部分数据,本篇我们将完成最后一步:对查询词进行预处理,并计算出查询词与相关文档的相似度,将文档按相似度由高到低排序。同时,完成搜索界面开发和服务器快速搭建的工作。
1.计算查询与文档相似度
(1)查询词预处理
我们在第三篇文章中,构建出了轮排 加上 B+树的索引结构,本次的查询就是基于该结构开展的,因此我们需要首先获取到构建好的B+树。
在第二篇文章中,其实我们也处理过了 AND 、OR、ANDNOT三种操作符,已经可以由单个词组成的查询链查询出匹配的DocId了。但在实际的查询中,用户可能输入并不是完全按照我们的假设,可能会在一个词的位置输入多个词组。
举个例子来说,例如用户希望查询 【数字图像处理 AND 竞赛】,竞赛就看做一个词,它是可以在我们的词库中查询到的;而数字图像处理这个词,在我们的词库中是找不到这样一个完整的词汇的,因为抓取下来的数据已经在存入B+树之前,就由分词器做过分词了,所以此时B+树中能找到的词只有【数字】、【图像】、【处理】。因此,针对这种情况,我们对每一个输入词进行处理,依然让其通过一次分词器,获取到输入词分词后的结果(DocId),将其与原本的输入词的查询结果(DocId)做并集操作。在这个例子中就是,把【数字图像处理】这个词变为【数字图像处理】OR 【数字】OR【图像】OR【处理】,这样即使造成查询出某些不符合用户原意的结果,也能因为后续还要计算相似度而排名靠后展示。
需要注意的是,由于我们的搜索引擎同时支持精确查询和通配符查询。因此,这种拆分搜索词的操作,我们只对精确查找的内容进行处理,对于含通配符的搜索词,我们是不做这一步的。
下面,我们就进行代码实现:我们假设你已经有了前面实验的代码。
public class Experiment6 {
private static Experiment2 experiment2 = new Experiment2();
private static Experiment3 experiment3 = new Experiment3();
public static Map<String, Integer> queryWord_map = new HashMap<>();
public static Integer queryWord_count = 0;
public static BplusTree bPlusTree;
public static void initBPlusTree() {
bPlusTree = experiment3.exp3(); //轮排 + B+树索引结构
}
/**
* 查询用户搜索内容匹配的DocId
* @param input 用户输入
* @param bPlusTree B+树和轮排的索引结构
* @return 查询出的所有匹配的DocId
*/
public static List<Integer> query(String input, BplusTree bPlusTree) {
String[] in_strs = input.split(" ");
//单独处理第一个搜索词
List<Integer> result = getDocList(experiment3.queryForList(in_strs[0], bPlusTree));
List<Integer> next;
//对输入的一个词进行分词并与原结果合并
if (!in_strs[0].contains("*")) {
result = splitWordMerge(in_strs[0], result); //只对精确查找的词进行此操作
}
int len = in_strs.length;
for (int i = 1; i < len; i += 2) {
//i取奇数
next = getDocList(experiment3.queryForList(in_strs[i + 1], bPlusTree));
//对输入的一个词进行分词并与原结果合并
if (!in_strs[i + 1].contains("*")) {
next = splitWordMerge(in_strs[i + 1], next); //只对精确查找的词进行此操作
}
//根据操作数合并result和next的DocId
switch (in_strs[i]) {